1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        手撕 32 個(gè)面試高頻知識(shí),輕松應(yīng)對(duì)編程題

        共 14222字,需瀏覽 29分鐘

         ·

        2020-10-06 08:08

        作者:洛霞(字節(jié)跳動(dòng))

        來(lái)源:https://juejin.im/post/6875152247714480136

        作為前端開(kāi)發(fā),JS是重中之重,最近結(jié)束了面試的高峰期,基本上offer也定下來(lái)了就等開(kāi)獎(jiǎng),趁著這個(gè)時(shí)間總結(jié)下32個(gè)手寫(xiě)JS問(wèn)題,這些都是高頻面試題,希望對(duì)你能有所幫助。

        關(guān)于源碼都緊遵規(guī)范,都可跑通MDN示例,其余的大多會(huì)涉及一些關(guān)于JS的應(yīng)用題和本人面試過(guò)程

        01.數(shù)組扁平化

        數(shù)組扁平化是指將一個(gè)多維數(shù)組變?yōu)橐粋€(gè)一維數(shù)組

        const?arr?=?[1,?[2,?[3,?[4,?5]]],?6];
        //?=>?[1,?2,?3,?4,?5,?6]

        方法一:使用flat()

        const?res1?=?arr.flat(Infinity);

        方法二:利用正則

        const?res2?=?JSON.stringify(arr).replace(/\[|\]/g,?'').split(',');

        但數(shù)據(jù)類型都會(huì)變?yōu)樽址?/p>

        方法三:正則改良版本

        const?res3?=?JSON.parse('['?+?JSON.stringify(arr).replace(/\[|\]/g,?'')?+?']');

        方法四:使用reduce

        const?flatten?=?arr?=>?{
        ??return?arr.reduce((pre,?cur)?=>?{
        ????return?pre.concat(Array.isArray(cur)???flatten(cur)?:?cur);
        ??},?[])
        }
        const?res4?=?flatten(arr);

        方法五:函數(shù)遞歸

        const?res5?=?[];
        const?fn?=?arr?=>?{
        ??for?(let?i?=?0;?i?????if?(Array.isArray(arr[i]))?{
        ??????fn(arr[i]);
        ????}?else?{
        ??????res5.push(arr[i]);
        ????}
        ??}
        }
        fn(arr);

        02.數(shù)組去重

        const?arr?=?[1,?1,?'1',?17,?true,?true,?false,?false,?'true',?'a',?{},?{}];
        //?=>?[1,?'1',?17,?true,?false,?'true',?'a',?{},?{}]

        方法一:利用Set

        const?res1?=?Array.from(new?Set(arr));

        方法二:兩層for循環(huán)+splice

        const?unique1?=?arr?=>?{
        ??let?len?=?arr.length;
        ??for?(let?i?=?0;?i?????for?(let?j?=?i?+?1;?j???????if?(arr[i]?===?arr[j])?{
        ????????arr.splice(j,?1);
        ????????//?每刪除一個(gè)樹(shù),j--保證j的值經(jīng)過(guò)自加后不變。同時(shí),len--,減少循環(huán)次數(shù)提升性能
        ????????len--;
        ????????j--;
        ??????}
        ????}
        ??}
        ??return?arr;
        }

        方法三:利用indexOf

        const?unique2?=?arr?=>?{
        ??const?res?=?[];
        ??for?(let?i?=?0;?i?????if?(res.indexOf(arr[i])?===?-1)?res.push(arr[i]);
        ??}
        ??return?res;
        }

        當(dāng)然也可以用include、filter,思路大同小異。

        方法四:利用include

        const?unique3?=?arr?=>?{
        ??const?res?=?[];
        ??for?(let?i?=?0;?i?????if?(!res.includes(arr[i]))?res.push(arr[i]);
        ??}
        ??return?res;
        }

        方法五:利用filter

        const?unique4?=?arr?=>?{
        ??return?arr.filter((item,?index)?=>?{
        ????return?arr.indexOf(item)?===?index;
        ??});
        }

        方法六:利用Map

        const?unique5?=?arr?=>?{
        ??const?map?=?new?Map();
        ??const?res?=?[];
        ??for?(let?i?=?0;?i?????if?(!map.has(arr[i]))?{
        ??????map.set(arr[i],?true)
        ??????res.push(arr[i]);
        ????}
        ??}
        ??return?res;
        }

        03.類數(shù)組轉(zhuǎn)化為數(shù)組

        類數(shù)組是具有length屬性,但不具有數(shù)組原型上的方法。常見(jiàn)的類數(shù)組有arguments、DOM操作方法返回的結(jié)果。

        方法一:Array.from

        Array.from(document.querySelectorAll('div'))

        方法二:Array.prototype.slice.call()

        Array.prototype.slice.call(document.querySelectorAll('div'))

        方法三:擴(kuò)展運(yùn)算符

        [...document.querySelectorAll('div')]

        方法四:利用concat

        Array.prototype.concat.apply([],?document.querySelectorAll('div'));

        04.Array.prototype.filter()

        Array.prototype.filter?=?function(callback,?thisArg)?{
        ??if?(this?==?undefined)?{
        ????throw?new?TypeError('this?is?null?or?not?undefined');
        ??}
        ??if?(typeof?callback?!==?'function')?{
        ????throw?new?TypeError(callback?+?'is?not?a?function');
        ??}
        ??const?res?=?[];
        ??//?讓O成為回調(diào)函數(shù)的對(duì)象傳遞(強(qiáng)制轉(zhuǎn)換對(duì)象)
        ??const?O?=?Object(this);
        ??//?>>>0?保證len為number,且為正整數(shù)
        ??const?len?=?O.length?>>>?0;
        ??for?(let?i?=?0;?i?????//?檢查i是否在O的屬性(會(huì)檢查原型鏈)
        ????if?(i?in?O)?{
        ??????//?回調(diào)函數(shù)調(diào)用傳參
        ??????if?(callback.call(thisArg,?O[i],?i,?O))?{
        ????????res.push(O[i]);
        ??????}
        ????}
        ??}
        ??return?res;
        }

        對(duì)于>>>0有疑問(wèn)的:解釋>>>0的作用

        05.Array.prototype.map()

        Array.prototype.map?=?function(callback,?thisArg)?{
        ??if?(this?==?undefined)?{
        ????throw?new?TypeError('this?is?null?or?not?defined');
        ??}
        ??if?(typeof?callback?!==?'function')?{
        ????throw?new?TypeError(callback?+?'?is?not?a?function');
        ??}
        ??const?res?=?[];
        ??//?同理
        ??const?O?=?Object(this);
        ??const?len?=?O.length?>>>?0;
        ??for?(let?i?=?0;?i?????if?(i?in?O)?{
        ??????//?調(diào)用回調(diào)函數(shù)并傳入新數(shù)組
        ??????res[i]?=?callback.call(thisArg,?O[i],?i,?this);
        ????}
        ??}
        ??return?res;
        }

        06.Array.prototype.forEach()

        forEach跟map類似,唯一不同的是forEach是沒(méi)有返回值的。

        Array.prototype.forEach?=?function(callback,?thisArg)?{
        ??if?(this?==?null)?{
        ????throw?new?TypeError('this?is?null?or?not?defined');
        ??}
        ??if?(typeof?callback?!==?"function")?{
        ????throw?new?TypeError(callback?+?'?is?not?a?function');
        ??}
        ??const?O?=?Object(this);
        ??const?len?=?O.length?>>>?0;
        ??let?k?=?0;
        ??while?(k?????if?(k?in?O)?{
        ??????callback.call(thisArg,?O[k],?k,?O);
        ????}
        ????k++;
        ??}
        }

        07.Array.prototype.reduce()

        Array.prototype.reduce?=?function(callback,?initialValue)?{
        ??if?(this?==?undefined)?{
        ????throw?new?TypeError('this?is?null?or?not?defined');
        ??}
        ??if?(typeof?callback?!==?'function')?{
        ????throw?new?TypeError(callbackfn?+?'?is?not?a?function');
        ??}
        ??const?O?=?Object(this);
        ??const?len?=?this.length?>>>?0;
        ??let?accumulator?=?initialValue;
        ??let?k?=?0;
        ??//?如果第二個(gè)參數(shù)為undefined的情況下
        ??//?則數(shù)組的第一個(gè)有效值作為累加器的初始值
        ??if?(accumulator?===?undefined)?{
        ????while?(k?in?O))?{
        ??????k++;
        ????}
        ????//?如果超出數(shù)組界限還沒(méi)有找到累加器的初始值,則TypeError
        ????if?(k?>=?len)?{
        ??????throw?new?TypeError('Reduce?of?empty?array?with?no?initial?value');
        ????}
        ????accumulator?=?O[k++];
        ??}
        ??while?(k?????if?(k?in?O)?{
        ??????accumulator?=?callback.call(undefined,?accumulator,?O[k],?k,?O);
        ????}
        ????k++;
        ??}
        ??return?accumulator;
        }

        08.Function.prototype.apply()

        第一個(gè)參數(shù)是綁定的this,默認(rèn)為window,第二個(gè)參數(shù)是數(shù)組或類數(shù)組

        Function.prototype.apply?=?function(context?=?window,?args)?{
        ??if?(typeof?this?!==?'function')?{
        ????throw?new?TypeError('Type?Error');
        ??}
        ??const?fn?=?Symbol('fn');
        ??context[fn]?=?this;

        ??const?res?=?context[fn](...args);
        ??delete?context[fn];
        ??return?res;
        }

        09.Function.prototype.call

        call唯一不同的是,call()方法接受的是一個(gè)參數(shù)列表

        Function.prototype.call?=?function(context?=?window,?...args)?{
        ??if?(typeof?this?!==?'function')?{
        ????throw?new?TypeError('Type?Error');
        ??}
        ??const?fn?=?Symbol('fn');
        ??context[fn]?=?this;

        ??const?res?=?context[fn](...args);
        ??delete?context[fn];
        ??return?res;
        }

        10.Function.prototype.bind

        Function.prototype.bind?=?function(context,?...args)?{
        ??if?(typeof?this?!==?'function')?{
        ????throw?new?Error("Type?Error");
        ??}
        ??//?保存this的值
        ??var?self?=?this;

        ??return?function?F()?{
        ????//?考慮new的情況
        ????if(this?instanceof?F)?{
        ??????return?new?self(...args,?...arguments)
        ????}
        ????return?self.apply(context,?[...args,?...arguments])
        ??}
        }

        11.debounce(防抖)

        觸發(fā)高頻時(shí)間后n秒內(nèi)函數(shù)只會(huì)執(zhí)行一次,如果n秒內(nèi)高頻時(shí)間再次觸發(fā),則重新計(jì)算時(shí)間。

        const?debounce?=?(fn,?time)?=>?{
        ??let?timeout?=?null;
        ??return?function()?{
        ????clearTimeout(timeout)
        ????timeout?=?setTimeout(()?=>?{
        ??????fn.apply(this,?arguments);
        ????},?time);
        ??}
        };

        防抖常應(yīng)用于用戶進(jìn)行搜索輸入節(jié)約請(qǐng)求資源,window觸發(fā)resize事件時(shí)進(jìn)行防抖只觸發(fā)一次。

        12.throttle(節(jié)流)

        高頻時(shí)間觸發(fā),但n秒內(nèi)只會(huì)執(zhí)行一次,所以節(jié)流會(huì)稀釋函數(shù)的執(zhí)行頻率。

        const?throttle?=?(fn,?time)?=>?{
        ??let?flag?=?true;
        ??return?function()?{
        ????if?(!flag)?return;
        ????flag?=?false;
        ????setTimeout(()?=>?{
        ??????fn.apply(this,?arguments);
        ??????flag?=?true;
        ????},?time);
        ??}
        }

        節(jié)流常應(yīng)用于鼠標(biāo)不斷點(diǎn)擊觸發(fā)、監(jiān)聽(tīng)滾動(dòng)事件。

        13.函數(shù)珂里化

        指的是將一個(gè)接受多個(gè)參數(shù)的函數(shù) 變?yōu)?接受一個(gè)參數(shù)返回一個(gè)函數(shù)的固定形式,這樣便于再次調(diào)用,例如f(1)(2)

        經(jīng)典面試題:實(shí)現(xiàn)add(1)(2)(3)(4)=10; 、 add(1)(1,2,3)(2)=9;

        function?add()?{
        ??const?_args?=?[...arguments];
        ??function?fn()?{
        ????_args.push(...arguments);
        ????return?fn;
        ??}
        ??fn.toString?=?function()?{
        ????return?_args.reduce((sum,?cur)?=>?sum?+?cur);
        ??}
        ??return?fn;
        }

        14.模擬new操作

        3個(gè)步驟:

        1. 以`ctor.prototype`為原型創(chuàng)建一個(gè)對(duì)象。
        2. 執(zhí)行構(gòu)造函數(shù)并將this綁定到新創(chuàng)建的對(duì)象上。
        3. 判斷構(gòu)造函數(shù)執(zhí)行返回的結(jié)果是否是引用數(shù)據(jù)類型,若是則返回構(gòu)造函數(shù)執(zhí)行的結(jié)果,否則返回創(chuàng)建的對(duì)象。
        function?newOperator(ctor,?...args)?{
        ??if?(typeof?ctor?!==?'function')?{
        ????throw?new?TypeError('Type?Error');
        ??}
        ??const?obj?=?Object.create(ctor.prototype);
        ??const?res?=?ctor.apply(obj,?args);

        ??const?isObject?=?typeof?res?===?'object'?&&?res?!==?null;
        ??const?isFunction?=?typeof?res?===?'function';
        ??return?isObject?||?isFunction???res?:?obj;
        }

        15.instanceof

        instanceof運(yùn)算符用于檢測(cè)構(gòu)造函數(shù)的prototype屬性是否出現(xiàn)在某個(gè)實(shí)例對(duì)象的原型鏈上。

        const?myInstanceof?=?(left,?right)?=>?{
        ??//?基本數(shù)據(jù)類型都返回false
        ??if?(typeof?left?!==?'object'?||?left?===?null)?return?false;
        ??let?proto?=?Object.getPrototypeOf(left);
        ??while?(true)?{
        ????if?(proto?===?null)?return?false;
        ????if?(proto?===?right.prototype)?return?true;
        ????proto?=?Object.getPrototypeOf(proto);
        ??}
        }

        16.原型繼承

        這里只寫(xiě)寄生組合繼承了,中間還有幾個(gè)演變過(guò)來(lái)的繼承但都有一些缺陷

        function?Parent()?{
        ??this.name?=?'parent';
        }
        function?Child()?{
        ??Parent.call(this);
        ??this.type?=?'children';
        }
        Child.prototype?=?Object.create(Parent.prototype);
        Child.prototype.constructor?=?Child;

        17.Object.is

        Object.is解決的主要是這兩個(gè)問(wèn)題:

        +0?===?-0??//?true
        NaN?===?NaN?//?false

        const?is=?(x,?y)?=>?{
        ??if?(x?===?y)?{
        ????//?+0和-0應(yīng)該不相等
        ????return?x?!==?0?||?y?!==?0?||?1/x?===?1/y;
        ??}?else?{
        ????return?x?!==?x?&&?y?!==?y;
        ??}
        }

        18.Object.assign

        Object.assign()方法用于將所有可枚舉屬性的值從一個(gè)或多個(gè)源對(duì)象復(fù)制到目標(biāo)對(duì)象。它將返回目標(biāo)對(duì)象(請(qǐng)注意這個(gè)操作是淺拷貝)

        Object.defineProperty(Object,?'assign',?{
        ??value:?function(target,?...args)?{
        ????if?(target?==?null)?{
        ??????return?new?TypeError('Cannot?convert?undefined?or?null?to?object');
        ????}
        ????
        ????//?目標(biāo)對(duì)象需要統(tǒng)一是引用數(shù)據(jù)類型,若不是會(huì)自動(dòng)轉(zhuǎn)換
        ????const?to?=?Object(target);

        ????for?(let?i?=?0;?i???????//?每一個(gè)源對(duì)象
        ??????const?nextSource?=?args[i];
        ??????if?(nextSource?!==?null)?{
        ????????//?使用for...in和hasOwnProperty雙重判斷,確保只拿到本身的屬性、方法(不包含繼承的)
        ????????for?(const?nextKey?in?nextSource)?{
        ??????????if?(Object.prototype.hasOwnProperty.call(nextSource,?nextKey))?{
        ????????????to[nextKey]?=?nextSource[nextKey];
        ??????????}
        ????????}
        ??????}
        ????}
        ????return?to;
        ??},
        ??//?不可枚舉
        ??enumerable:?false,
        ??writable:?true,
        ??configurable:?true,
        })

        19.深拷貝

        遞歸的完整版本(考慮到了Symbol屬性):

        const?cloneDeep1?=?(target,?hash?=?new?WeakMap())?=>?{
        ??//?對(duì)于傳入?yún)?shù)處理
        ??if?(typeof?target?!==?'object'?||?target?===?null)?{
        ????return?target;
        ??}
        ??//?哈希表中存在直接返回
        ??if?(hash.has(target))?return?hash.get(target);

        ??const?cloneTarget?=?Array.isArray(target)???[]?:?{};
        ??hash.set(target,?cloneTarget);

        ??//?針對(duì)Symbol屬性
        ??const?symKeys?=?Object.getOwnPropertySymbols(target);
        ??if?(symKeys.length)?{
        ????symKeys.forEach(symKey?=>?{
        ??????if?(typeof?target[symKey]?===?'object'?&&?target[symKey]?!==?null)?{
        ????????cloneTarget[symKey]?=?cloneDeep1(target[symKey]);
        ??????}?else?{
        ????????cloneTarget[symKey]?=?target[symKey];
        ??????}
        ????})
        ??}

        ??for?(const?i?in?target)?{
        ????if?(Object.prototype.hasOwnProperty.call(target,?i))?{
        ??????cloneTarget[i]?=
        ????????typeof?target[i]?===?'object'?&&?target[i]?!==?null
        ??????????cloneDeep1(target[i],?hash)
        ????????:?target[i];
        ????}
        ??}
        ??return?cloneTarget;
        }

        20.Promise

        實(shí)現(xiàn)思路:Promise源碼實(shí)現(xiàn)

        const?PENDING?=?'PENDING';??????//?進(jìn)行中
        const?FULFILLED?=?'FULFILLED';??//?已成功
        const?REJECTED?=?'REJECTED';????//?已失敗

        class?Promise?{
        ??constructor(exector)?{
        ????//?初始化狀態(tài)
        ????this.status?=?PENDING;
        ????//?將成功、失敗結(jié)果放在this上,便于then、catch訪問(wèn)
        ????this.value?=?undefined;
        ????this.reason?=?undefined;
        ????//?成功態(tài)回調(diào)函數(shù)隊(duì)列
        ????this.onFulfilledCallbacks?=?[];
        ????//?失敗態(tài)回調(diào)函數(shù)隊(duì)列
        ????this.onRejectedCallbacks?=?[];

        ????const?resolve?=?value?=>?{
        ??????//?只有進(jìn)行中狀態(tài)才能更改狀態(tài)
        ??????if?(this.status?===?PENDING)?{
        ????????this.status?=?FULFILLED;
        ????????this.value?=?value;
        ????????//?成功態(tài)函數(shù)依次執(zhí)行
        ????????this.onFulfilledCallbacks.forEach(fn?=>?fn(this.value));
        ??????}
        ????}
        ????const?reject?=?reason?=>?{
        ??????//?只有進(jìn)行中狀態(tài)才能更改狀態(tài)
        ??????if?(this.status?===?PENDING)?{
        ????????this.status?=?REJECTED;
        ????????this.reason?=?reason;
        ????????//?失敗態(tài)函數(shù)依次執(zhí)行
        ????????this.onRejectedCallbacks.forEach(fn?=>?fn(this.reason))
        ??????}
        ????}
        ????try?{
        ??????//?立即執(zhí)行executor
        ??????//?把內(nèi)部的resolve和reject傳入executor,用戶可調(diào)用resolve和reject
        ??????exector(resolve,?reject);
        ????}?catch(e)?{
        ??????//?executor執(zhí)行出錯(cuò),將錯(cuò)誤內(nèi)容reject拋出去
        ??????reject(e);
        ????}
        ??}
        ??then(onFulfilled,?onRejected)?{
        ????onFulfilled?=?typeof?onFulfilled?===?'function'???onFulfilled?:?value?=>?value;
        ????onRejected?=?typeof?onRejected?===?'function'??onRejected:
        ??????reason?=>?{?throw?new?Error(reason?instanceof?Error???reason.message:reason)?}
        ????//?保存this
        ????const?self?=?this;
        ????return?new?Promise((resolve,?reject)?=>?{
        ??????if?(self.status?===?PENDING)?{
        ????????self.onFulfilledCallbacks.push(()?=>?{
        ??????????//?try捕獲錯(cuò)誤
        ??????????try?{
        ????????????//?模擬微任務(wù)
        ????????????setTimeout(()?=>?{
        ??????????????const?result?=?onFulfilled(self.value);
        ??????????????//?分兩種情況:
        ??????????????//?1.?回調(diào)函數(shù)返回值是Promise,執(zhí)行then操作
        ??????????????//?2.?如果不是Promise,調(diào)用新Promise的resolve函數(shù)
        ??????????????result?instanceof?Promise???result.then(resolve,?reject)?:?resolve(result);
        ????????????})
        ??????????}?catch(e)?{
        ????????????reject(e);
        ??????????}
        ????????});
        ????????self.onRejectedCallbacks.push(()?=>?{
        ??????????//?以下同理
        ??????????try?{
        ????????????setTimeout(()?=>?{
        ??????????????const?result?=?onRejected(self.reason);
        ??????????????//?不同點(diǎn):此時(shí)是reject
        ??????????????result?instanceof?Promise???result.then(resolve,?reject)?:?reject(result);
        ????????????})
        ??????????}?catch(e)?{
        ????????????reject(e);
        ??????????}
        ????????})
        ??????}?else?if?(self.status?===?FULFILLED)?{
        ????????try?{
        ??????????setTimeout(()?=>?{
        ????????????const?result?=?onFulfilled(self.value);
        ????????????result?instanceof?Promise???result.then(resolve,?reject)?:?resolve(result);
        ??????????});
        ????????}?catch(e)?{
        ??????????reject(e);
        ????????}
        ??????}?else?if?(self.status?===?REJECTED){
        ????????try?{
        ??????????setTimeout(()?=>?{
        ????????????const?result?=?onRejected(self.reason);
        ????????????result?instanceof?Promise???result.then(resolve,?reject)?:?reject(result);
        ??????????})
        ????????}?catch(e)?{
        ??????????reject(e);
        ????????}
        ??????}
        ????});
        ??}
        ??catch(onRejected)?{
        ????return?this.then(null,?onRejected);
        ??}
        ??static?resolve(value)?{
        ????if?(value?instanceof?Promise)?{
        ??????//?如果是Promise實(shí)例,直接返回
        ??????return?value;
        ????}?else?{
        ??????//?如果不是Promise實(shí)例,返回一個(gè)新的Promise對(duì)象,狀態(tài)為FULFILLED
        ??????return?new?Promise((resolve,?reject)?=>?resolve(value));
        ????}
        ??}
        ??static?reject(reason)?{
        ????return?new?Promise((resolve,?reject)?=>?{
        ??????reject(reason);
        ????})
        ??}
        }

        21.Promise.all

        Promise.all是支持鏈?zhǔn)秸{(diào)用的,本質(zhì)上就是返回了一個(gè)Promise實(shí)例,通過(guò)resolvereject來(lái)改變實(shí)例狀態(tài)。

        Promise.myAll?=?function(promiseArr)?{
        ??return?new?Promise((resolve,?reject)?=>?{
        ????const?ans?=?[];
        ????let?index?=?0;
        ????for?(let?i?=?0;?i???????promiseArr[i]
        ??????.then(res?=>?{
        ????????ans[i]?=?res;
        ????????index++;
        ????????if?(index?===?promiseArr.length)?{
        ??????????resolve(ans);
        ????????}
        ??????})
        ??????.catch(err?=>?reject(err));
        ????}
        ??})
        }

        22.Promise.race

        Promise.race?=?function(promiseArr)?{
        ??return?new?Promise((resolve,?reject)?=>?{
        ????promiseArr.forEach(p?=>?{
        ??????//?如果不是Promise實(shí)例需要轉(zhuǎn)化為Promise實(shí)例
        ??????Promise.resolve(p).then(
        ????????val?=>?resolve(val),
        ????????err?=>?reject(err),
        ??????)
        ????})
        ??})
        }

        23.Promise并行限制

        就是實(shí)現(xiàn)有并行限制的Promise調(diào)度器問(wèn)題。

        詳細(xì)實(shí)現(xiàn)思路:某條高頻面試原題:實(shí)現(xiàn)有并行限制的Promise調(diào)度器

        class?Scheduler?{
        ??constructor()?{
        ????this.queue?=?[];
        ????this.maxCount?=?2;
        ????this.runCounts?=?0;
        ??}
        ??add(promiseCreator)?{
        ????this.queue.push(promiseCreator);
        ??}
        ??taskStart()?{
        ????for?(let?i?=?0;?i?this.maxCount;?i++)?{
        ??????this.request();
        ????}
        ??}
        ??request()?{
        ????if?(!this.queue?||?!this.queue.length?||?this.runCounts?>=?this.maxCount)?{
        ??????return;
        ????}
        ????this.runCounts++;

        ????this.queue.shift()().then(()?=>?{
        ??????this.runCounts--;
        ??????this.request();
        ????});
        ??}
        }
        ???
        const?timeout?=?time?=>?new?Promise(resolve?=>?{
        ??setTimeout(resolve,?time);
        })
        ??
        const?scheduler?=?new?Scheduler();
        ??
        const?addTask?=?(time,order)?=>?{
        ??scheduler.add(()?=>?timeout(time).then(()=>console.log(order)))
        }
        ??
        ??
        addTask(1000,?'1');
        addTask(500,?'2');
        addTask(300,?'3');
        addTask(400,?'4');
        scheduler.taskStart()
        //?2
        //?3
        //?1
        //?4

        24.JSONP

        script標(biāo)簽不遵循同源協(xié)議,可以用來(lái)進(jìn)行跨域請(qǐng)求,優(yōu)點(diǎn)就是兼容性好但僅限于GET請(qǐng)求

        const?jsonp?=?({?url,?params,?callbackName?})?=>?{
        ??const?generateUrl?=?()?=>?{
        ????let?dataSrc?=?'';
        ????for?(let?key?in?params)?{
        ??????if?(Object.prototype.hasOwnProperty.call(params,?key))?{
        ????????dataSrc?+=?`${key}=${params[key]}&`;
        ??????}
        ????}
        ????dataSrc?+=?`callback=${callbackName}`;
        ????return?`${url}?${dataSrc}`;
        ??}
        ??return?new?Promise((resolve,?reject)?=>?{
        ????const?scriptEle?=?document.createElement('script');
        ????scriptEle.src?=?generateUrl();
        ????document.body.appendChild(scriptEle);
        ????window[callbackName]?=?data?=>?{
        ??????resolve(data);
        ??????document.removeChild(scriptEle);
        ????}
        ??})
        }

        25.AJAX

        const?getJSON?=?function(url)?{
        ??return?new?Promise((resolve,?reject)?=>?{
        ????const?xhr?=?XMLHttpRequest???new?XMLHttpRequest()?:?new?ActiveXObject('Mscrosoft.XMLHttp');
        ????xhr.open('GET',?url,?false);
        ????xhr.setRequestHeader('Accept',?'application/json');
        ????xhr.onreadystatechange?=?function()?{
        ??????if?(xhr.readyState?!==?4)?return;
        ??????if?(xhr.status?===?200?||?xhr.status?===?304)?{
        ????????resolve(xhr.responseText);
        ??????}?else?{
        ????????reject(new?Error(xhr.responseText));
        ??????}
        ????}
        ????xhr.send();
        ??})
        }

        26.event模塊

        實(shí)現(xiàn)node中回調(diào)函數(shù)的機(jī)制,node中回調(diào)函數(shù)其實(shí)是內(nèi)部使用了觀察者模式。

        觀察者模式:定義了對(duì)象間一種一對(duì)多的依賴關(guān)系,當(dāng)目標(biāo)對(duì)象Subject發(fā)生改變時(shí),所有依賴它的對(duì)象Observer都會(huì)得到通知。

        function?EventEmitter()?{
        ??this.events?=?new?Map();
        }

        //?需要實(shí)現(xiàn)的一些方法:
        //?addListener、removeListener、once、removeAllListeners、emit

        //?模擬實(shí)現(xiàn)addlistener方法
        const?wrapCallback?=?(fn,?once?=?false)?=>?({?callback:?fn,?once?});
        EventEmitter.prototype.addListener?=?function(type,?fn,?once?=?false)?{
        ??const?hanlder?=?this.events.get(type);
        ??if?(!hanlder)?{
        ????//?沒(méi)有type綁定事件
        ????this.events.set(type,?wrapCallback(fn,?once));
        ??}?else?if?(hanlder?&&?typeof?hanlder.callback?===?'function')?{
        ????//?目前type事件只有一個(gè)回調(diào)
        ????this.events.set(type,?[hanlder,?wrapCallback(fn,?once)]);
        ??}?else?{
        ????//?目前type事件數(shù)>=2
        ????hanlder.push(wrapCallback(fn,?once));
        ??}
        }
        //?模擬實(shí)現(xiàn)removeListener
        EventEmitter.prototype.removeListener?=?function(type,?listener)?{
        ??const?hanlder?=?this.events.get(type);
        ??if?(!hanlder)?return;
        ??if?(!Array.isArray(this.events))?{
        ????if?(hanlder.callback?===?listener.callback)?this.events.delete(type);
        ????else?return;
        ??}
        ??for?(let?i?=?0;?i?????const?item?=?hanlder[i];
        ????if?(item.callback?===?listener.callback)?{
        ??????hanlder.splice(i,?1);
        ??????i--;
        ??????if?(hanlder.length?===?1)?{
        ????????this.events.set(type,?hanlder[0]);
        ??????}
        ????}
        ??}
        }
        //?模擬實(shí)現(xiàn)once方法
        EventEmitter.prototype.once?=?function(type,?listener)?{
        ??this.addListener(type,?listener,?true);
        }
        //?模擬實(shí)現(xiàn)emit方法
        EventEmitter.prototype.emit?=?function(type,?...args)?{
        ??const?hanlder?=?this.events.get(type);
        ??if?(!hanlder)?return;
        ??if?(Array.isArray(hanlder))?{
        ????hanlder.forEach(item?=>?{
        ??????item.callback.apply(this,?args);
        ??????if?(item.once)?{
        ????????this.removeListener(type,?item);
        ??????}
        ????})
        ??}?else?{
        ????hanlder.callback.apply(this,?args);
        ????if?(hanlder.once)?{
        ??????this.events.delete(type);
        ????}
        ??}
        ??return?true;
        }
        EventEmitter.prototype.removeAllListeners?=?function(type)?{
        ??const?hanlder?=?this.events.get(type);
        ??if?(!hanlder)?return;
        ??this.events.delete(type);
        }

        27.圖片懶加載

        可以給img標(biāo)簽統(tǒng)一自定義屬性src='default.png',當(dāng)檢測(cè)到圖片出現(xiàn)在窗口之后再補(bǔ)充src屬性,此時(shí)才會(huì)進(jìn)行圖片資源加載。

        function?lazyload()?{
        ??const?imgs?=?document.getElementsByTagName('img');
        ??const?len?=?imgs.length;
        ??//?視口的高度
        ??const?viewHeight?=?document.documentElement.clientHeight;
        ??//?滾動(dòng)條高度
        ??const?scrollHeight?=?document.documentElement.scrollTop?||?document.body.scrollTop;
        ??for?(let?i?=?0;?i?????const?offsetHeight?=?imgs[i].offsetTop;
        ????if?(offsetHeight???????const?src?=?imgs[i].dataset.src;
        ??????imgs[i].src?=?src;
        ????}
        ??}
        }

        //?可以使用節(jié)流優(yōu)化一下
        window.addEventListener('scroll',?lazyload);

        28.滾動(dòng)加載

        原理就是監(jiān)聽(tīng)頁(yè)面滾動(dòng)事件,分析clientHeight、scrollTop、scrollHeight三者的屬性關(guān)系。

        window.addEventListener('scroll',?function()?{
        ??const?clientHeight?=?document.documentElement.clientHeight;
        ??const?scrollTop?=?document.documentElement.scrollTop;
        ??const?scrollHeight?=?document.documentElement.scrollHeight;
        ??if?(clientHeight?+?scrollTop?>=?scrollHeight)?{
        ????//?檢測(cè)到滾動(dòng)至頁(yè)面底部,進(jìn)行后續(xù)操作
        ????//?...
        ??}
        },?false);

        一個(gè)Demo:頁(yè)面滾動(dòng)加載的Demo

        29.渲染幾萬(wàn)條數(shù)據(jù)不卡住頁(yè)面

        渲染大數(shù)據(jù)時(shí),合理使用createDocumentFragmentrequestAnimationFrame,將操作切分為一小段一小段執(zhí)行。

        setTimeout(()?=>?{
        ??//?插入十萬(wàn)條數(shù)據(jù)
        ??const?total?=?100000;
        ??//?一次插入的數(shù)據(jù)
        ??const?once?=?20;
        ??//?插入數(shù)據(jù)需要的次數(shù)
        ??const?loopCount?=?Math.ceil(total?/?once);
        ??let?countOfRender?=?0;
        ??const?ul?=?document.querySelector('ul');
        ??//?添加數(shù)據(jù)的方法
        ??function?add()?{
        ????const?fragment?=?document.createDocumentFragment();
        ????for(let?i?=?0;?i???????const?li?=?document.createElement('li');
        ??????li.innerText?=?Math.floor(Math.random()?*?total);
        ??????fragment.appendChild(li);
        ????}
        ????ul.appendChild(fragment);
        ????countOfRender?+=?1;
        ????loop();
        ??}
        ??function?loop()?{
        ????if(countOfRender???????window.requestAnimationFrame(add);
        ????}
        ??}
        ??loop();
        },?0)

        30.打印出當(dāng)前網(wǎng)頁(yè)使用了多少種HTML元素

        一行代碼可以解決:

        const?fn?=?()?=>?{
        ??return?[...new?Set([...document.querySelectorAll('*')].map(el?=>?el.tagName))].length;
        }

        值得注意的是:DOM操作返回的是類數(shù)組,需要轉(zhuǎn)換為數(shù)組之后才可以調(diào)用數(shù)組的方法。

        31.將VirtualDom轉(zhuǎn)化為真實(shí)DOM結(jié)構(gòu)

        這是當(dāng)前SPA應(yīng)用的核心概念之一

        //?vnode結(jié)構(gòu):
        //?{
        //???tag,
        //???attrs,
        //???children,
        //?}

        //Virtual?DOM?=>?DOM
        function?render(vnode,?container)?{
        ??container.appendChild(_render(vnode));
        }
        function?_render(vnode)?{
        ??//?如果是數(shù)字類型轉(zhuǎn)化為字符串
        ??if?(typeof?vnode?===?'number')?{
        ????vnode?=?String(vnode);
        ??}
        ??//?字符串類型直接就是文本節(jié)點(diǎn)
        ??if?(typeof?vnode?===?'string')?{
        ????return?document.createTextNode(vnode);
        ??}
        ??//?普通DOM
        ??const?dom?=?document.createElement(vnode.tag);
        ??if?(vnode.attrs)?{
        ????//?遍歷屬性
        ????Object.keys(vnode.attrs).forEach(key?=>?{
        ??????const?value?=?vnode.attrs[key];
        ??????dom.setAttribute(key,?value);
        ????})
        ??}
        ??//?子數(shù)組進(jìn)行遞歸操作
        ??vnode.children.forEach(child?=>?render(child,?dom));
        ??return?dom;
        }

        32.字符串解析問(wèn)題

        var?a?=?{
        ????b:?123,
        ????c:?'456',
        ????e:?'789',
        }
        var?str=`a{a.b}aa{a.c}aa?{a.d}aaaa`;
        //?=>?'a123aa456aa?{a.d}aaaa'

        實(shí)現(xiàn)函數(shù)使得將str字符串中的{}內(nèi)的變量替換,如果屬性不存在保持原樣(比如{a.d}

        類似于模版字符串,但有一點(diǎn)出入,實(shí)際上原理大差不差

        const?fn1?=?(str,?obj)?=>?{
        ????let?res?=?'';
        ????//?標(biāo)志位,標(biāo)志前面是否有{
        ????let?flag?=?false;
        ????let?start;
        ????for?(let?i?=?0;?i?????????if?(str[i]?===?'{')?{
        ????????????flag?=?true;
        ????????????start?=?i?+?1;
        ????????????continue;
        ????????}
        ????????if?(!flag)?res?+=?str[i];
        ????????else?{
        ????????????if?(str[i]?===?'}')?{
        ????????????????flag?=?false;
        ????????????????res?+=?match(str.slice(start,?i),?obj);
        ????????????}
        ????????}
        ????}
        ????return?res;
        }
        //?對(duì)象匹配操作
        const?match?=?(str,?obj)?=>?{
        ????const?keys?=?str.split('.').slice(1);
        ????let?index?=?0;
        ????let?o?=?obj;
        ????while?(index?????????const?key?=?keys[index];
        ????????if?(!o[key])?{
        ????????????return?`{${str}}`;
        ????????}?else?{
        ????????????o?=?o[key];
        ????????}
        ????????index++;
        ????}
        ????return?o;
        }




        》》面試官都在用的題庫(kù),快來(lái)看看《

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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            俄罗斯freexxxx性 | 亚洲.欧美.丝袜.中文.综合 | 日韩七区| 乳色吐息观看 | 亚洲天堂手机在线 | 国产精品免费精品 | 成人无码视频免费看 | 欧美一区二区三区二区 | 国产免费黄色片 | 72式性无遮挡免费观看网址 |