我们知道在前端进阶和面试的前端─前时候,会考察到很多手写源码的网打问题,这些是尽─阶和通过学习和练习是可以掌握的,下面列举了八个手写的端进的个代码代码系列,希望能够对你有所帮助。面试 在Promise的手写学习中,之前也写过相关的前端─前分享文章,敬请参见《从小白视角上手Promise、网打Async/Await和手撕代码》。尽─阶和 深拷贝:拷贝所有的属性值,源码下载以及属性地址指向的网打值的内存空间。 当遇到对象时,就再新开一个对象,然后将第二层源对象的属性值,完整地拷贝到这个新开的对象中。 其思路是:引入一个数组 uniqueList 用来存储已经拷贝的数组,每次循环遍历时,先判断对象是否在 uniqueList 中了,如果在的话就不执行拷贝逻辑了。 单例模式:保证一个类仅有一个实例,并提供一个访问它的亿华云全局访问点。实现方法一般是先判断实例是否存在,如果存在直接返回,如果不存在就先创建再返回。 在Promise的学习中,之前也写过相关的分享文章,敬请参见《一网打尽──他们都在用这些”防抖“和”节流“方法》。 传递给函数的参数处理,不太一样,其他部分跟call一样。 apply接受第二个参数为类数组对象, 这里用了《JavaScript权威指南》中判断是否为类数组对象的方法。 拷贝源函数: 返回拷贝的函数 调用拷贝的函数: 根据call的规则设置上下文对象,也就是this的指向。 通过设置context的属性,将函数的this指向隐式绑定到context上 通过隐式绑定执行函数并传递参数。 删除临时属性,返回函数执行结果 构造函数式继承并没有继承父类原型上的方法。 上面的继承方式有所缺陷,所以写这种方式即可。写在前面
1 手写Promise系列
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 丢失引用的尽─阶和深拷贝
3.2 终极方案的深拷贝(栈和深度优先的思想)
4 手写一个单例模式
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 手写“防抖”和“节流”
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
7.2 bind
7.3 call
8 手写继承
8.1 构造函数式继承
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 寄生组合继承
参考文章
《前端进阶之必会的JavaScript技巧总结》 《js基础-面试官想知道你有多理解call,apply,bind?[不看后悔系列]》