经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » JavaScript » 查看文章
前端工程师进阶之旅-手撕代码【前端常用方法以及面试常见题】
来源:cnblogs  作者:耶温  时间:2021/5/6 17:57:52  对本文有异议

前端工程师进阶之旅-手撕代码

主要包括一些工作中常用的方法,面试常问到的方法。还有一些不太了解,趁机深入了解的知识点。都弄懂之后还是有点提升的。

数据类型判断

  1. function myTypeof(data) {
  2. return Object.prototype.toString.call(data).slice(8, -1).toLowerCase();
  3. }

数组去重

  1. //new Set() 集合
  2. //ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
  3. function unique(arr) {
  4. return [...new Set(arr)];
  5. }
  6. //数组去重 filter
  7. function unique(arr) {
  8. return arr.filter((item, index, array) => {
  9. return array.indexOf(item) === index;
  10. });
  11. }
  12. // 数组去重 forEach
  13. function unique(arr) {
  14. let res = [];
  15. arr.forEach((item) => {
  16. res.includes(item) ? "" : res.push(item);
  17. });
  18. return res;
  19. }

数组排序

  1. // 快排排序
  2. function quickSort(arr) {
  3. // 数组长度为1 直接返回
  4. if (arr.length <= 1) return arr;
  5. var left = [],
  6. right = [];
  7. var centerIndex = Math.floor(arr.length / 2);
  8. // 定义中间值
  9. var center = arr[centerIndex];
  10. for (let i = 0; i < arr.length; i++) {
  11. // 小于中间值数据添加到left
  12. if (arr[i] < center) {
  13. left.push(arr[i]);
  14. } else if (arr[i] > center) {
  15. // 大于中间值数据添加到right
  16. right.push(arr[i]);
  17. }
  18. }
  19. // 递归返回 concat连接返回数据
  20. return quickSort(left).concat([center], quickSort(right));
  21. }
  22. // 冒泡排序
  23. function bubbleSort(arr) {
  24. for (var i = 0; i < arr.length - 1; i++) {
  25. for (var j = i + 1; j < arr.length; j++) {
  26. if (arr[i] > arr[j]) {
  27. var temp = arr[j];
  28. arr[j] = arr[i];
  29. arr[i] = temp;
  30. }
  31. }
  32. }
  33. return arr;
  34. }

数组扁平化

  1. //使用 Infinity,可展开任意深度的嵌套数组
  2. arr.flat(Infinity);
  3. //适用JSON转换
  4. JSON.parse("[" + JSON.stringify(arr).replace(/\[|\]/g, "") + "]");
  5. //递归
  6. function myFlat(arr) {
  7. let res = [];
  8. arr.forEach((item) => {
  9. if (Array.isArray(item)) {
  10. res = res.concat(myFlat(item));
  11. } else {
  12. res.push(item);
  13. }
  14. });
  15. return res;
  16. }
  17. //some
  18. function myFlat(arr) {
  19. while (arr.some((res) => Array.isArray(res))) {
  20. arr = [].concat(...arr);
  21. }
  22. return arr;
  23. }

回文字符串相关

  1. //回文:正读和反读都一样的字符串。
  2. //判断是否是回文字符串
  3. function isPalindrome(str) {
  4. if (!str) return false
  5. return str == str.split("").reverse().join("")
  6. }
  7. //找出字符串中最长的回文字段
  8. //循环暴力破解
  9. function palindrome(str) {
  10. var len = str.length
  11. var maxStr = ''
  12. if (isPalindrome(str)) return str
  13. for (let i = 0; i <= len - 1; i++) {
  14. for (let j = i + 1; j <= len; j++) {
  15. var s = str.slice(i, j)
  16. if (!isPalindrome(s)) continue
  17. if (s.length > maxStr.length) {
  18. maxStr = s
  19. }
  20. }
  21. }
  22. return maxStr
  23. }
  24. //改造方法 中心扩展法
  25. function palindrome(str) {
  26. var len = str.length
  27. var s = ''
  28. if (len < 2) return str
  29. for (let i = 0; i < len; i++) {
  30. /**判断是否是回文,i作为回文中间值,有两种可能 */
  31. isPalindrome(i, i)
  32. isPalindrome(i, i + 1)
  33. }
  34. function isPalindrome(i, j) {
  35. while (i >= 0 && j <= len && str[i] == str[j]) {
  36. i--
  37. j++
  38. }
  39. // 判断是否是最长回文
  40. if (j - i - 1 > s.length) {
  41. s = str.slice(i + 1, j)
  42. }
  43. }
  44. return s
  45. }

深拷贝深克隆

  1. // 简单克隆 无法复制函数
  2. var newObj = JSON.parse(JSON.stringify(obj));
  3. // 深克隆 无法克隆特殊实例 Date等
  4. function deepClone(target) {
  5. if (typeof target !== "object") {
  6. return target;
  7. }
  8. var result;
  9. if (Object.prototype.toString.call(target) == "[object Array]") {
  10. // 数组
  11. result = [];
  12. } else {
  13. // 对象
  14. result = {};
  15. }
  16. for (var prop in target) {
  17. if (target.hasOwnProperty(prop)) {
  18. result[prop] = deepClone(target[prop]);
  19. }
  20. }
  21. return result;
  22. }
  23. //复杂版深克隆
  24. function deepClone(target) {
  25. if (typeof target !== "object") return target;
  26. // 检测RegDate类型创建特殊实例
  27. let constructor = target.constructor;
  28. if (/^(RegExp|Date)$/i.test(constructor.name)) {
  29. return new constructor(target);
  30. }
  31. // 判断类型
  32. var result =
  33. Object.prototype.toString.call(target) == "[object Array]" ? [] : {};
  34. // 迭代循环
  35. for (var prop in target) {
  36. if (target.hasOwnProperty(prop)) {
  37. // 递归
  38. result[prop] = deepClone(target[prop]);
  39. }
  40. }
  41. return result;
  42. }

继承方法

原型链继承:

  1. // 原型链继承
  2. // 问题:原型中的引用对象会被所有的实例共享,子类在实例化的时候不能给父类构造函数传参
  3. function Father() {
  4. this.hobby = ["coding", "eat"];
  5. }
  6. Father.prototype.skill = function () {
  7. console.log("i will javascript");
  8. };
  9. function Son() {}
  10. Son.prototype = new Father();
  11. var father = new Father();
  12. var son = new Son();
  13. var son1 = new Son();
  14. console.log(father.hobby); //[ 'coding', 'eat' ]
  15. father.hobby.push("play");
  16. console.log(father.hobby, son.hobby, son1.hobby);
  17. //[ 'coding', 'eat', 'play' ] [ 'coding', 'eat' ] [ 'coding', 'eat' ]
  18. son.hobby.push("hei");
  19. console.log(father.hobby, son.hobby, son1.hobby);
  20. //[ 'coding', 'eat', 'play' ] [ 'coding', 'eat', 'hei' ] [ 'coding', 'eat', 'hei' ]
  21. son.skill(); //i will javascript

借用构造函数实现继承

  1. // 原型链继承
  2. // 问题:方法需要定义在构造函数内,因此每次创建子类实例都会创建一边方法
  3. function Father(name) {
  4. this.name = name;
  5. this.sayNmae = function () {
  6. return this.name;
  7. };
  8. }
  9. function Son(name) {
  10. Father.call(this, name);
  11. }
  12. Son.prototype = new Father();
  13. var father = new Father("wenbo");
  14. var son = new Son("zhijian");
  15. console.log(father.name, son.name); //wenbo zhijian
  16. console.log(father.sayNmae(), son.sayNmae()); //wenbo zhijian

组合继承

  1. //组合继承,结合原型链继承和借用构造函数,使用原型链继承原型上的属性和方法,借用构造函数继承实例属性。
  2. //即可以把方法定义在原型上实现重用,又可以让每个实例都有自己的属性
  3. // 组合继承
  4. function Father(name) {
  5. this.name = name;
  6. }
  7. Father.prototype.sayName = function () {
  8. return this.name;
  9. };
  10. function Son(name, age) {
  11. Father.call(this, name);
  12. this.age = age;
  13. }
  14. Son.prototype = new Father();
  15. Son.prototype.constructor = Son;
  16. var son = new Son("yewen", 18);
  17. console.log(son); //Son { name: 'yewen', age: 18 }
  18. console.log(son.sayName()); //yewen

寄生式组合继承

  1. //寄生组合继承
  2. // 组合继承会导致调用两次父类构造函数
  3. function Father(name) {
  4. this.name = name;
  5. }
  6. Father.prototype.sayName = function () {
  7. return this.name;
  8. };
  9. function Son(name, age) {
  10. Father.call(this, name);
  11. this.age = age;
  12. }
  13. Son.prototype = Object.create(Father.prototype);
  14. Son.prototype.constructor = Son;

class 实现继承

  1. // calss继承
  2. class Father {
  3. constructor(name) {
  4. this.name = name;
  5. }
  6. getName() {
  7. return this.name;
  8. }
  9. }
  10. class Son extends Father {
  11. constructor(name, age) {
  12. super(name);
  13. this.age = age;
  14. }
  15. getAge() {
  16. return this.age;
  17. }
  18. }
  19. var son = new Son("heihei", 18);
  20. console.log(son); //Son { name: 'heihei', age: 18 }
  21. console.log(son.getName(), son.getAge()); //heihei 18

事件总线(发布订阅模式)

  1. class EventEmitter {
  2. constructor() {
  3. this.cache = {};
  4. }
  5. on(name, fn) {
  6. if (this.cache[name]) {
  7. this.cache[name].push(fn);
  8. } else {
  9. this.cache[name] = [fn];
  10. }
  11. }
  12. off(name, fn) {
  13. let tasks = this.cache[name];
  14. if (tasks) {
  15. const index = tasks.findIndex((f) => f === fn || f.callback === fn);
  16. index >= 0 ? tasks.splice(index, 1) : "";
  17. }
  18. }
  19. emit(name, once, ...args) {
  20. if (this.cache[name]) {
  21. // 创建副本
  22. let tasks = this.cache[name].slice();
  23. for (const fn of tasks) {
  24. fn(...args);
  25. }
  26. once ? delete this.cache[name] : "";
  27. }
  28. }
  29. }
  30. let demo = new EventEmitter();
  31. demo.on("wenbo", function (data) {
  32. console.log("wenbo", data);
  33. });
  34. let fn1 = function (data) {
  35. console.log("hello:", data);
  36. };
  37. demo.on("wenbo", fn1);
  38. demo.emit("wenbo", false, "world");
  39. demo.off("wenbo", fn1);
  40. demo.emit("wenbo", false, "world");
  41. //wenbo world
  42. //hello: world
  43. //wenbo world

获得滚动距离

  1. function getScrollOffset() {
  2. if (window.pageXOffset) {
  3. return {
  4. x: window.pageXOffset,
  5. y: window.pageYOffset,
  6. };
  7. } else {
  8. return {
  9. x: document.body.scrollLeft + document.documentElement.scrollLeft,
  10. y: document.body.scrollTop + document.documentElement.scrollTop,
  11. };
  12. }
  13. }

获得视口尺寸

  1. function getViewportOffset() {
  2. if (window.innerWidth) {
  3. return {
  4. w: window.innerWidth,
  5. h: window.innerHeight,
  6. };
  7. } else {
  8. // ie8及其以下
  9. if (document.compatMode === "BackCompat") {
  10. // 怪异模式
  11. return {
  12. w: document.body.clientWidth,
  13. h: document.body.clientHeight,
  14. };
  15. } else {
  16. // 标准模式
  17. return {
  18. w: document.documentElement.clientWidth,
  19. h: document.documentElement.clientHeight,
  20. };
  21. }
  22. }
  23. }

防抖函数

  1. function debounce(fun, wait) {
  2. var timeId = null;
  3. return function () {
  4. var _this = this;
  5. var _arg = arguments;
  6. clearTimeout(timeId);
  7. timeId = setTimeout(function () {
  8. fun.apply(_this, _arg);
  9. }, wait);
  10. };
  11. }

节流函数

  1. function throttle(fun, wait) {
  2. var lastTime = 0;
  3. return function () {
  4. var _this = this;
  5. var _arg = arguments;
  6. var nowTime = new Date().getTime();
  7. if (nowTime - lastTime > wait) {
  8. fun.apply(_this, _arg);
  9. lastTime = nowTime;
  10. }
  11. };
  12. }

图片加载优化懒加载

  1. // 获取全部img元素 并且将类数组转化成数组
  2. let imgList = [...document.querySelectorAll("img")];
  3. let len = imgList.length;
  4. // 图片懒加载
  5. function imgLazyLoad() {
  6. let count = 0;
  7. return (function () {
  8. let isLoadList = [];
  9. imgList.forEach((item, index) => {
  10. let h = item.getBoundingClientRect();
  11. // 判断图片是否快要滚动道可视区域
  12. if (h.top < window.innerHeight + 200) {
  13. item.src = item.dataset.src;
  14. console.log(item.dataset.src);
  15. isLoadList.push(index);
  16. count++;
  17. // 全部加载 移出scroll事件
  18. if (len == count) {
  19. document.removeEventListener("scroll", imgLazyLoad);
  20. }
  21. }
  22. });
  23. // 移出已经加载完成的图片
  24. imgList = imgList.filter((img, index) => !isLoadList.includes(index));
  25. })();
  26. }
  27. // 节流函数
  28. function throttle(fun, wait) {
  29. var lastTime = 0;
  30. return function () {
  31. var _this = this;
  32. var _arg = arguments;
  33. var nowTime = new Date().getTime();
  34. if (nowTime - lastTime > wait) {
  35. fun.apply(_this, _arg);
  36. lastTime = nowTime;
  37. }
  38. };
  39. }
  40. // 默认执行一次加载首屏图片
  41. imgLazyLoad();
  42. // 节流执行
  43. document.addEventListener("scroll", throttle(imgLazyLoad, 200));

绑定事件

  1. function addEvent(elem, type, handle) {
  2. if (elem.addEventListener) {
  3. //非ie和ie9以上
  4. elem.addEventListener(type, handle, false);
  5. } else if (elem.attachEvent) {
  6. //ie8以下 ie6-8
  7. elem.attachEvent("on" + type, function () {
  8. handle.call(elem);
  9. });
  10. } else {
  11. // 其他
  12. elem["on" + type] = handle;
  13. }
  14. }

解绑事件

  1. function removeEvent(elem, type, handle) {
  2. if (elem.removeEventListener) {
  3. //非ie和ie9以上
  4. elem.removeEventListener(type, handle, false);
  5. } else if (elem.detachEvent) {
  6. //ie8以下 ie6-8
  7. elem.detachEvent("on" + type, handle);
  8. } else {
  9. //其他
  10. elem["on" + type] = null;
  11. }
  12. }

取消冒泡事件

  1. function stopBubble(e) {
  2. //兼容ie9以下
  3. if (e && e.stopPropagation) {
  4. e.stopPropagation();
  5. } else {
  6. window.event.cancelBubble = true;
  7. }
  8. }

JS 创建 Script

  1. function loadScript(url, callback) {
  2. var script = document.createElement("script");
  3. if (script.readyState) {
  4. // 兼容ie8及以下版本
  5. script.onreadystatechange = function () {
  6. if (script.readyState === "complete" || script.readyState === "loaded") {
  7. // 回调函数
  8. callback();
  9. }
  10. };
  11. } else {
  12. // 加载完成之后
  13. script.onload = function () {
  14. // 回调函数
  15. callback();
  16. };
  17. }
  18. script.src = url;
  19. // 添加标签
  20. document.body.appendChild(script);
  21. }
  1. var cookie = {
  2. //设置cookie
  3. set: function (name, value, time) {
  4. document.cookie = `${name}=${value};expires=${time};path=/`;
  5. return this;
  6. },
  7. //获取cookie
  8. get: function (name) {
  9. var arr;
  10. var reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
  11. if ((arr = document.cookie.match(reg))) {
  12. return unescape(arr[2]);
  13. } else {
  14. return null;
  15. }
  16. },
  17. //移出token
  18. remove: function (name) {
  19. return this.setCookie(name, "", -1);
  20. },
  21. };

验证邮箱

  1. function isAvailableEmail(email) {
  2. var reg = /^([\w+\.])+@\w+([.]\w+)+$/;
  3. return reg.test(email);
  4. }

封装 myForEach 方法

  1. // thisValue 可选参数。当执行回调函数 callback 时,用作 this 的值。
  2. Array.prototype.myForEach = function (callback, thisValue) {
  3. var _this;
  4. // 当this为空抛出异常
  5. if (this == null) {
  6. throw new TypeError(" this is null or not defined");
  7. }
  8. // var len = this.length
  9. // this.length >>> 0 相当于 所有非数值转换成0 ,所有大于等于 0 等数取整数部分
  10. var len = this.length >>> 0;
  11. // callback不是函数时 抛出异常
  12. if (typeof callback !== "function") {
  13. throw new TypeError(callback + " is not a function");
  14. }
  15. // 判断是够有传参 thisValue
  16. if (arguments.length > 1) {
  17. _this = thisValue;
  18. }
  19. // 循环遍历
  20. for (var i = 0; i < len; i++) {
  21. // 回调函数
  22. callback.call(_this, this[i], i, this);
  23. }
  24. };

封装 myFilter 方法

  1. Array.prototype.myFilter = function (callback, thisValue) {
  2. var _this;
  3. var arr = [];
  4. if (this == null) {
  5. throw new TypeError(" this is null or not defined");
  6. }
  7. var len = this.length >>> 0;
  8. if (typeof callback !== "function") {
  9. throw new TypeError(callback + " is not a function");
  10. }
  11. if (arguments.length > 1) {
  12. _this = thisValue;
  13. }
  14. for (var i = 0; i < len; i++) {
  15. callback.call(_this, this[i], i, this) && arr.push(this[i]);
  16. }
  17. return arr;
  18. };

封装 myMap 方法

  1. Array.prototype.myMAp = function (callback, thisValue) {
  2. var _this;
  3. var arr = [];
  4. if (this == null) {
  5. throw new TypeError(" this is null or not defined");
  6. }
  7. var len = this.length >>> 0;
  8. if (typeof callback !== "function") {
  9. throw new TypeError(callback + " is not a function");
  10. }
  11. if (arguments.length > 1) {
  12. _this = thisValue;
  13. }
  14. for (var i = 0; i < len; i++) {
  15. arr.push(callback.call(_this, this[i], i, this));
  16. }
  17. return arr;
  18. };

封装 myEvery 方法

  1. Array.prototype.myEvery = function (callback, thisValue) {
  2. var _this;
  3. if (this == null) {
  4. throw new TypeError(" this is null or not defined");
  5. }
  6. var len = this.length >>> 0;
  7. if (typeof callback !== "function") {
  8. throw new TypeError(callback + " is not a function");
  9. }
  10. if (arguments.length > 1) {
  11. _this = thisValue;
  12. }
  13. for (var i = 0; i < len; i++) {
  14. if (!callback.call(_this, this[i], i, this)) {
  15. return false;
  16. }
  17. }
  18. return true;
  19. };

封装 myReduce 方法

  1. Array.prototype.myEvery = function (callback, initialValue) {
  2. var value = 0;
  3. console.log(value);
  4. if (this == null) {
  5. throw new TypeError(" this is null or not defined");
  6. }
  7. var len = this.length >>> 0;
  8. if (typeof callback !== "function") {
  9. throw new TypeError(callback + " is not a function");
  10. }
  11. if (arguments.length > 1) {
  12. value = initialValue;
  13. }
  14. for (var i = 0; i < len; i++) {
  15. value = callback(value, this[i], i, this);
  16. }
  17. return value;
  18. };

获取 URL 参数

  1. function getURLParam(url) {
  2. let obj = {};
  3. url.replace(/(?<=[?|&])(\w+)=(\w+)/g, function (data, key, value) {
  4. if (obj[key]) {
  5. obj[key] = [].concat(obj[key], value);
  6. } else {
  7. obj[key] = value;
  8. }
  9. });
  10. return obj;
  11. }

HTML 字符串模板

  1. function render(template, data) {
  2. return template.replace(/\{(\w+)}/g, function ($1, key) {
  3. return data[key] ? data[key] : "";
  4. });
  5. }
  6. let html = "我叫{name},今年{id}岁。";
  7. let data = {
  8. name: "Yevin",
  9. age: 18,
  10. };
  11. render(html, data); //我叫Yevin,今年18岁

利用 JSONP 实现跨域请求

  1. function jsonp(url, callbackName) {
  2. return new Promise((resolve, reject) => {
  3. var script = document.createElement("script");
  4. script.src = "demo.js";
  5. document.body.appendChild(script);
  6. window[callbackName] = function (res) {
  7. //移除remove
  8. script.remove();
  9. //返回数据
  10. resolve(res);
  11. };
  12. });
  13. }

原生 JS 封装 AJAX

  1. function Ajax(method, url, callback, data, async = true) {
  2. var xhr;
  3. //同一转换method方法为大写
  4. method = method.toUpperCase();
  5. // 开启XMLHTTPRequest
  6. xhr = new XMLHttpRequest();
  7. // 监控状态变化 执行回调函数
  8. xhr.onreadystatechange = function () {
  9. if (xhr.readyState == 4 && xhr.readyState == 200) {
  10. // 回调函数返回参数
  11. callback(xhr.responseText);
  12. }
  13. };
  14. // 判断请求方式 get post或者其他
  15. if (method == "GET") {
  16. xhr.open("GET", url, async);
  17. } else if (method == "POST") {
  18. xhr.open("POST", url, async);
  19. // 设置请求头
  20. xhr.setRequestHeader("Content-Type", "application/json");
  21. // 发送参数
  22. xhr.send(data);
  23. }
  24. }

格式化时间

  1. // formatDate 时间格式,data默认为当前时间
  2. function formatDate(formatDate, date = new Date()) {
  3. var obj = {
  4. yyyy: date.getFullYear(), //4位数年份
  5. yy: ("" + date.getFullYear()).slice(-2), //2位数年份,最后两位
  6. M: date.getMonth() + 1, //月份
  7. MM: ("0" + (date.getMonth() + 1)).slice(-2), //2位数月份
  8. d: date.getDate(), //日份
  9. dd: ("0" + date.getDate()).slice(-2), //2位数 日份
  10. H: date.getHours(), //小时 24小时制
  11. HH: ("0" + date.getHours()).slice(-2), //2位数小时 24小时制
  12. h: date.getHours() % 12, //小时 12小时制
  13. hh: ("0" + (date.getHours() % 12)).slice(-2), //2位数小时 12小时制
  14. m: date.getMinutes(), //分
  15. mm: ("0" + date.getMinutes()).slice(-2), //2位数分
  16. s: date.getSeconds(), //秒
  17. ss: ("0" + date.getSeconds()).slice(-2), //两位数秒
  18. w: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"][
  19. date.getDay()
  20. ], //星期
  21. };
  22. // 根据传入字符串使用正则替换相关数据
  23. return formatDate.replace(/([a-z]+)/gi, function ($1) {
  24. return obj[$1];
  25. });
  26. }

函数柯里化

  1. //把多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术
  2. function curry(fun) {
  3. let fn = function (...arg) {
  4. if (arg.length == fun.length) return fun(...arg);
  5. return (...arg2) => fn(...arg, ...arg2);
  6. };
  7. return fn;
  8. }
  9. function demo(a, b) {
  10. return a * b;
  11. }
  12. console.log(demo(1, 2)); //2
  13. console.log(curry(demo)(1)(2)); //2

偏函数

  1. // 偏函数,就是固定一个函数的一个或者多个参数,返回一个新的函数,这个函数用于接受剩余的参数。
  2. function partial(fn, ...arg) {
  3. return function (...args) {
  4. return fn(...arg, ...args);
  5. };
  6. }
  7. function demo(a, b, c) {
  8. console.log(a, b, c); // 1, 2,3
  9. }
  10. var a = partial(demo, 1);
  11. a(2, 3);

在元素后面插入新元素

  1. function insertAfter(target, ele) {
  2. //查看是否有兄弟元素
  3. var nextEle = ele.nextElementSibling;
  4. if (nextEle == null) {
  5. // 无兄弟元素,直接添加到当前元素之后
  6. this.appendChild(target);
  7. } else {
  8. // 若有兄弟元素,添加在下一个兄弟元素之前
  9. this.insertBefore(target, nextEle);
  10. }
  11. }

原文链接:http://www.cnblogs.com/yevin/p/14699961.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号