【总结】手写实现JS常见核心的概念
文章目录
- 1.函数防抖和函数节流
- 2.对象深拷贝
- 3.数组去重
- 4.apply,call和bind方法
- 5.new操作符
- 6.instanceof()方法
1.函数防抖和函数节流
函数防抖和节流都是通过限制事件的触发频率来进行性能优化,常见场景有鼠标拖拽,窗口移动等,其中,
函数防抖的原理是:触发事件的n秒内,若事件再次被触发,则重新计时,直到倒计时顺利结束,才执行事件回调
函数节流的原理是:触发事件后立即执行一次回调,但在n秒内对于再次触发的事件不予回应
实现代码如下:
/****1.防抖节流函数****/// 防抖:1s内再次触发事件则重新计时,直到倒计时结束才执行事件回调// 实现思路:在闭包中,内函数的定时器调用外函数的timer// 口诀:重复就清除,赋值不声明,延迟再执行functiondebounce(fn,delay){lettimer=null;returnfunction(...args){if(timer)clearTimeout(timer)timer=setTimeout(()=>{fn.apply(this,args)timer=null},delay)}}// 调用示例:document.getElementById("btn1").addEventListener("click",debounce(function(){console.log("点击防抖了...")},1000))// 节流:1s内只触发一次事件,期间再次触发不予回应// 实现思路:在闭包中,判断两次触发的时间是否大于1s(delay的值),是就执行// 口诀:新旧时间戳相减,间隔大于延迟就执行并更新functionthrottle(fn,delay){letlastTime=null;returnfunction(...args){constnowTime=Date.now()if(nowTime-lastTime>delay){fn.apply(this,args)lastTime=nowTime}}}//调用示例:document.getElementById("btn2").addEventListener("click",throttle(function(){console.log("点击节流了...")},1000))2.对象深拷贝
思路是通过递归方法,对于对象属性仍为复杂类型时,递归调用当前函数,直到变为简单类型时返回
/****2.对象深拷贝****/// 实现思路:判断传参是否为简单类型,若是则直return,若否,声明一个变量(空对象或空数组),其类型取决于确定为复杂类型的传参的类型,遍历传参并判断当前属性是否是复杂类型,若是则递归调用functiondeepClone(obj){if(typeofobj!=='object'||obj===null)returnobjvarnewObj=Array.isArray(obj)?[]:{}for(letkeyinobj){if(obj.hasOwnProperty(key))newObj[key]=deepClone(obj[key])}returnnewObj}3.数组去重
/*****3.数组去重*****/functionuniqueArr(arr){varnewArr=[]for(vari=0;i<arr.length;i++){if(newArr.indexOf(arr[i])==-1){//若是新数组中没有当前元素,则加入新数组中newArr.push(arr[i])}}returnnewArr}functionuniqueArr2(arr){returnArray.from(newSet(arr))}// 调用示例:console.log(uniqueArr([1,2,3,4,5,4,3,2,1]))4.apply,call和bind方法
// 手写自定义的myCall// 1.绑定到Function的原型上,以便每个函数都能访问Function.prototype.MyCall=function(ctx,...args){//回顾:this指向call的第一个参数,而第二个和之后的参数用于传参// 2.如果ctx是null或undefined,则将ctx设置为全局对象ctx=ctx||window;// 3.利用Symbol的唯一性,创建一个独一无二的属性名,以防止覆盖原有属性constfnSymbol=Symbol();// 4.将当前函数this挂载到ctx上,作为其ctx对象的一个属性,以便在ctx上调用ctx[fnSymbol]=this;//call的第一个参数就是this指向的,也就是当前函数// 5.调用函数,并将剩余的参数传入constresult=ctx[fnSymbol](...args);//call的第二个参数以及以后的参数,都是传入的参数// 6.删除该属性deletectx[fnSymbol];// 7.返回结果returnresult;}//手写myApply方法,和myCall的唯一区别是第二个参数要写成数组形式// 手写自定义myApply,就是把...args换成数组ArrayargsFunction.prototype.MyApply=function(ctx,args){ctx=ctx||window;constfnSymbol=Symbol();ctx[fnSymbol]=this;constresult=ctx[fnSymbol](...args);deletectx[fnSymbol];returnresult;}//手写myBind方法,在闭包中调用myCall即可// 手写自定义的myBind,思路是返回一个函数,在该函数内部调用myCallFunction.prototype.MyBind=function(ctx,...args1){constthat=this;returnfunction(...args2){constargs=[...args1,...args2];returnthat.MyCall(ctx,...args)}}//测试用例:functiontest(a,b,c){console.log(this.name);console.log(a,b,c);}constobj={name:'obj'}constnewTest=test.MyBind(obj,1);newTest(2,3);//obj 1 2 35.new操作符
new一个对象时,发生了什么?
1.创建一个空对象,继承构造函数的原型2.执行构造函数:改变this指向3.判断对象类型,若是对象或函数,则直接返回;否则返回创建的空对象4.返回实例对象实现代码:
functionmyNew(constructor,...args){// 1.创建一个空对象,继承构造函数的原型constobj=Object.create(constructor.prototype)// 2.执行构造函数:改变this指向constresult=constructor.apply(obj,args)// 3.判断对象类型,若是对象或函数,则直接返回;否则返回创建的空对象if(typeofresult==='object'&&result!==null||typeofresult==='function'){returnresult}// 4.返回实例对象returnobj;}//测试用例:functionPerson(name){this.name=name;}constp=myNew(Person,'test');console.log(p.name);// test6.instanceof()方法
原生instanceof的作用和语法是:判断A是否是B的实例--instanceof(A,B)
判断条件:A的原型链上是否有B的prototype
functionMyInstanceof(A,B){// 1.若A是null或undefined直接返回falseif(A===null||typeofA!=='object')returnfalse;// 2.获取A的原型constproto=A.__proto__;// 3.遍历原型,直到找到B.prototype或nullwhile(true){if(proto===null)returnfalseif(proto===B.prototype)returntrueproto=proto.__proto__}}