【总结】手写实现JS常见核心的概念

张开发
2026/4/20 8:29:21 15 分钟阅读
【总结】手写实现JS常见核心的概念
文章目录1.函数防抖和函数节流2.对象深拷贝3.数组去重4.apply,call和bind方法5.new操作符6.instanceof()方法1.函数防抖和函数节流函数防抖和节流都是通过限制事件的触发频率来进行性能优化,常见场景有鼠标拖拽,窗口移动等,其中,函数防抖的原理是:触发事件的n秒内,若事件再次被触发,则重新计时,直到倒计时顺利结束,才执行事件回调函数节流的原理是:触发事件后立即执行一次回调,但在n秒内对于再次触发的事件不予回应实现代码如下:/****1.防抖节流函数****/// 防抖:1s内再次触发事件则重新计时,直到倒计时结束才执行事件回调// 实现思路:在闭包中,内函数的定时器调用外函数的timer// 口诀:重复就清除,赋值不声明,延迟再执行functiondebounce(fn,delay){lettimernull;returnfunction(...args){if(timer)clearTimeout(timer)timersetTimeout((){fn.apply(this,args)timernull},delay)}}// 调用示例:document.getElementById(btn1).addEventListener(click,debounce(function(){console.log(点击防抖了...)},1000))// 节流:1s内只触发一次事件,期间再次触发不予回应// 实现思路:在闭包中,判断两次触发的时间是否大于1s(delay的值),是就执行// 口诀:新旧时间戳相减,间隔大于延迟就执行并更新functionthrottle(fn,delay){letlastTimenull;returnfunction(...args){constnowTimeDate.now()if(nowTime-lastTimedelay){fn.apply(this,args)lastTimenowTime}}}//调用示例:document.getElementById(btn2).addEventListener(click,throttle(function(){console.log(点击节流了...)},1000))2.对象深拷贝思路是通过递归方法,对于对象属性仍为复杂类型时,递归调用当前函数,直到变为简单类型时返回/****2.对象深拷贝****/// 实现思路:判断传参是否为简单类型,若是则直return,若否,声明一个变量(空对象或空数组),其类型取决于确定为复杂类型的传参的类型,遍历传参并判断当前属性是否是复杂类型,若是则递归调用functiondeepClone(obj){if(typeofobj!object||objnull)returnobjvarnewObjArray.isArray(obj)?[]:{}for(letkeyinobj){if(obj.hasOwnProperty(key))newObj[key]deepClone(obj[key])}returnnewObj}3.数组去重/*****3.数组去重*****/functionuniqueArr(arr){varnewArr[]for(vari0;iarr.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.MyCallfunction(ctx,...args){//回顾:this指向call的第一个参数,而第二个和之后的参数用于传参// 2.如果ctx是null或undefined,则将ctx设置为全局对象ctxctx||window;// 3.利用Symbol的唯一性,创建一个独一无二的属性名,以防止覆盖原有属性constfnSymbolSymbol();// 4.将当前函数this挂载到ctx上,作为其ctx对象的一个属性,以便在ctx上调用ctx[fnSymbol]this;//call的第一个参数就是this指向的,也就是当前函数// 5.调用函数,并将剩余的参数传入constresultctx[fnSymbol](...args);//call的第二个参数以及以后的参数,都是传入的参数// 6.删除该属性deletectx[fnSymbol];// 7.返回结果returnresult;}//手写myApply方法,和myCall的唯一区别是第二个参数要写成数组形式// 手写自定义myApply,就是把...args换成数组ArrayargsFunction.prototype.MyApplyfunction(ctx,args){ctxctx||window;constfnSymbolSymbol();ctx[fnSymbol]this;constresultctx[fnSymbol](...args);deletectx[fnSymbol];returnresult;}//手写myBind方法,在闭包中调用myCall即可// 手写自定义的myBind,思路是返回一个函数,在该函数内部调用myCallFunction.prototype.MyBindfunction(ctx,...args1){constthatthis;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}constnewTesttest.MyBind(obj,1);newTest(2,3);//obj 1 2 35.new操作符new一个对象时,发生了什么?1.创建一个空对象,继承构造函数的原型2.执行构造函数:改变this指向3.判断对象类型,若是对象或函数,则直接返回;否则返回创建的空对象4.返回实例对象实现代码:functionmyNew(constructor,...args){// 1.创建一个空对象,继承构造函数的原型constobjObject.create(constructor.prototype)// 2.执行构造函数:改变this指向constresultconstructor.apply(obj,args)// 3.判断对象类型,若是对象或函数,则直接返回;否则返回创建的空对象if(typeofresultobjectresult!null||typeofresultfunction){returnresult}// 4.返回实例对象returnobj;}//测试用例:functionPerson(name){this.namename;}constpmyNew(Person,test);console.log(p.name);// test6.instanceof()方法原生instanceof的作用和语法是:判断A是否是B的实例--instanceof(A,B)判断条件:A的原型链上是否有B的prototypefunctionMyInstanceof(A,B){// 1.若A是null或undefined直接返回falseif(Anull||typeofA!object)returnfalse;// 2.获取A的原型constprotoA.__proto__;// 3.遍历原型,直到找到B.prototype或nullwhile(true){if(protonull)returnfalseif(protoB.prototype)returntrueprotoproto.__proto__}}

更多文章