我们知道在前端进阶和面试的前端─前时候,会考察到很多手写源码的网打问题,这些是尽─阶和通过学习和练习是可以掌握的,下面列举了八个手写的端进的个代码代码系列,希望能够对你有所帮助。面试
在Promise的手写学习中,之前也写过相关的前端─前分享文章,敬请参见《从小白视角上手Promise、网打Async/Await和手撕代码》。尽─阶和
深拷贝:拷贝所有的属性值,云服务器提供商以及属性地址指向的网打值的内存空间。
当遇到对象时,就再新开一个对象,然后将第二层源对象的属性值,完整地拷贝到这个新开的对象中。
// 丢失引用的深拷贝 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; }其思路是:引入一个数组 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; }单例模式:保证一个类仅有一个实例,并提供一个访问它的云南idc服务商全局访问点。实现方法一般是先判断实例是否存在,如果存在直接返回,如果不存在就先创建再返回。
// 创建单例对象,使用闭包 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); }在Promise的学习中,之前也写过相关的分享文章,敬请参见《一网打尽──他们都在用这些”防抖“和”节流“方法》。
传递给函数的参数处理,不太一样,其他部分跟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; // 返回函数执行结果 };拷贝源函数:
通过变量储存源函数 使用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; // 返回拷贝的函数 };根据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; // 返回函数执行结果 };构造函数式继承并没有继承父类原型上的方法。
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上面的继承方式有所缺陷,所以写这种方式即可。
function Parent() { this.name = parent; } function Child() { Parent.call(this); this.type = children; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child;