【前端】一网打尽──前端进阶和面试必会的8个手写代码

IT科技2025-11-04 22:08:3428124

写在前面

我们知道在前端进阶和面试的前端─前时候,会考察到很多手写源码的网打问题,这些是尽─阶和通过学习和练习是可以掌握的,下面列举了八个手写的端进的个代码代码系列,希望能够对你有所帮助。面试

1 手写Promise系列

在Promise的手写学习中,之前也写过相关的前端─前分享文章,敬请参见《从小白视角上手Promise、网打Async/Await和手撕代码》。尽─阶和

1.1 Promise.all

//手写promise.all Promise.prototype._all = promiseList => {   // 当输入的端进的个代码是一个promise列表   const len = promiseList.length;   const result = [];   let count = 0;   //    return new Promise((resolve,reject)=>{     // 循环遍历promise列表中的promise事件     for(let i = 0; i < len; i++){       // 遍历到第i个promise事件,判断其事件是面试成功还是失败       promiseList[i].then(data=>{         result[i] = data;         count++;         // 当遍历到最后一个promise时,结果的手写数组长度和promise列表长度一致,说明成功         count === len && resolve(result);       },前端─前error=>{         return reject(error);       })     }   }) } 

1.2 Promise.race

// 手写promise.race Promise.prototype._race = promiseList => {   const len = promiseList.length;   return new Promise((resolve,reject)=>{     // 循环遍历promise列表中的promise事件     for(let i = 0; i < len; i++){       promiseList[i]().then(data=>{         return resolve(data);       },error=>{         return reject(error);       })     }   }) } 

1.3 Promise.finally

Promise.prototype._finally = function(promiseFunc){   return this.then(data=>Promise.resolve(promiseFunc()).then(data=>data)   ,error=>Promise.reject(promiseFunc()).then(error=>{throw error})) } 

2 手写Aysnc/Await

function asyncGenertor(genFunc){   return new Promise((resolve,reject)=>{     // 生成一个迭代器     const gen = genFunc();     const step = (type,args)=>{       let next;       try{         next = gen[type](args);       }catch(e){         return reject(e);       }       // 从next中获取done和value的值       const {done,value} = next;       // 如果迭代器的状态是true       if(done) return resolve(value);       Promise.resolve(value).then(         val=>step("next",val),         err=>step("throw",err)       )     }     step("next");   }) } 

3 深拷贝

深拷贝:拷贝所有的属性值,云服务器以及属性地址指向的网打值的内存空间。

3.1 丢失引用的尽─阶和深拷贝

当遇到对象时,就再新开一个对象,然后将第二层源对象的属性值,完整地拷贝到这个新开的对象中。

// 丢失引用的深拷贝 function deepClone(obj){   // 判断obj的类型是否为object类型   if(!obj && typeof obj !== "object") return;   // 判断对象是数组类型还是对象类型   let newObj = Array.isArray(obj) ? [] : {};   // 遍历obj的键值对   for(const [key,value] of Object.entries(obj)){     newObj[key] = typeof value === "string" ? deepClone(value) : value;   };   return newObj; } 

3.2 终极方案的深拷贝(栈和深度优先的思想)

其思路是:引入一个数组 uniqueList 用来存储已经拷贝的数组,每次循环遍历时,先判断对象是否在 uniqueList 中了,如果在的话就不执行拷贝逻辑了。

function deepCopy(obj){   // 用于去重   const uniqueList = [];   // 设置根节点   let root = {};   // 遍历数组   const loopList = [{     parent: root,     key: undefined,     data: obj   }];   // 遍历循环   while(loopList.length){     // 深度优先-将数组最后的元素取出     const {parent,key,data} = loopList.pop();     // 初始化赋值目标,key--undefined时拷贝到父元素,否则拷贝到子元素     let result = parent;     if(typeof key !== "undefined") result = parent[key] = {};     // 数据已存在时     let uniqueData = uniqueList.find(item=>item.source === data);     if(uniqueData){       parent[key] = uniqueData.target;       // 中断本次循环       continue;     }     // 数据不存在时     // 保存源数据,在拷贝数据中对应的引用     uniqueList.push({       source:data,       target:result     });     // 遍历数据     for(let k in data){       if(data.hasOwnProperty(k)){         typeof data[k] === "object"          ?           // 下一次循环           loopList.push({             parent:result,             key:k,             data:data[k]           })         :          result[k] = data[k];       }     }   }   return root; } 

4 手写一个单例模式

单例模式:保证一个类仅有一个实例,并提供一个访问它的源码库全局访问点。实现方法一般是先判断实例是否存在,如果存在直接返回,如果不存在就先创建再返回。

// 创建单例对象,使用闭包 const getSingle = function(func){   let result;   return function(){     return result || (result = func.apply(this,arguments));   } } // 使用Proxy拦截 const proxy = function(func){   let reuslt;   const handler = {     construct:function(){       if(!result) result = Reflect.construct(func,arguments);       return result;     }   }   return new Proxy(func,hendler); } 

5 手写封装一个ajax函数

/*  封装自己的ajax函数 参数1:{string} method 请求方法 参数2:{string} url 请求地址 参数2:{Object} params 请求参数 参数3:{function} done 请求完成后执行的回调函数 */ function ajax(method,url,params,done){   // 1.创建xhr对象,兼容写法   let xhr = window.XMLHttpRequest    ? new XMLHttpRequest()   : new ActiveXObject("Microsoft.XMLHTTP");   // 将method转换成大写   method = method.toUpperCase();   // 参数拼接   let newParams = [];   for(let key in params){     newParams.push(`${key}=${params[k]}`);   }   let str = newParams.join("&");   // 判断请求方法   if(method === "GET") url += `?${str}`;   // 打开请求方式   xhr.open(method,url);   let data = null;   if(method === "POST"){     // 设置请求头     xhr.setRequestHeader(("Content-Type","application/x-www-form-urlencoded"));     data = str;   }   xhr.send(data);   // 指定xhr状态变化事件处理函数   // 执行回调函数   xhr.onreadystatechange = function(){     if(this.readyState === 4) done(JSON.parse(xhr.responseText));   } } 

6 手写“防抖”和“节流”

在Promise的学习中,之前也写过相关的分享文章,敬请参见《一网打尽──他们都在用这些”防抖“和”节流“方法》。

6.1 防抖

/*  func:要进行防抖处理的函数 delay:要进行延时的时间 immediate:是否使用立即执行 true立即执行 false非立即执行 */ function debounce(func,delay,immediate){   let timeout; //定时器   return function(arguments){     // 判断定时器是否存在,存在的话进行清除,重新进行定时器计数     if(timeout) clearTimeout(timeout);     // 判断是立即执行的防抖还是非立即执行的防抖     if(immediate){//立即执行       const flag = !timeout;//此处是取反操作       timeout = setTimeout(()=>{         timeout = null;       },delay);       // 触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果。       if(flag) func.call(this,arguments);     }else{//非立即执行       timeout = setTimeout(()=>{         func.call(this,arguments);       },delay)     }   } } 

6.2 节流

// 节流--定时器版  function throttle(func,delay){    let timeout;//定义一个定时器标记    return function(arguments){      // 判断是否存在定时器      if(!timeout){         // 创建一个定时器        timeout = setTimeout(()=>{          // delay时间间隔清空定时器          clearTimeout(timeout);          func.call(this,arguments);        },delay)      }    }  } 

7 手写apply、bind、免费源码下载call

7.1 apply

传递给函数的参数处理,不太一样,其他部分跟call一样。

apply接受第二个参数为类数组对象, 这里用了《JavaScript权威指南》中判断是否为类数组对象的方法。

Function.prototype._apply = function (context) {     if (context === null || context === undefined) {         context = window // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)     } else {         context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象     }     // JavaScript权威指南判断是否为类数组对象     function isArrayLike(o) {         if (o &&                                    // o不是null、undefined等             typeof o === object &&                // o是对象             isFinite(o.length) &&                   // o.length是有限数值             o.length >= 0 &&                        // o.length为非负值             o.length === Math.floor(o.length) &&    // o.length是整数             o.length < 4294967296)                  // o.length < 2^32             return true         else             return false     }     const specialPrototype = Symbol(特殊属性Symbol) // 用于临时储存函数     context[specialPrototype] = this; // 隐式绑定this指向到context上     let args = arguments[1]; // 获取参数数组     let result     // 处理传进来的第二个参数     if (args) {         // 是否传递第二个参数         if (!Array.isArray(args) && !isArrayLike(args)) {             throw new TypeError(myApply 第二个参数不为数组并且不为类数组对象抛出错误);         } else {             args = Array.from(args) // 转为数组             result = context[specialPrototype](...args); // 执行函数并展开数组,传递函数参数         }     } else {         result = context[specialPrototype](); // 执行函数      }     delete context[specialPrototype]; // 删除上下文对象的属性     return result; // 返回函数执行结果 }; 

7.2 bind

拷贝源函数:

通过变量储存源函数 使用Object.create复制源函数的prototype给fToBind

返回拷贝的函数

调用拷贝的函数:

new调用判断:通过instanceof判断函数是否通过new调用,来决定绑定的context 绑定this+传递参数 返回源函数的执行结果 Function.prototype._bind = function (objThis, ...params) {     const thisFn = this; // 存储源函数以及上方的params(函数参数)     // 对返回的函数 secondParams 二次传参     let fToBind = function (...secondParams) {         const isNew = this instanceof fToBind // this是否是fToBind的实例 也就是返回的fToBind是否通过new调用         const context = isNew ? this : Object(objThis) // new调用就绑定到this上,否则就绑定到传入的objThis上         return thisFn.call(context, ...params, ...secondParams); // 用call调用源函数绑定this的指向并传递参数,返回执行结果     };     if (thisFn.prototype) {         // 复制源函数的prototype给fToBind 一些情况下函数没有prototype,比如箭头函数         fToBind.prototype = Object.create(thisFn.prototype);     }     return fToBind; // 返回拷贝的函数 }; 

7.3 call

根据call的规则设置上下文对象,也就是this的指向。

通过设置context的属性,将函数的this指向隐式绑定到context上

通过隐式绑定执行函数并传递参数。

删除临时属性,返回函数执行结果

Function.prototype._call = function (context, ...arr) {     if (context === null || context === undefined) {        // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)         context = window      } else {         context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象     }     const specialPrototype = Symbol(特殊属性Symbol) // 用于临时储存函数     context[specialPrototype] = this; // 函数的this指向隐式绑定到context上     let result = context[specialPrototype](...arr); // 通过隐式绑定执行函数并传递参数     delete context[specialPrototype]; // 删除上下文对象的属性     return result; // 返回函数执行结果 }; 

8 手写继承

8.1 构造函数式继承

构造函数式继承并没有继承父类原型上的方法。

function fatherUser(username, password) {   let _password = password    this.username = username    fatherUser.prototype.login = function () {       console.log(this.username + 要登录父亲账号,密码是 + _password)   } } function sonUser(username, password) {   fatherUser.call(this, username, password)   this.articles = 3 // 文章数量 } const yichuanUser = new sonUser(yichuan, xxx) console.log(yichuanUser.username) // yichuan console.log(yichuanUser.username) // xxx console.log(yichuanUser.login()) // TypeError: yichuanUser.login is not a function 

8.2 组合式继承

function fatherUser(username, password) {   let _password = password    this.username = username    fatherUser.prototype.login = function () {       console.log(this.username + 要登录fatherUser,密码是 + _password)   } } function sonUser(username, password) {   fatherUser.call(this, username, password) // 第二次执行 fatherUser 的构造函数   this.articles = 3 // 文章数量 } sonUser.prototype = new fatherUser(); // 第二次执行 fatherUser 的构造函数 const yichuanUser = new sonUser(yichuan, xxx) 

8.3 寄生组合继承

上面的继承方式有所缺陷,所以写这种方式即可。

function Parent() {   this.name = parent; } function Child() {   Parent.call(this);   this.type = children; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; 

参考文章

《前端进阶之必会的JavaScript技巧总结》 《js基础-面试官想知道你有多理解call,apply,bind?[不看后悔系列]》
本文地址:http://www.bzuk.cn/news/8d32799664.html
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

全站热门

保护电脑文件安全(以电脑文件加密码错误为中心的保护策略探讨)

C语言库函数 Memcpy 和 Memmove 的区别,你知道多少?

6月份Github上热门JavaScript项目排行

Python实现之初等函数三之三角函数

HTCOneMax拍照表现如何?(探索HTCOneMax相机的功能与性能)

带Kubernetes的容器化存储在大企业成为主流

大规模信息流推荐系统 研发效能提升之优秀实践

浅谈RSocket与响应式编程

友情链接

滇ICP备2023006006号-33