<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一文幫你搞定 90% 的 JS 手寫題

          共 19506字,需瀏覽 40分鐘

           ·

          2021-12-20 13:16

          點(diǎn)擊上方關(guān)注?前端技術(shù)江湖一起學(xué)習(xí),天天進(jìn)步


          還在害怕手寫題嗎,本文可以幫你擴(kuò)展并鞏固自己的JS基礎(chǔ),順便搞定90%的手寫題。在工作中還可以對(duì)常用的需求進(jìn)行手寫實(shí)現(xiàn),比如深拷貝、防抖節(jié)流等可以直接用于往后的項(xiàng)目中,提高項(xiàng)目開發(fā)效率。不說(shuō)廢話了,下面就直接上代碼吧。

          1.call的實(shí)現(xiàn)

          • 第一個(gè)參數(shù)為null或者undefined時(shí),this指向全局對(duì)象window,值為原始值的指向該原始值的自動(dòng)包裝對(duì)象,如 String、Number、Boolean
          • 為了避免函數(shù)名與上下文(context)的屬性發(fā)生沖突,使用Symbol類型作為唯一值
          • 將函數(shù)作為傳入的上下文(context)屬性執(zhí)行
          • 函數(shù)執(zhí)行完成后刪除該屬性
          • 返回執(zhí)行結(jié)果
          Function.prototype.myCall?=?function(context,...args){
          ????let?cxt?=?context?||?window;
          ????//將當(dāng)前被調(diào)用的方法定義在cxt.func上.(為了能以對(duì)象調(diào)用形式綁定this)
          ????//新建一個(gè)唯一的Symbol變量避免重復(fù)
          ????let?func?=?Symbol()?
          ????cxt[func]?=?this;
          ????args?=?args???args?:?[]
          ????//以對(duì)象調(diào)用形式調(diào)用func,此時(shí)this指向cxt?也就是傳入的需要綁定的this指向
          ????const?res?=?args.length?>?0???cxt[func](...args)?:?cxt[func]();
          ????//刪除該方法,不然會(huì)對(duì)傳入對(duì)象造成污染(添加該方法)
          ????delete?cxt[func];
          ????return?res;
          }

          2.apply的實(shí)現(xiàn)

          • 前部分與call一樣
          • 第二個(gè)參數(shù)可以不傳,但類型必須為數(shù)組或者類數(shù)組
          Function.prototype.myApply?=?function(context,args?=?[]){
          ????let?cxt?=?context?||?window;
          ????//將當(dāng)前被調(diào)用的方法定義在cxt.func上.(為了能以對(duì)象調(diào)用形式綁定this)
          ????//新建一個(gè)唯一的Symbol變量避免重復(fù)
          ????let?func?=?Symbol()
          ????cxt[func]?=?this;
          ????//以對(duì)象調(diào)用形式調(diào)用func,此時(shí)this指向cxt?也就是傳入的需要綁定的this指向
          ????const?res?=?args.length?>?0???cxt[func](...args)?:?cxt[func]();
          ????delete?cxt[func];
          ????return?res;
          }

          3.bind的實(shí)現(xiàn)

          需要考慮:

          • bind() 除了 this 外,還可傳入多個(gè)參數(shù);
          • bind 創(chuàng)建的新函數(shù)可能傳入多個(gè)參數(shù);
          • 新函數(shù)可能被當(dāng)做構(gòu)造函數(shù)調(diào)用;
          • 函數(shù)可能有返回值;

          實(shí)現(xiàn)方法:

          • bind 方法不會(huì)立即執(zhí)行,需要返回一個(gè)待執(zhí)行的函數(shù);(閉包)
          • 實(shí)現(xiàn)作用域綁定(apply)
          • 參數(shù)傳遞(apply 的數(shù)組傳參)
          • 當(dāng)作為構(gòu)造函數(shù)的時(shí)候,進(jìn)行原型繼承
          Function.prototype.myBind?=?function?(context,?...args)?{
          ????//新建一個(gè)變量賦值為this,表示當(dāng)前函數(shù)
          ????const?fn?=?this
          ????//判斷有沒有傳參進(jìn)來(lái),若為空則賦值[]
          ????args?=?args???args?:?[]
          ????//返回一個(gè)newFn函數(shù),在里面調(diào)用fn
          ????return?function?newFn(...newFnArgs)?{
          ????????if?(this?instanceof?newFn)?{
          ????????????return?new?fn(...args,?...newFnArgs)
          ????????}
          ????????return?fn.apply(context,?[...args,...newFnArgs])
          ????}
          }
          • 測(cè)試
          let?name?=?'小王',age?=17;
          let?obj?=?{
          ????name:'小張',
          ????age:?this.age,
          ????myFun:?function(from,to){
          ????????console.log(this.name?+?'?年齡?'?+?this.age+'來(lái)自?'+from+'去往'+?to)
          ????}
          }
          let?db?=?{
          ????name:?'德瑪',
          ????age:?99
          }

          //結(jié)果
          obj.myFun.myCall(db,'成都','上海');?????//?德瑪?年齡?99??來(lái)自?成都去往上海
          obj.myFun.myApply(db,['成都','上海']);??????//?德瑪?年齡?99??來(lái)自?成都去往上海
          obj.myFun.myBind(db,'成都','上海')();???????//?德瑪?年齡?99??來(lái)自?成都去往上海
          obj.myFun.myBind(db,['成都','上海'])();???//?德瑪?年齡?99??來(lái)自?成都,?上海去往?undefined

          4.寄生式組合繼承

          function?Person(obj)?{
          ????this.name?=?obj.name
          ????this.age?=?obj.age
          }
          Person.prototype.add?=?function(value){
          ????console.log(value)
          }
          var?p1?=?new?Person({name:"番茄",?age:?18})

          function?Person1(obj)?{
          ????Person.call(this,?obj)
          ????this.sex?=?obj.sex
          }
          //?這一步是繼承的關(guān)鍵
          Person1.prototype?=?Object.create(Person.prototype);
          Person1.prototype.constructor?=?Person1;

          Person1.prototype.play?=?function(value){
          ????console.log(value)
          }
          var?p2?=?new?Person1({name:"雞蛋",?age:?118,?sex:?"男"})

          5.ES6繼承

          //class?相當(dāng)于es5中構(gòu)造函數(shù)
          //class中定義方法時(shí),前后不能加function,全部定義在class的protopyte屬性中
          //class中定義的所有方法是不可枚舉的
          //class中只能定義方法,不能定義對(duì)象,變量等
          //class和方法內(nèi)默認(rèn)都是嚴(yán)格模式
          //es5中constructor為隱式屬性
          class?People{
          ??constructor(name='wang',age='27'){
          ????this.name?=?name;
          ????this.age?=?age;
          ??}
          ??eat(){
          ????console.log(`${this.name}?${this.age}?eat?food`)
          ??}
          }
          //繼承父類
          class?Woman?extends?People{?
          ???constructor(name?=?'ren',age?=?'27'){?
          ?????//繼承父類屬性
          ?????super(name,?age);?
          ???}?
          ????eat(){?
          ?????//繼承父類方法
          ??????super.eat()?
          ????}?
          }?
          let?wonmanObj=new?Woman('xiaoxiami');?
          wonmanObj.eat();

          //es5繼承先創(chuàng)建子類的實(shí)例對(duì)象,然后再將父類的方法添加到this上(Parent.apply(this))。?
          //es6繼承是使用關(guān)鍵字super先創(chuàng)建父類的實(shí)例對(duì)象this,最后在子類class中修改this。

          6.new的實(shí)現(xiàn)

          • 一個(gè)繼承自 Foo.prototype 的新對(duì)象被創(chuàng)建。
          • 使用指定的參數(shù)調(diào)用構(gòu)造函數(shù) Foo,并將 this 綁定到新創(chuàng)建的對(duì)象。new Foo 等同于 new Foo(),也就是沒有指定參數(shù)列表,F(xiàn)oo 不帶任何參數(shù)調(diào)用的情況。
          • 由構(gòu)造函數(shù)返回的對(duì)象就是 new 表達(dá)式的結(jié)果。如果構(gòu)造函數(shù)沒有顯式返回一個(gè)對(duì)象,則使用步驟1創(chuàng)建的對(duì)象。
          • 一般情況下,構(gòu)造函數(shù)不返回值,但是用戶可以選擇主動(dòng)返回對(duì)象,來(lái)覆蓋正常的對(duì)象創(chuàng)建步驟
          function?Ctor(){
          ????....
          }

          function?myNew(ctor,...args){
          ????if(typeof?ctor?!==?'function'){
          ??????throw?'myNew?function?the?first?param?must?be?a?function';
          ????}
          ????var?newObj?=?Object.create(ctor.prototype);?//創(chuàng)建一個(gè)繼承自ctor.prototype的新對(duì)象
          ????var?ctorReturnResult?=?ctor.apply(newObj,?args);?//將構(gòu)造函數(shù)ctor的this綁定到newObj中
          ????var?isObject?=?typeof?ctorReturnResult?===?'object'?&&?ctorReturnResult?!==?null;
          ????var?isFunction?=?typeof?ctorReturnResult?===?'function';
          ????if(isObject?||?isFunction){
          ????????return?ctorReturnResult;
          ????}
          ????return?newObj;
          }

          let?c?=?myNew(Ctor);

          7.instanceof的實(shí)現(xiàn)

          • instanceof 是用來(lái)判斷A是否為B的實(shí)例,表達(dá)式為:A instanceof B,如果A是B的實(shí)例,則返回true,否則返回false。
          • instanceof 運(yùn)算符用來(lái)測(cè)試一個(gè)對(duì)象在其原型鏈中是否存在一個(gè)構(gòu)造函數(shù)的 prototype 屬性。
          • 不能檢測(cè)基本數(shù)據(jù)類型,在原型鏈上的結(jié)果未必準(zhǔn)確,不能檢測(cè)null,undefined
          • 實(shí)現(xiàn):遍歷左邊變量的原型鏈,直到找到右邊變量的 prototype,如果沒有找到,返回 false
          function?myInstanceOf(a,b){
          ????let?left?=?a.__proto__;
          ????let?right?=?b.prototype;
          ????while(true){
          ????????if(left?==?null){
          ????????????return?false
          ????????}
          ????????if(left?==?right){
          ????????????return?true
          ????????}
          ????????left?=?left.__proto__
          ????}
          }

          //instanceof 運(yùn)算符用于判斷構(gòu)造函數(shù)的 prototype 屬性是否出現(xiàn)在對(duì)象的原型鏈中的任何位置。
          function?myInstanceof(left,?right)?{
          ????let?proto?=?Object.getPrototypeOf(left),?//?獲取對(duì)象的原型
          ????prototype?=?right.prototype;?//?獲取構(gòu)造函數(shù)的?prototype?對(duì)象
          ????//?判斷構(gòu)造函數(shù)的?prototype?對(duì)象是否在對(duì)象的原型鏈上
          ????while?(true)?{
          ????????if?(!proto)?return?false;
          ????????if?(proto?===?prototype)?return?true;
          ????????proto?=?Object.getPrototypeOf(proto);
          ????}
          }

          8.Object.create()的實(shí)現(xiàn)

          • MDN文檔
          • Object.create()會(huì)將參數(shù)對(duì)象作為一個(gè)新創(chuàng)建的空對(duì)象的原型, 并返回這個(gè)空對(duì)象
          //簡(jiǎn)略版
          function?myCreate(obj){
          ????//?新聲明一個(gè)函數(shù)
          ????function?C(){};
          ????//?將函數(shù)的原型指向obj
          ????C.prototype?=?obj;
          ????//?返回這個(gè)函數(shù)的實(shí)力化對(duì)象
          ????return?new?C()
          }
          //官方版Polyfill
          if?(typeof?Object.create?!==?"function")?{
          ????Object.create?=?function?(proto,?propertiesObject)?{
          ????????if?(typeof?proto?!==?'object'?&&?typeof?proto?!==?'function')?{
          ????????????throw?new?TypeError('Object?prototype?may?only?be?an?Object:?'?+?proto);
          ????????}?else?if?(proto?===?null)?{
          ????????????throw?new?Error("This?browser's?implementation?of?Object.create?is?a?shim?and?doesn't?support?'null'?as?the?first?argument.");
          ????????}

          ????????if?(typeof?propertiesObject?!==?'undefined')?throw?new?Error("This?browser's?implementation?of?Object.create?is?a?shim?and?doesn't?support?a?second?argument.");

          ????????function?F()?{}
          ????????F.prototype?=?proto;

          ????????return?new?F();
          ????};
          }

          9.實(shí)現(xiàn) Object.assign

          Object.assign2?=?function(target,?...source)?{
          ????if?(target?==?null)?{
          ????????throw?new?TypeError('Cannot?convert?undefined?or?null?to?object')
          ????}
          ????let?ret?=?Object(target)?
          ????source.forEach(function(obj)?{
          ????????if?(obj?!=?null)?{
          ????????????for?(let?key?in?obj)?{
          ????????????????if?(obj.hasOwnProperty(key))?{
          ????????????????????ret[key]?=?obj[key]
          ????????????????}
          ????????????}
          ????????}
          ????})
          ????return?ret
          }

          10.Promise的實(shí)現(xiàn)

          實(shí)現(xiàn) Promise 需要完全讀懂 Promise A+ 規(guī)范,不過(guò)從總體的實(shí)現(xiàn)上看,有如下幾個(gè)點(diǎn)需要考慮到:

          • Promise本質(zhì)是一個(gè)狀態(tài)機(jī),且狀態(tài)只能為以下三種:Pending(等待態(tài))、Fulfilled(執(zhí)行態(tài))、Rejected(拒絕態(tài)),狀態(tài)的變更是單向的,只能從Pending -> Fulfilled 或 Pending -> Rejected,狀態(tài)變更不可逆
          • then 需要支持鏈?zhǔn)秸{(diào)用
          class?Promise?{
          ????callbacks?=?[];
          ????state?=?'pending';//增加狀態(tài)
          ????value?=?null;//保存結(jié)果
          ????constructor(fn)?{
          ????????fn(this._resolve.bind(this),?this._reject.bind(this));
          ????}
          ????then(onFulfilled,?onRejected)?{
          ????????return?new?Promise((resolve,?reject)?=>?{
          ????????????this._handle({
          ????????????????onFulfilled:?onFulfilled?||?null,
          ????????????????onRejected:?onRejected?||?null,
          ????????????????resolve:?resolve,
          ????????????????reject:?reject
          ????????????});
          ????????});
          ????}
          ????_handle(callback)?{
          ????????if?(this.state?===?'pending')?{
          ????????????this.callbacks.push(callback);
          ????????????return;
          ????????}
          ?
          ????????let?cb?=?this.state?===?'fulfilled'???callback.onFulfilled?:?callback.onRejected;
          ?
          ????????if?(!cb)?{//如果then中沒有傳遞任何東西
          ????????????cb?=?this.state?===?'fulfilled'???callback.resolve?:?callback.reject;
          ????????????cb(this.value);
          ????????????return;
          ????????}
          ?
          ????????let?ret?=?cb(this.value);
          ????????cb?=?this.state?===?'fulfilled'???callback.resolve?:?callback.reject;
          ????????cb(ret);
          ????}
          ????_resolve(value)?{
          ?
          ????????if?(value?&&?(typeof?value?===?'object'?||?typeof?value?===?'function'))?{
          ????????????var?then?=?value.then;
          ????????????if?(typeof?then?===?'function')?{
          ????????????????then.call(value,?this._resolve.bind(this),?this._reject.bind(this));
          ????????????????return;
          ????????????}
          ????????}
          ?
          ????????this.state?=?'fulfilled';//改變狀態(tài)
          ????????this.value?=?value;//保存結(jié)果
          ????????this.callbacks.forEach(callback?=>?this._handle(callback));
          ????}
          ????_reject(error)?{
          ????????this.state?=?'rejected';
          ????????this.value?=?error;
          ????????this.callbacks.forEach(callback?=>?this._handle(callback));
          ????}
          }

          Promise.resolve

          • Promsie.resolve(value) 可以將任何值轉(zhuǎn)成值為 value 狀態(tài)是 fulfilled 的 Promise,但如果傳入的值本身是 Promise 則會(huì)原樣返回它。
          Promise.resolve(value)?{
          ??if?(value?&&?value?instanceof?Promise)?{
          ????return?value;
          ??}?else?if?(value?&&?typeof?value?===?'object'?&&?typeof?value.then?===?'function')?{
          ????let?then?=?value.then;
          ????return?new?Promise(resolve?=>?{
          ??????then(resolve);
          ????});
          ??}?else?if?(value)?{
          ????return?new?Promise(resolve?=>?resolve(value));
          ??}?else?{
          ????return?new?Promise(resolve?=>?resolve());
          ??}
          }

          Promise.reject

          • 和 Promise.resolve() 類似,Promise.reject() 會(huì)實(shí)例化一個(gè) rejected 狀態(tài)的 Promise。但與 Promise.resolve() 不同的是,如果給 Promise.reject() 傳遞一個(gè) Promise 對(duì)象,則這個(gè)對(duì)象會(huì)成為新 Promise 的值。
          Promise.reject?=?function(reason)?{
          ????return?new?Promise((resolve,?reject)?=>?reject(reason))
          }

          Promise.all

          • 傳入的所有 Promsie 都是 fulfilled,則返回由他們的值組成的,狀態(tài)為 fulfilled 的新 Promise;
          • 只要有一個(gè) Promise 是 rejected,則返回 rejected 狀態(tài)的新 Promsie,且它的值是第一個(gè) rejected 的 Promise 的值;
          • 只要有一個(gè) Promise 是 pending,則返回一個(gè) pending 狀態(tài)的新 Promise;
          Promise.all?=?function(promiseArr)?{
          ????let?index?=?0,?result?=?[]
          ????return?new?Promise((resolve,?reject)?=>?{
          ????????promiseArr.forEach((p,?i)?=>?{
          ????????????Promise.resolve(p).then(val?=>?{
          ????????????????index++
          ????????????????result[i]?=?val
          ????????????????if?(index?===?promiseArr.length)?
          {
          ????????????????????resolve(result)
          ????????????????}
          ????????????},?err?=>?{
          ????????????????reject(err)
          ????????????})
          ????????})
          ????})
          }

          Promise.race

          • Promise.race 會(huì)返回一個(gè)由所有可迭代實(shí)例中第一個(gè) fulfilled 或 rejected 的實(shí)例包裝后的新實(shí)例。
          Promise.race?=?function(promiseArr)?{
          ????return?new?Promise((resolve,?reject)?=>?{
          ????????promiseArr.forEach(p?=>?{
          ????????????Promise.resolve(p).then(val?=>?{
          ????????????????resolve(val)
          ????????????},?err?=>?{
          ????????????????rejecte(err)
          ????????????})
          ????????})
          ????})
          }

          11.Ajax的實(shí)現(xiàn)

          function?ajax(url,method,body,headers){
          ????return?new?Promise((resolve,reject)=>{
          ????????let?req?=?new?XMLHttpRequest();
          ????????req.open(methods,url);
          ????????for(let?key?in?headers){
          ????????????req.setRequestHeader(key,headers[key])
          ????????}
          ????????req.onreadystatechange(()=>{
          ????????????if(req.readystate?==?4){
          ????????????????if(req.status?>=?'200'?&&?req.status?<=?300){
          ????????????????????resolve(req.responeText)
          ????????????????}else{
          ????????????????????reject(req)
          ????????????????}
          ????????????}
          ????????})
          ????????req.send(body)
          ????})
          }

          12.實(shí)現(xiàn)防抖函數(shù)(debounce)

          • 連續(xù)觸發(fā)在最后一次執(zhí)行方法,場(chǎng)景:輸入框匹配
          let?debounce?=?(fn,time?=?1000)?=>?{
          ????let?timeLock?=?null

          ????return?function?(...args)
          {
          ????????clearTimeout(timeLock)
          ????????timeLock?=?setTimeout(()=>{
          ????????????fn(...args)
          ????????},time)
          ????}
          }

          13.實(shí)現(xiàn)節(jié)流函數(shù)(throttle)

          • 在一定時(shí)間內(nèi)只觸發(fā)一次,場(chǎng)景:長(zhǎng)列表滾動(dòng)節(jié)流
          let?throttle?=?(fn,time?=?1000)?=>?{
          ????let?flag?=?true;

          ????return?function?(...args){
          ????????if(flag){
          ????????????flag?=?false;
          ????????????setTimeout(()=>{
          ????????????????flag?=?true;
          ????????????????fn(...args)
          ????????????},time)
          ????????}
          ????}
          }

          14.深拷貝(deepclone)

          • 判斷類型,正則和日期直接返回新對(duì)象
          • 空或者非對(duì)象類型,直接返回原值
          • 考慮循環(huán)引用,判斷如果hash中含有直接返回hash中的值
          • 新建一個(gè)相應(yīng)的new obj.constructor加入hash
          • 遍歷對(duì)象遞歸(普通key和key是symbol情況)
          function?deepClone(obj,hash?=?new?WeakMap()){
          ????if(obj?instanceof?RegExp)?return?new?RegExp(obj);
          ????if(obj?instanceof?Date)?return?new?Date(obj);
          ????if(obj?===?null?||?typeof?obj?!==?'object')?return?obj;
          ????//循環(huán)引用的情況
          ????if(hash.has(obj)){
          ????????return?hash.get(obj)
          ????}
          ????//new?一個(gè)相應(yīng)的對(duì)象
          ????//obj為Array,相當(dāng)于new?Array()
          ????//obj為Object,相當(dāng)于new?Object()
          ????let?constr?=?new?obj.constructor();
          ????hash.set(obj,constr);
          ????for(let?key?in?obj){
          ????????if(obj.hasOwnProperty(key)){
          ????????????constr[key]?=?deepClone(obj[key],hash)
          ????????}
          ????}
          ????//考慮symbol的情況
          ????let?symbolObj?=?Object.getOwnPropertySymbols(obj)
          ????for(let?i=0;i????????if(obj.hasOwnProperty(symbolObj[i])){
          ????????????constr[symbolObj[i]]?=?deepClone(obj[symbolObj[i]],hash)
          ????????}
          ????}
          ????return?constr
          }

          15.數(shù)組扁平化的實(shí)現(xiàn)(flat)

          let?arr?=?[1,2,[3,4,[5,[6]]]]
          console.log(arr.flat(Infinity))//flat參數(shù)為指定要提取嵌套數(shù)組的結(jié)構(gòu)深度,默認(rèn)值為?1
          //用reduce實(shí)現(xiàn)
          function?fn(arr){
          ???return?arr.reduce((prev,cur)=>{
          ??????return?prev.concat(Array.isArray(cur)?fn(cur):cur)
          ???},[])
          }

          16.函數(shù)柯里化

          function?sumFn(a,b,c){return?a+?b?+?c};
          let?sum?=?curry(sumFn);
          sum(2)(3)(5)//10
          sum(2,3)(5)//10
          function?curry(fn,...args){
          ??let?fnLen?=?fn.length,
          ??????argsLen?=?args.length;
          ??//對(duì)比函數(shù)的參數(shù)和當(dāng)前傳入?yún)?shù)
          ??//若參數(shù)不夠就繼續(xù)遞歸返回curry
          ??//若參數(shù)夠就調(diào)用函數(shù)返回相應(yīng)的值
          ??if(fnLen?>?argsLen){
          ????return?function(...arg2s){
          ??????return?curry(fn,...args,...arg2s)
          ????}
          ??}else{
          ????return?fn(...args)
          ??}
          }

          17.使用閉包實(shí)現(xiàn)每隔一秒打印 1,2,3,4

          for?(var?i=1;?i<=5;?i++)?{
          ??(function?(i)?{
          ????setTimeout(()?=>?console.log(i),?1000*i)
          ??})(i)
          }

          18.手寫一個(gè) jsonp

          const?jsonp?=?function?(url,?data)?{
          ????return?new?Promise((resolve,?reject)?=>?{
          ????????//?初始化url
          ????????let?dataString?=?url.indexOf('?')?===?-1???'?'?:?''
          ????????let?callbackName?=?`jsonpCB_${Date.now()}`
          ????????url?+=?`${dataString}callback=${callbackName}`
          ????????if?(data)?{
          ????????????//?有請(qǐng)求參數(shù),依次添加到url
          ????????????for?(let?k?in?data)?{
          ????????????????url?+=?`${k}=${data[k]}`
          ????????????}
          ????????}
          ????????let?jsNode?=?document.createElement('script')
          ????????jsNode.src?=?url
          ????????//?觸發(fā)callback,觸發(fā)后刪除js標(biāo)簽和綁定在window上的callback
          ????????window[callbackName]?=?result?=>?{
          ????????????delete?window[callbackName]
          ????????????document.body.removeChild(jsNode)
          ????????????if?(result)?{
          ????????????????resolve(result)
          ????????????}?else?{
          ????????????????reject('沒有返回?cái)?shù)據(jù)')
          ????????????}
          ????????}
          ????????//?js加載異常的情況
          ????????jsNode.addEventListener('error',?()?=>?{
          ????????????delete?window[callbackName]
          ????????????document.body.removeChild(jsNode)
          ????????????reject('JavaScript資源加載失敗')
          ????????},?false)
          ????????//?添加js節(jié)點(diǎn)到document上時(shí),開始請(qǐng)求
          ????????document.body.appendChild(jsNode)
          ????})
          }
          jsonp('http://192.168.0.103:8081/jsonp',?{
          ????a:?1,
          ????b:?'heiheihei'
          })
          .then(result?=>?{
          ????console.log(result)
          })
          .catch(err?=>?{
          ????console.error(err)
          })

          19.手寫一個(gè)觀察者模式

          class?Subject{
          ??constructor(name){
          ????this.name?=?name
          ????this.observers?=?[]
          ????this.state?=?'XXXX'
          ??}
          ??//?被觀察者要提供一個(gè)接受觀察者的方法
          ??attach(observer){
          ????this.observers.push(observer)
          ??}

          ??//?改變被觀察著的狀態(tài)
          ??setState(newState){
          ????this.state?=?newState
          ????this.observers.forEach(o=>{
          ??????o.update(newState)
          ????})
          ??}
          }

          class?Observer{
          ??constructor(name){
          ????this.name?=?name
          ??}

          ??update(newState){
          ????console.log(`${this.name}say:${newState}`)
          ??}
          }

          //?被觀察者?燈
          let?sub?=?new?Subject('燈')
          let?mm?=?new?Observer('小明')
          let?jj?=?new?Observer('小健')
          ?
          //?訂閱?觀察者
          sub.attach(mm)
          sub.attach(jj)
          ?
          sub.setState('燈亮了來(lái)電了')

          20.EventEmitter 實(shí)現(xiàn)

          class?EventEmitter?{
          ????constructor()?{
          ????????this.events?=?{};
          ????}
          ????on(event,?callback)?{
          ????????let?callbacks?=?this.events[event]?||?[];
          ????????callbacks.push(callback);
          ????????this.events[event]?=?callbacks;
          ????????return?this;
          ????}
          ????off(event,?callback)?{
          ????????let?callbacks?=?this.events[event];
          ????????this.events[event]?=?callbacks?&&?callbacks.filter(fn?=>?fn?!==?callback);
          ????????return?this;
          ????}
          ????emit(event,?...args)?{
          ????????let?callbacks?=?this.events[event];
          ????????callbacks.forEach(fn?=>?{
          ????????????fn(...args);
          ????????});
          ????????return?this;
          ????}
          ????once(event,?callback)?{
          ????????let?wrapFun?=?function?(...args)?{
          ????????????callback(...args);
          ????????????this.off(event,?wrapFun);
          ????????};
          ????????this.on(event,?wrapFun);
          ????????return?this;
          ????}
          }

          21.生成隨機(jī)數(shù)的各種方法?

          function?getRandom(min,?max)?{
          ??return?Math.floor(Math.random()?*?(max?-?min))?+?min???
          }

          22.如何實(shí)現(xiàn)數(shù)組的隨機(jī)排序?

          let?arr?=?[2,3,454,34,324,32]
          arr.sort(randomSort)
          function?randomSort(a,?b)?{
          ??return?Math.random()?>?0.5???-1?:?1;
          }

          23.寫一個(gè)通用的事件偵聽器函數(shù)。

          const?EventUtils?=?{
          ??//?視能力分別使用dom0||dom2||IE方式?來(lái)綁定事件
          ??//?添加事件
          ??addEvent:?function(element,?type,?handler)?{
          ????if?(element.addEventListener)?{
          ??????element.addEventListener(type,?handler,?false);
          ????}?else?if?(element.attachEvent)?{
          ??????element.attachEvent("on"?+?type,?handler);
          ????}?else?{
          ??????element["on"?+?type]?=?handler;
          ????}
          ??},
          ??//?移除事件
          ??removeEvent:?function(element,?type,?handler)?{
          ????if?(element.removeEventListener)?{
          ??????element.removeEventListener(type,?handler,?false);
          ????}?else?if?(element.detachEvent)?{
          ??????element.detachEvent("on"?+?type,?handler);
          ????}?else?{
          ??????element["on"?+?type]?=?null;
          ????}
          ??},
          ?//?獲取事件目標(biāo)
          ??getTarget:?function(event)?{
          ????return?event.target?||?event.srcElement;
          ??},
          ??//?獲取?event?對(duì)象的引用,取到事件的所有信息,確保隨時(shí)能使用?event
          ??getEvent:?function(event)?{
          ????return?event?||?window.event;
          ??},
          ?//?阻止事件(主要是事件冒泡,因?yàn)?IE?不支持事件捕獲)
          ??stopPropagation:?function(event)?{
          ????if?(event.stopPropagation)?{
          ??????event.stopPropagation();
          ????}?else?{
          ??????event.cancelBubble?=?true;
          ????}
          ??},
          ??//?取消事件的默認(rèn)行為
          ??preventDefault:?function(event)?{
          ????if?(event.preventDefault)?{
          ??????event.preventDefault();
          ????}?else?{
          ??????event.returnValue?=?false;
          ????}
          ??}
          };

          24.使用迭代的方式實(shí)現(xiàn) flatten 函數(shù)。

          var?arr?=?[1,?2,?3,?[4,?5],?[6,?[7,?[8]]]]
          /**?*?使用遞歸的方式處理?*?wrap?內(nèi)保
          存結(jié)果?ret?*?返回一個(gè)遞歸函數(shù)?**/

          function?wrap()?{
          ????var?ret?=?[];
          ????return?function?flat(a)?{
          ????????for?(var?item?of
          ????????????a)?{
          ????????????????if?(item.constructor?===?Array)?{
          ????????????????????ret.concat(flat(item))
          ????????????????}?else?{
          ????????????????????ret.push(item)
          ????????????????}
          ????????}
          ????????return?ret
          ????}
          }?
          console.log(wrap()(arr));

          25.怎么實(shí)現(xiàn)一個(gè)sleep

          • sleep函數(shù)作用是讓線程休眠,等到指定時(shí)間在重新喚起。
          function?sleep(delay)?{
          ??var?start?=?(new?Date()).getTime();
          ??while?((new?Date()).getTime()?-?start?????continue;
          ??}
          }

          function?test()?{
          ??console.log('111');
          ??sleep(2000);
          ??console.log('222');
          }

          test()

          26.實(shí)現(xiàn)正則切分千分位(10000 => 10,000)

          //無(wú)小數(shù)點(diǎn)
          let?num1?=?'1321434322222'
          num1.replace(/(\d)(?=(\d{3})+$)/g,'$1,')
          //有小數(shù)點(diǎn)
          let?num2?=?'342243242322.3432423'
          num2.replace(/(\d)(?=(\d{3})+\.)/g,'$1,')

          27.對(duì)象數(shù)組去重

          輸入:
          [{a:1,b:2,c:3},{b:2,c:3,a:1},{d:2,c:2}]
          輸出:
          [{a:1,b:2,c:3},{d:2,c:2}]
          • 首先寫一個(gè)函數(shù)把對(duì)象中的key排序,然后再轉(zhuǎn)成字符串
          • 遍歷數(shù)組利用Set將轉(zhuǎn)為字符串后的對(duì)象去重
          function?objSort(obj){
          ????let?newObj?=?{}
          ????//遍歷對(duì)象,并將key進(jìn)行排序
          ????Object.keys(obj).sort().map(key?=>?{
          ????????newObj[key]?=?obj[key]
          ????})
          ????//將排序好的數(shù)組轉(zhuǎn)成字符串
          ????return?JSON.stringify(newObj)
          }

          function?unique(arr){
          ????let?set?=?new?Set();
          ????for(let?i=0;i????????let?str?=?objSort(arr[i])
          ????????set.add(str)
          ????}
          ????//將數(shù)組中的字符串轉(zhuǎn)回對(duì)象
          ????arr?=?[...set].map(item?=>?{
          ????????return?JSON.parse(item)
          ????})
          ????return?arr
          }

          28.解析 URL Params 為對(duì)象

          let?url?=?'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
          parseParam(url)
          /*?結(jié)果
          {?user:?'anonymous',
          ??id:?[?123,?456?],?//?重復(fù)出現(xiàn)的?key?要組裝成數(shù)組,能被轉(zhuǎn)成數(shù)字的就轉(zhuǎn)成數(shù)字類型
          ??city:?'北京',?//?中文需解碼
          ??enabled:?true,?//?未指定值得?key?約定為?true
          }
          */

          function?parseParam(url)?{
          ??const?paramsStr?=?/.+\?(.+)$/.exec(url)[1];?//?將???后面的字符串取出來(lái)
          ??const?paramsArr?=?paramsStr.split('&');?//?將字符串以?&?分割后存到數(shù)組中
          ??let?paramsObj?=?{};
          ??//?將?params?存到對(duì)象中
          ??paramsArr.forEach(param?=>?{
          ????if?(/=/.test(param))?{?//?處理有?value?的參數(shù)
          ??????let?[key,?val]?=?param.split('=');?//?分割?key?和?value
          ??????val?=?decodeURIComponent(val);?//?解碼
          ??????val?=?/^\d+$/.test(val)???parseFloat(val)?:?val;?//?判斷是否轉(zhuǎn)為數(shù)字

          ??????if?(paramsObj.hasOwnProperty(key))?{?//?如果對(duì)象有?key,則添加一個(gè)值
          ????????paramsObj[key]?=?[].concat(paramsObj[key],?val);
          ??????}?else?{?//?如果對(duì)象沒有這個(gè)?key,創(chuàng)建?key?并設(shè)置值
          ????????paramsObj[key]?=?val;
          ??????}
          ????}?else?{?//?處理沒有?value?的參數(shù)
          ??????paramsObj[param]?=?true;
          ????}
          ??})

          ??return?paramsObj;
          }

          29.模板引擎實(shí)現(xiàn)

          let?template?=?'我是{{name}},年齡{{age}},性別{{sex}}';
          let?data?=?{
          ??name:?'姓名',
          ??age:?18
          }
          render(template,?data);?//?我是姓名,年齡18,性別undefined
          function?render(template,?data)?{
          ??const?reg?=?/\{\{(\w+)\}\}/;?//?模板字符串正則
          ??if?(reg.test(template))?{?//?判斷模板里是否有模板字符串
          ????const?name?=?reg.exec(template)[1];?//?查找當(dāng)前模板里第一個(gè)模板字符串的字段
          ????template?=?template.replace(reg,?data[name]);?//?將第一個(gè)模板字符串渲染
          ????return?render(template,?data);?//?遞歸的渲染并返回渲染后的結(jié)構(gòu)
          ??}
          ??return?template;?//?如果模板沒有模板字符串直接返回
          }

          30.轉(zhuǎn)化為駝峰命名

          var?s1?=?"get-element-by-id"
          //?轉(zhuǎn)化為?getElementById
          var?f?=?function(s)?{
          ????return?s.replace(/-\w/g,?function(x)?{
          ????????return?x.slice(1).toUpperCase();
          ????})
          }

          31.查找字符串中出現(xiàn)最多的字符和個(gè)數(shù)

          • 例: abbcccddddd -> 字符最多的是d,出現(xiàn)了5次
          let?str?=?"abcabcabcbbccccc";
          let?num?=?0;
          let?char?=?'';

          ?//?使其按照一定的次序排列
          str?=?str.split('').sort().join('');
          //?"aaabbbbbcccccccc"

          //?定義正則表達(dá)式
          let?re?=?/(\w)\1+/g;
          str.replace(re,($0,$1)?=>?{
          ????if(num?0.length){
          ????????num?=?$0.length;
          ????????char?=?$1;????????
          ????}
          });
          console.log(`字符最多的是${char},出現(xiàn)了${num}次`);

          32.圖片懶加載

          let?imgList?=?[...document.querySelectorAll('img')]
          let?length?=?imgList.length

          const?imgLazyLoad?=?function()?{
          ????let?count?=?0
          ????return?(function()?{
          ????????let?deleteIndexList?=?[]
          ????????imgList.forEach((img,?index)?=>?{
          ????????????let?rect?=?img.getBoundingClientRect()
          ????????????if?(rect.top?????????????????img.src?=?img.dataset.src
          ????????????????deleteIndexList.push(index)
          ????????????????count++
          ????????????????if?(count?===?length)?{
          ????????????????????document.removeEventListener('scroll',?imgLazyLoad)
          ????????????????}
          ????????????}
          ????????})
          ????????imgList?=?imgList.filter((img,?index)?=>?!deleteIndexList.includes(index))
          ????})()
          }

          //?這里最好加上防抖處理
          document.addEventListener('scroll',?imgLazyLoad)

          參考資料

          • 高頻 JavaScript 手寫
          • 初、中級(jí)前端應(yīng)該要掌握的手寫代碼實(shí)現(xiàn)
          • 22 道高頻 JavaScript 手寫
          • 死磕 36 個(gè) JS 手寫題(搞懂后,提升真的大)

          作者:xpsilvester

          https://juejin.cn/post/6963167124881670152

          The End

          歡迎自薦投稿到《前端技術(shù)江湖》,如果你覺得這篇內(nèi)容對(duì)你挺有啟發(fā),記得點(diǎn)個(gè)?「在看」


          點(diǎn)個(gè)『在看』支持下?


          瀏覽 34
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本黄色站视频 | 四虎av| 影音先锋在线三级 | 一级大片免费看 | 色老板在线永久免费视频 |