前端工程师进阶之旅-手撕代码【前端常用方法以及面试常见题】

  • A+
所属分类:Web前端
摘要

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


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

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

废话少说,直接干代码就完事了。

数据类型判断

使用 Object.prototype.toString 实现。

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

数组去重

//new Set() 集合 //ES6提供了新的数据结构Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。 function unique(arr) {   return [...new Set(arr)]; }  //数组去重 filter function unique(arr) {   return arr.filter((item, index, array) => {     return array.indexOf(item) === index;   }); }  // 数组去重  forEach function unique(arr) {   let res = [];   arr.forEach((item) => {     res.includes(item) ? "" : res.push(item);   });   return res; } 

数组扁平化

//使用 Infinity,可展开任意深度的嵌套数组 arr.flat(Infinity);  //适用JSON转换 JSON.parse("[" + JSON.stringify(arr).replace(/[|]/g, "") + "]");  //递归 function myFlat(arr) {   let res = [];   arr.forEach((item) => {     if (Array.isArray(item)) {       res = res.concat(myFlat(item));     } else {       res.push(item);     }   });   return res; } //some function myFlat(arr) {   while (arr.some((res) => Array.isArray(res))) {     arr = [].concat(...arr);   }   return arr; } 

深拷贝深克隆

// 简单克隆 无法复制函数 var newObj = JSON.parse(JSON.stringify(obj));  // 深克隆  无法克隆特殊实例  Date等 function deepClone(target) {   if (typeof target !== "object") {     return target;   }   var result;   if (Object.prototype.toString.call(target) == "[object Array]") {     // 数组     result = [];   } else {     // 对象     result = {};   }   for (var prop in target) {     if (target.hasOwnProperty(prop)) {       result[prop] = deepClone(target[prop]);     }   }   return result; }  //复杂版深克隆 function deepClone(target) {   if (typeof target !== "object") return target;   // 检测RegDate类型创建特殊实例   let constructor = target.constructor;   if (/^(RegExp|Date)$/i.test(constructor.name)) {     return new constructor(target);   }   // 判断类型   var result =     Object.prototype.toString.call(target) == "[object Array]" ? [] : {};   // 迭代循环   for (var prop in target) {     if (target.hasOwnProperty(prop)) {       // 递归       result[prop] = deepClone(target[prop]);     }   }   return result; } 

继承方法

原型链继承:

// 原型链继承 // 问题:原型中的引用对象会被所有的实例共享,子类在实例化的时候不能给父类构造函数传参 function Father() {   this.hobby = ["coding", "eat"]; } Father.prototype.skill = function () {   console.log("i will javascript"); }; function Son() {} Son.prototype = new Father();  var father = new Father(); var son = new Son(); var son1 = new Son(); console.log(father.hobby); //[ 'coding', 'eat' ]  father.hobby.push("play"); console.log(father.hobby, son.hobby, son1.hobby); //[ 'coding', 'eat', 'play' ] [ 'coding', 'eat' ] [ 'coding', 'eat' ]  son.hobby.push("hei"); console.log(father.hobby, son.hobby, son1.hobby); //[ 'coding', 'eat', 'play' ] [ 'coding', 'eat', 'hei' ] [ 'coding', 'eat', 'hei' ]  son.skill(); //i will javascript 

借用构造函数实现继承

// 原型链继承 // 问题:方法需要定义在构造函数内,因此每次创建子类实例都会创建一边方法 function Father(name) {   this.name = name;   this.sayNmae = function () {     return this.name;   }; }  function Son(name) {   Father.call(this, name); } Son.prototype = new Father();  var father = new Father("wenbo"); var son = new Son("zhijian");  console.log(father.name, son.name); //wenbo zhijian console.log(father.sayNmae(), son.sayNmae()); //wenbo zhijian 

组合继承

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

寄生式组合继承

//寄生组合继承 // 组合继承会导致调用两次父类构造函数 function Father(name) {   this.name = name; } Father.prototype.sayName = function () {   return this.name; };  function Son(name, age) {   Father.call(this, name);   this.age = age; }  Son.prototype = Object.create(Father.prototype); Son.prototype.constructor = Son; 

class 实现继承

// calss继承 class Father {   constructor(name) {     this.name = name;   }   getName() {     return this.name;   } }  class Son extends Father {   constructor(name, age) {     super(name);     this.age = age;   }   getAge() {     return this.age;   } } var son = new Son("heihei", 18); console.log(son); //Son { name: 'heihei', age: 18 } console.log(son.getName(), son.getAge()); //heihei 18 

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

class EventEmitter {   constructor() {     this.cache = {};   }   //添加订阅   on(name, fn) {     if (this.cache[name]) {       this.cache[name].push(fn);     } else {       this.cache[name] = [fn];     }   }   //删除订阅   off(name, fn) {     let tasks = this.cache[name];     if (tasks) {       const index = tasks.findIndex((f) => f === fn || f.callback === fn);       index >= 0 ? tasks.splice(index, 1) : "";     }   }   //发布事件   emit(name, once, ...args) {     if (this.cache[name]) {       // 创建副本       let tasks = this.cache[name].slice();       for (const fn of tasks) {         fn(...args);       }       once ? delete this.cache[name] : "";     }   } }  let demo = new EventEmitter(); demo.on("wenbo", function (data) {   console.log("wenbo", data); }); let fn1 = function (data) {   console.log("hello:", data); }; demo.on("wenbo", fn1);  demo.emit("wenbo", false, "world"); demo.off("wenbo", fn1); demo.emit("wenbo", false, "world");  //wenbo world //hello: world  //wenbo world 

防抖函数

function debounce(fun, wait) {   var timeId = null;   return function () {     var _this = this;     var _arg = arguments;     clearTimeout(timeId);     timeId = setTimeout(function () {       fun.apply(_this, _arg);     }, wait);   }; } 

节流函数

function throttle(fun, wait) {   var lastTime = 0;   return function () {     var _this = this;     var _arg = arguments;     var nowTime = new Date().getTime();     if (nowTime - lastTime > wait) {       fun.apply(_this, _arg);       lastTime = nowTime;     }   }; } 

图片加载优化懒加载

//   获取全部img元素 并且将类数组转化成数组 let imgList = [...document.querySelectorAll("img")]; let len = imgList.length; // 图片懒加载 function imgLazyLoad() {   let count = 0;   return (function () {     let isLoadList = [];     imgList.forEach((item, index) => {       let h = item.getBoundingClientRect();       //  判断图片是否快要滚动道可视区域       if (h.top < window.innerHeight + 200) {         item.src = item.dataset.src;         console.log(item.dataset.src);         isLoadList.push(index);         count++;         // 全部加载 移出scroll事件         if (len == count) {           document.removeEventListener("scroll", imgLazyLoad);         }       }     });     // 移出已经加载完成的图片     imgList = imgList.filter((img, index) => !isLoadList.includes(index));   })(); } // 节流函数 function throttle(fun, wait) {   var lastTime = 0;   return function () {     var _this = this;     var _arg = arguments;     var nowTime = new Date().getTime();     if (nowTime - lastTime > wait) {       fun.apply(_this, _arg);       lastTime = nowTime;     }   }; } // 默认执行一次加载首屏图片 imgLazyLoad(); // 节流执行 document.addEventListener("scroll", throttle(imgLazyLoad, 200)); 

管理操作Cookie

 var cookie = {   //设置cookie   set: function (name, value, time) {     document.cookie = `${name}=${value};expires=${time};path=/`;     return this;   },   //获取cookie   get: function (name) {         var arr;         var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)');         if ((arr = document.cookie.match(reg))) {             return unescape(arr[2]);         } else {             return null;         }     },    //移出token    remove: function (name) {     return this.setCookie(name, "", -1);   }, }; 

封装 myForEach 方法

// thisValue 可选参数。当执行回调函数 callback 时,用作 this 的值。 Array.prototype.myForEach = function (callback, thisValue) {     var _this     // 当this为空抛出异常     if (this == null) {         throw new TypeError(' this is null or not defined');     }     //  var len = this.length      //  this.length >>> 0  相当于 所有非数值转换成0 ,所有大于等于 0 等数取整数部分     var len = this.length >>> 0     // callback不是函数时  抛出异常     if (typeof callback !== "function") {         throw new TypeError(callback + ' is not a function');     }     // 判断是够有传参 thisValue     if (arguments.length > 1) {         _this = thisValue     }     // 循环遍历     for (var i = 0; i < len; i++) {         // 回调函数           callback.call(_this, this[i], i, this)     } } 

封装 myFilter 方法

Array.prototype.myFilter = function (callback, thisValue) {     var _this     var arr = []      if (this == null) {         throw new TypeError(' this is null or not defined');     }      var len = this.length >>> 0      if (typeof callback !== "function") {         throw new TypeError(callback + ' is not a function');     }      if (arguments.length > 1) {         _this = thisValue     }      for (var i = 0; i < len; i++) {         callback.call(_this, this[i], i, this) && arr.push(this[i])     }     return arr } 

封装 myMap 方法

Array.prototype.myMAp = function (callback, thisValue) {     var _this     var arr = []      if (this == null) {         throw new TypeError(' this is null or not defined');     }      var len = this.length >>> 0      if (typeof callback !== "function") {         throw new TypeError(callback + ' is not a function');     }     if (arguments.length > 1) {         _this = thisValue     }      for (var i = 0; i < len; i++) {         arr.push(callback.call(_this, this[i], i, this))     }     return arr } 

封装 myEvery 方法

Array.prototype.myEvery = function (callback, thisValue) {     var _this      if (this == null) {         throw new TypeError(' this is null or not defined');     }      var len = this.length >>> 0      if (typeof callback !== "function") {         throw new TypeError(callback + ' is not a function');     }      if (arguments.length > 1) {         _this = thisValue     }      for (var i = 0; i < len; i++) {         if (!callback.call(_this, this[i], i, this)) {             return false         }     }     return true } 

封装 myReduce 方法

Array.prototype.myEvery = function (callback, initialValue) {     var value = 0     console.log(value)     if (this == null) {         throw new TypeError(' this is null or not defined');     }     var len = this.length >>> 0     if (typeof callback !== "function") {         throw new TypeError(callback + ' is not a function');     }      if (arguments.length > 1) {         value = initialValue     }     for (var i = 0; i < len; i++) {         value = callback(value, this[i], i, this)     }     return value } 

获取 URL 参数返回对象

function getURLParam(url) {   let obj = {};   url.replace(/(?<=[?|&])(w+)=(w+)/g, function (data, key, value) {     if (obj[key]) {       obj[key] = [].concat(obj[key], value);     } else {       obj[key] = value;     }   });   return obj; } 

HTML 字符串模板

function render(template, data) {   return template.replace(/{(w+)}/g, function ($1, key) {     return data[key] ? data[key] : "";   }); }  let html = "我叫{name},今年{id}岁。"; let data = {   name: "Yevin",   age: 18, }; render(html, data); //我叫Yevin,今年18岁 

利用 JSONP 实现跨域请求

function jsonp(url, callbackName) {   return new Promise((resolve, reject) => {     var script = document.createElement("script");     script.src = "demo.js";     document.body.appendChild(script);      window[callbackName] = function (res) {       //移除remove       script.remove();       //返回数据       resolve(res);     };   }); } 

原生 JS 封装 AJAX

function Ajax(method, url, callback, data, async = true) {   var xhr;   //同一转换method方法为大写   method = method.toUpperCase();   // 开启XMLHTTPRequest   xhr = new XMLHttpRequest();   // 监控状态变化 执行回调函数   xhr.onreadystatechange = function () {     if (xhr.readyState == 4 && xhr.readyState == 200) {       // 回调函数返回参数       callback(xhr.responseText);     }   };   // 判断请求方式 get post或者其他   if (method == "GET") {     xhr.open("GET", url, async);   } else if (method == "POST") {     xhr.open("POST", url, async);     // 设置请求头     xhr.setRequestHeader("Content-Type", "application/json");     // 发送参数     xhr.send(data);   } } 

函数柯里化

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

偏函数

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