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>

        前端面試js高頻手寫大全

        共 13274字,需瀏覽 27分鐘

         ·

        2021-01-12 02:13

        作者:sprina
        來源:SegmentFault 思否社區(qū)



        本文涵蓋了前端面試??嫉母鞣N重點手寫。


        建議優(yōu)先掌握:


        • instanceof (考察對原型鏈的理解)
        • new (對創(chuàng)建對象實例過程的理解)
        • call&apply&bind (對this指向的理解)
        • 手寫promise (對異步的理解)
        • 手寫原生ajax (對http請求方式的理解,重點是get和post請求)

        1. 手寫instanceof


        instanceof作用:

        判斷一個實例是否是其父類或者祖先類型的實例。

        instanceof?在查找的過程中會遍歷左邊變量的原型鏈,直到找到右邊變量的?prototype查找失敗,返回 false

        let?myInstanceof?=?(target,origin)?=>?{
        ?????while(target)?{
        ?????????if(target.__proto__===origin.prototype)?{
        ????????????return?true
        ?????????}
        ?????????target?=?target.__proto__
        ?????}
        ?????return?false
        ?}
        ?let?a?=?[1,2,3]
        ?console.log(myInstanceof(a,Array));??//?true
        ?console.log(myInstanceof(a,Object));??//?true

        2. 實現(xiàn)數(shù)組的map方法


        Array.prototype.myMap?=?function(fn,?thisValue)?{
        ?????let?res?=?[]
        ?????thisValue?=?thisValue||[]
        ?????let?arr?=?this
        ?????for(let?i?in?arr)?{
        ????????res.push(fn(arr[i]))
        ?????}
        ?????return?res
        ?}

        3. reduce實現(xiàn)數(shù)組的map方法


        Array.prototype.myMap?=?function(fn,thisValue){
        ?????var?res?=?[];
        ?????thisValue?=?thisValue||[];
        ?????this.reduce(function(pre,cur,index,arr){
        ?????????return?res.push(fn.call(thisValue,cur,index,arr));
        ?????},[]);
        ?????return?res;
        }

        var?arr?=?[2,3,1,5];
        arr.myMap(function(item,index,arr){
        ?console.log(item,index,arr);
        })

        4. 手寫數(shù)組的reduce方法


        reduce() 方法接收一個函數(shù)作為累加器,數(shù)組中的每個值(從左到右)開始縮減,最終為一個值,是ES5中新增的又一個數(shù)組逐項處理方法

        參數(shù):

        • callback(一個在數(shù)組中每一項上調(diào)用的函數(shù),接受四個函數(shù):)
          • previousValue(上一次調(diào)用回調(diào)函數(shù)時的返回值,或者初始值)
          • currentValue(當前正在處理的數(shù)組元素)
          • currentIndex(當前正在處理的數(shù)組元素下標)
          • array(調(diào)用reduce()方法的數(shù)組)
        • initialValue(可選的初始值。作為第一次調(diào)用回調(diào)函數(shù)時傳給previousValue的值)
        function?reduce(arr,?cb,?initialValue){
        ?????var?num?=?initValue?==?undefined??num?=?arr[0]:?initValue;
        ?????var?i?=?initValue?==?undefined??1:?0
        ?????for?(i;?i????????num?=?cb(num,arr[i],i)
        ?????}
        ?????return?num
        ?}
        ?
        ?function?fn(result,?currentValue,?index){
        ?????return?result?+?currentValue
        ?}
        ?
        ?var?arr?=?[2,3,4,5]
        ?var?b?=?reduce(arr,?fn,10)?
        ?var?c?=?reduce(arr,?fn)
        ?console.log(b)???//?24

        5. 數(shù)組扁平化


        數(shù)組扁平化就是把多維數(shù)組轉(zhuǎn)化成一維數(shù)組

        1. es6提供的新方法 flat(depth)

        let?a?=?[1,[2,3]];?
        a.flat();?//?[1,2,3]?
        a.flat(1);?//[1,2,3]

        其實還有一種更簡單的辦法,無需知道數(shù)組的維度,直接將目標數(shù)組變成1維數(shù)組。depth的值設(shè)置為Infinity。

        let?a?=?[1,[2,3,[4,[5]]]];?
        a.flat(Infinity);?//?[1,2,3,4,5]??a是4維數(shù)組

        2. 利用cancat

        var?arr1?=?[1,?2,?3,?[1,?2,?3,?4,?[2,?3,?4]]];
        ?function?flatten(arr)?{
        ?????var?res?=?[];
        ?????for?(let?i?=?0,?length?=?arr.length;?i??????if?(Array.isArray(arr[i]))?{
        ?????res?=?res.concat(flatten(arr[i]));?//concat?并不會改變原數(shù)組
        ?????//res.push(...flatten(arr[i]));?//擴展運算符?
        ?????}?else?{
        ?????????res.push(arr[i]);
        ???????}
        ?????}
        ?????return?res;
        ?}
        ?flatten(arr1);?//[1,?2,?3,?1,?2,?3,?4,?2,?3,?4]

        6. 函數(shù)柯里化


        柯里化的定義:接收一部分參數(shù),返回一個函數(shù)接收剩余參數(shù),接收足夠參數(shù)后,執(zhí)行原函數(shù)。

        當柯里化函數(shù)接收到足夠參數(shù)后,就會執(zhí)行原函數(shù),如何去確定何時達到足夠的參數(shù)呢?

        有兩種思路:

        1. 通過函數(shù)的 length 屬性,獲取函數(shù)的形參個數(shù),形參的個數(shù)就是所需的參數(shù)個數(shù)
        2. 在調(diào)用柯里化工具函數(shù)時,手動指定所需的參數(shù)個數(shù)

        將這兩點結(jié)合一下,實現(xiàn)一個簡單 curry 函數(shù):

        /**
        ?*?將函數(shù)柯里化
        ?*?@param?fn????待柯里化的原函數(shù)
        ?*?@param?len???所需的參數(shù)個數(shù),默認為原函數(shù)的形參個數(shù)
        ?*/
        function?curry(fn,len?=?fn.length)?{
        ?return?_curry.call(this,fn,len)
        }

        /**
        ?*?中轉(zhuǎn)函數(shù)
        ?*?@param?fn????待柯里化的原函數(shù)
        ?*?@param?len???所需的參數(shù)個數(shù)
        ?*?@param?args??已接收的參數(shù)列表
        ?*/
        function?_curry(fn,len,...args)?{
        ????return?function?(...params)?{
        ?????????let?_args?=?[...args,...params];
        ?????????if(_args.length?>=?len){
        ?????????????return?fn.apply(this,_args);
        ?????????}else{
        ??????????return?_curry.call(this,fn,len,..._args)
        ?????????}
        ????}
        }

        我們來驗證一下:

        let?_fn?=?curry(function(a,b,c,d,e){
        ?console.log(a,b,c,d,e)
        });

        _fn(1,2,3,4,5);?????//?print:?1,2,3,4,5
        _fn(1)(2)(3,4,5);???//?print:?1,2,3,4,5
        _fn(1,2)(3,4)(5);???//?print:?1,2,3,4,5
        _fn(1)(2)(3)(4)(5);?//?print:?1,2,3,4,5

        我們常用的工具庫 lodash 也提供了 curry 方法,并且增加了非常好玩的 placeholder 功能,通過占位符的方式來改變傳入?yún)?shù)的順序。

        比如說,我們傳入一個占位符,本次調(diào)用傳遞的參數(shù)略過占位符, 占位符所在的位置由下次調(diào)用的參數(shù)來填充,比如這樣:

        直接看一下官網(wǎng)的例子:


        接下來我們來思考,如何實現(xiàn)占位符的功能。

        對于 lodash 的 curry 函數(shù)來說,curry 函數(shù)掛載在 lodash 對象上,所以將 lodash 對象當做默認占位符來使用。

        而我們的自己實現(xiàn)的 curry 函數(shù),本身并沒有掛載在任何對象上,所以將 curry 函數(shù)當做默認占位符

        使用占位符,目的是改變參數(shù)傳遞的順序,所以在 curry 函數(shù)實現(xiàn)中,每次需要記錄是否使用了占位符,并且記錄占位符所代表的參數(shù)位置。

        直接上代碼:

        /**
        ?*?@param??fn???????????待柯里化的函數(shù)
        ?*?@param??length???????需要的參數(shù)個數(shù),默認為函數(shù)的形參個數(shù)
        ?*?@param??holder???????占位符,默認當前柯里化函數(shù)
        ?*?@return?{Function}???柯里化后的函數(shù)
        ?*/
        function?curry(fn,length?=?fn.length,holder?=?curry){
        ?return?_curry.call(this,fn,length,holder,[],[])
        }
        /**
        ?*?中轉(zhuǎn)函數(shù)
        ?*?@param?fn????????????柯里化的原函數(shù)
        ?*?@param?length????????原函數(shù)需要的參數(shù)個數(shù)
        ?*?@param?holder????????接收的占位符
        ?*?@param?args??????????已接收的參數(shù)列表
        ?*?@param?holders???????已接收的占位符位置列表
        ?*?@return?{Function}???繼續(xù)柯里化的函數(shù)?或?最終結(jié)果
        ?*/
        function?_curry(fn,length,holder,args,holders){
        ?return?function(..._args){
        ?//將參數(shù)復制一份,避免多次操作同一函數(shù)導致參數(shù)混亂
        ?let?params?=?args.slice();
        ?//將占位符位置列表復制一份,新增加的占位符增加至此
        ?let?_holders?=?holders.slice();
        ?//循環(huán)入?yún)ⅲ芳訁?shù)?或?替換占位符
        ?_args.forEach((arg,i)=>{
        ?//真實參數(shù)?之前存在占位符?將占位符替換為真實參數(shù)
        ?if?(arg?!==?holder?&&?holders.length)?{
        ?????let?index?=?holders.shift();
        ?????_holders.splice(_holders.indexOf(index),1);
        ?????params[index]?=?arg;
        ?}
        ?//真實參數(shù)?之前不存在占位符?將參數(shù)追加到參數(shù)列表中
        ?else?if(arg?!==?holder?&&?!holders.length){
        ?????params.push(arg);
        ?}
        ?//傳入的是占位符,之前不存在占位符?記錄占位符的位置
        ?else?if(arg?===?holder?&&?!holders.length){
        ?????params.push(arg);
        ?????_holders.push(params.length?-?1);
        ?}
        ?//傳入的是占位符,之前存在占位符?刪除原占位符位置
        ?else?if(arg?===?holder?&&?holders.length){
        ????holders.shift();
        ?}
        ?});
        ?//?params?中前?length?條記錄中不包含占位符,執(zhí)行函數(shù)
        ?if(params.length?>=?length?&&?params.slice(0,length).every(i=>i!==holder)){
        ?return?fn.apply(this,params);
        ?}else{
        ?return?_curry.call(this,fn,length,holder,params,_holders)
        ?}
        ?}
        }

        驗證一下:;

        let?fn?=?function(a,?b,?c,?d,?e)?{
        ?console.log([a,?b,?c,?d,?e]);
        }

        let?_?=?{};?//?定義占位符
        let?_fn?=?curry(fn,5,_);??//?將函數(shù)柯里化,指定所需的參數(shù)個數(shù),指定所需的占位符

        _fn(1,?2,?3,?4,?5);?????????????????//?print:?1,2,3,4,5
        _fn(_,?2,?3,?4,?5)(1);??????????????//?print:?1,2,3,4,5
        _fn(1,?_,?3,?4,?5)(2);??????????????//?print:?1,2,3,4,5
        _fn(1,?_,?3)(_,?4,_)(2)(5);?????????//?print:?1,2,3,4,5
        _fn(1,?_,?_,?4)(_,?3)(2)(5);????????//?print:?1,2,3,4,5
        _fn(_,?2)(_,?_,?4)(1)(3)(5);????????//?print:?1,2,3,4,5

        至此,我們已經(jīng)完整實現(xiàn)了一個 curry 函數(shù)~~

        7. 實現(xiàn)深拷貝


        淺拷貝和深拷貝的區(qū)別:

        淺拷貝:只拷貝一層,更深層的對象級別的只拷貝引用

        深拷貝:拷貝多層,每一級別的數(shù)據(jù)都會拷貝。這樣更改拷貝值就不影響另外的對象

        ES6淺拷貝方法:Object.assign(target,...sources)

        let?obj={
        ?id:1,
        ?name:'Tom',
        ?msg:{
        ?age:18
        ?}
        }
        let?o={}
        //實現(xiàn)深拷貝??遞歸????可以用于生命游戲那個題對二維數(shù)組的拷貝,
        //但比較麻煩,因為已知元素都是值,直接復制就行,無需判斷
        function?deepCopy(newObj,oldObj){
        ?????for(var?k?in?oldObj){
        ?????????let?item=oldObj[k]
        ?????????//判斷是數(shù)組?對象?簡單類型?
        ?????????if(item?instanceof?Array){
        ?????????????newObj[k]=[]
        ?????????????deepCopy(newObj[k],item)
        ?????????}else?if(item?instanceof?Object){
        ?????????????newObj[k]={}
        ?????????????deepCopy(newObj[k],item)
        ?????????}else{??//簡單數(shù)據(jù)類型,直接賦值
        ?????????????newObj[k]=item
        ?????????}
        ?????}
        }

        8. 手寫call, apply, bind


        手寫call

        Function.prototype.myCall=function(context=window){??//?函數(shù)的方法,所以寫在Fuction原型對象上
        ?if(typeof?this?!=="function"){???//?這里if其實沒必要,會自動拋出錯誤
        ????throw?new?Error("不是函數(shù)")
        ?}
        ?const?obj=context||window???//這里可用ES6方法,為參數(shù)添加默認值,js嚴格模式全局作用域this為undefined
        ?obj.fn=this??????//this為調(diào)用的上下文,this此處為函數(shù),將這個函數(shù)作為obj的方法
        ?const?arg=[...arguments].slice(1)???//第一個為obj所以刪除,偽數(shù)組轉(zhuǎn)為數(shù)組
        ?res=obj.fn(...arg)
        ?delete?obj.fn???//?不刪除會導致context屬性越來越多
        ?return?res
        }

        //用法:f.call(obj,arg1)
        function?f(a,b){
        ?console.log(a+b)
        ?console.log(this.name)
        }
        let?obj={
        ?name:1
        }
        f.myCall(obj,1,2)?//否則this指向window

        obj.greet.call({name:?'Spike'})?//打出來的是?Spike

        手寫apply(arguments[this, [參數(shù)1,參數(shù)2.....] ])

        Function.prototype.myApply=function(context){??//?箭頭函數(shù)從不具有參數(shù)對象?。。。?!這里不能寫成箭頭函數(shù)
        ?let?obj=context||window
        ?obj.fn=this
        ?const?arg=arguments[1]||[]????//若有參數(shù),得到的是數(shù)組
        ?let?res=obj.fn(...arg)
        ?delete?obj.fn
        ?return?res
        }?
        function?f(a,b){
        ?console.log(a,b)
        ?console.log(this.name)
        }
        let?obj={
        ?name:'張三'
        }
        f.myApply(obj,[1,2])??//arguments[1]

        手寫bind

        this.value?=?2
        var?foo?=?{
        ?value:?1
        };
        var?bar?=?function(name,?age,?school){
        ?console.log(name)?//?'An'
        ?console.log(age)?//?22
        ?console.log(school)?//?'家里蹲大學'
        }
        var?result?=?bar.bind(foo,?'An')?//預置了部分參數(shù)'An'
        result(22,?'家里蹲大學')?//這個參數(shù)會和預置的參數(shù)合并到一起放入bar中

        簡單版本

        Function.prototype.bind?=?function(context,?...outerArgs)?{
        ?var?fn?=?this;
        ?return?function(...innerArgs)?{???//返回了一個函數(shù),...rest為實際調(diào)用時傳入的參數(shù)
        ?return?fn.apply(context,[...outerArgs,?...innerArgs]);??//返回改變了this的函數(shù),
        ?//參數(shù)合并
        ?}
        }

        new失敗的原因:

        例:

        //?聲明一個上下文
        let?thovino?=?{
        ?name:?'thovino'
        }

        //?聲明一個構(gòu)造函數(shù)
        let?eat?=?function?(food)?{
        ?this.food?=?food
        ?console.log(`${this.name}?eat?${this.food}`)
        }
        eat.prototype.sayFuncName?=?function?()?{
        ?console.log('func?name?:?eat')
        }

        //?bind一下
        let?thovinoEat?=?eat.bind(thovino)
        let?instance?=?new?thovinoEat('orange')??//實際上orange放到了thovino里面
        console.log('instance:',?instance)?//?{}

        生成的實例是個空對象

        在new操作符執(zhí)行時,我們的thovinoEat函數(shù)可以看作是這樣:

        function?thovinoEat?(...innerArgs)?{
        ?eat.call(thovino,?...outerArgs,?...innerArgs)
        }

        在new操作符進行到第三步的操作thovinoEat.call(obj, ...args)時,這里的obj是new操作符自己創(chuàng)建的那個簡單空對象{},但它其實并沒有替換掉thovinoEat函數(shù)內(nèi)部的那個上下文對象thovino。這已經(jīng)超出了call的能力范圍,因為這個時候要替換的已經(jīng)不是thovinoEat函數(shù)內(nèi)部的this指向,而應該是thovino對象。

        換句話說,我們希望的是new操作符將eat內(nèi)的this指向操作符自己創(chuàng)建的那個空對象。但是實際上指向了thovino,new操作符的第三步動作并沒有成功!

        可new可繼承版本

        Function.prototype.bind?=?function?(context,?...outerArgs)?{
        ?let?that?=?this;

        function?res?(...innerArgs)?{
        ?????if?(this?instanceof?res)?{
        ?????????//?new操作符執(zhí)行時
        ?????????//?這里的this在new操作符第三步操作時,會指向new自身創(chuàng)建的那個簡單空對象{}
        ?????????that.call(this,?...outerArgs,?...innerArgs)
        ?????}?else?{
        ?????????//?普通bind
        ?????????that.call(context,?...outerArgs,?...innerArgs)
        ?????}
        ?????}
        ?????res.prototype?=?this.prototype?//?。?!
        ?????return?res
        }

        9. 手動實現(xiàn)new


        new的過程文字描述:

        1. 創(chuàng)建一個空對象 obj;
        2. 將空對象的隱式原型(proto)指向構(gòu)造函數(shù)的prototype。
        3. 使用 call 改變 this 的指向
        4. 如果無返回值或者返回一個非對象值,則將 obj 返回作為新對象;如果返回值是一個新對象的話那么直接直接返回該對象。

        function?Person(name,age){
        ?this.name=name
        ?this.age=age
        }
        Person.prototype.sayHi=function(){
        ?console.log('Hi!我是'+this.name)
        }
        let?p1=new?Person('張三',18)

        ////手動實現(xiàn)new
        function?create(){
        ?let?obj={}
        ?//獲取構(gòu)造函數(shù)
        ?let?fn=[].shift.call(arguments)??//將arguments對象提出來轉(zhuǎn)化為數(shù)組,arguments并不是數(shù)組而是對象?????。?!這種方法刪除了arguments數(shù)組的第一個元素,??!這里的空數(shù)組里面填不填元素都沒關(guān)系,不影響arguments的結(jié)果??????或者let?arg?=?[].slice.call(arguments,1)
        ?obj.__proto__=fn.prototype
        ?let?res=fn.apply(obj,arguments)????//改變this指向,為實例添加方法和屬性
        ?//確保返回的是一個對象(萬一fn不是構(gòu)造函數(shù))
        ?return?typeof?res==='object'?res:obj
        }

        let?p2=create(Person,'李四',19)
        p2.sayHi()

        細節(jié):

        [].shift.call(arguments)??也可寫成:
        ?let?arg=[...arguments]
        ?let?fn=arg.shift()??//使得arguments能調(diào)用數(shù)組方法,第一個參數(shù)為構(gòu)造函數(shù)
        ?obj.__proto__=fn.prototype
        ?//改變this指向,為實例添加方法和屬性
        ?let?res=fn.apply(obj,arg)

        10. 手寫promise(常見promise.all, promise.race)


        //?Promise/A+?規(guī)范規(guī)定的三種狀態(tài)
        const?STATUS?=?{
        ?PENDING:?'pending',
        ?FULFILLED:?'fulfilled',
        ?REJECTED:?'rejected'
        }

        class?MyPromise?{
        ?//?構(gòu)造函數(shù)接收一個執(zhí)行回調(diào)
        ?constructor(executor)?{
        ?????this._status?=?STATUS.PENDING?//?Promise初始狀態(tài)
        ?????this._value?=?undefined?//?then回調(diào)的值
        ?????this._resolveQueue?=?[]?//?resolve時觸發(fā)的成功隊列
        ?????this._rejectQueue?=?[]?//?reject時觸發(fā)的失敗隊列
        ????
        ?//?使用箭頭函數(shù)固定this(resolve函數(shù)在executor中觸發(fā),不然找不到this)
        ?const?resolve?=?value?=>?{
        ?????const?run?=?()?=>?{
        ?????????//?Promise/A+?規(guī)范規(guī)定的Promise狀態(tài)只能從pending觸發(fā),變成fulfilled
        ?????????if?(this._status?===?STATUS.PENDING)?{
        ?????????????this._status?=?STATUS.FULFILLED?//?更改狀態(tài)
        ?????????????this._value?=?value?//?儲存當前值,用于then回調(diào)
        ????????????
        ?????????????//?執(zhí)行resolve回調(diào)
        ?????????????while?(this._resolveQueue.length)?{
        ?????????????????const?callback?=?this._resolveQueue.shift()
        ?????????????????callback(value)
        ?????????????}
        ?????????}
        ?????}
        ?????//把resolve執(zhí)行回調(diào)的操作封裝成一個函數(shù),放進setTimeout里,以實現(xiàn)promise異步調(diào)用的特性(規(guī)范上是微任務(wù),這里是宏任務(wù))
        ?????setTimeout(run)
        ?}

        ?//?同?resolve
        ?const?reject?=?value?=>?{
        ?????const?run?=?()?=>?{
        ?????????if?(this._status?===?STATUS.PENDING)?{
        ?????????this._status?=?STATUS.REJECTED
        ?????????this._value?=?value
        ????????
        ?????????while?(this._rejectQueue.length)?{
        ?????????????const?callback?=?this._rejectQueue.shift()
        ?????????????callback(value)
        ?????????}
        ?????}
        ?}
        ?????setTimeout(run)
        ?}

        ?????//?new?Promise()時立即執(zhí)行executor,并傳入resolve和reject
        ?????executor(resolve,?reject)
        ?}

        ?//?then方法,接收一個成功的回調(diào)和一個失敗的回調(diào)
        ?function?then(onFulfilled,?onRejected)?{
        ??//?根據(jù)規(guī)范,如果then的參數(shù)不是function,則忽略它,?讓值繼續(xù)往下傳遞,鏈式調(diào)用繼續(xù)往下執(zhí)行
        ??typeof?onFulfilled?!==?'function'???onFulfilled?=?value?=>?value?:?null
        ??typeof?onRejected?!==?'function'???onRejected?=?error?=>?error?:?null

        ??//?then?返回一個新的promise
        ??return?new?MyPromise((resolve,?reject)?=>?{
        ????const?resolveFn?=?value?=>?{
        ??????try?{
        ????????const?x?=?onFulfilled(value)
        ????????//?分類討論返回值,如果是Promise,那么等待Promise狀態(tài)變更,否則直接resolve
        ????????x?instanceof?MyPromise???x.then(resolve,?reject)?:?resolve(x)
        ??????}?catch?(error)?{
        ????????reject(error)
        ??????}
        ????}
        ??}
        }

        ??const?rejectFn?=?error?=>?{
        ??????try?{
        ????????const?x?=?onRejected(error)
        ????????x?instanceof?MyPromise???x.then(resolve,?reject)?:?resolve(x)
        ??????}?catch?(error)?{
        ????????reject(error)
        ??????}
        ????}

        ????switch?(this._status)?{
        ??????case?STATUS.PENDING:
        ????????this._resolveQueue.push(resolveFn)
        ????????this._rejectQueue.push(rejectFn)
        ????????break;
        ??????case?STATUS.FULFILLED:
        ????????resolveFn(this._value)
        ????????break;
        ??????case?STATUS.REJECTED:
        ????????rejectFn(this._value)
        ????????break;
        ????}
        ?})
        ?}
        ?catch?(rejectFn)?{
        ??return?this.then(undefined,?rejectFn)
        }
        //?promise.finally方法
        finally(callback)?{
        ??return?this.then(value?=>?MyPromise.resolve(callback()).then(()?=>?value),?error?=>?{
        ????MyPromise.resolve(callback()).then(()?=>?error)
        ??})
        }

        ?//?靜態(tài)resolve方法
        ?static?resolve(value)?{
        ??????return?value?instanceof?MyPromise???value?:?new?MyPromise(resolve?=>?resolve(value))
        ??}

        ?//?靜態(tài)reject方法
        ?static?reject(error)?{
        ??????return?new?MyPromise((resolve,?reject)?=>?reject(error))
        ????}

        ?//?靜態(tài)all方法
        ?static?all(promiseArr)?{
        ??????let?count?=?0
        ??????let?result?=?[]
        ??????return?new?MyPromise((resolve,?reject)?=>???????{
        ????????if?(!promiseArr.length)?{
        ??????????return?resolve(result)
        ????????}
        ????????promiseArr.forEach((p,?i)?=>?{
        ??????????MyPromise.resolve(p).then(value?=>?{
        ????????????count++
        ????????????result[i]?=?value
        ????????????if?(count?===?promiseArr.length)?{
        ??????????????resolve(result)
        ????????????}
        ??????????},?error?=>?{
        ????????????reject(error)
        ??????????})
        ????????})
        ??????})
        ????}

        ?//?靜態(tài)race方法
        ?static?race(promiseArr)?{
        ??????return?new?MyPromise((resolve,?reject)?=>?{
        ????????promiseArr.forEach(p?=>?{
        ??????????MyPromise.resolve(p).then(value?=>?{
        ????????????resolve(value)
        ??????????},?error?=>?{
        ????????????reject(error)
        ??????????})
        ????????})
        ??????})
        ????}
        }

        11. 手寫原生AJAX


        步驟

        1. 創(chuàng)建 XMLHttpRequest 實例
        2. 發(fā)出 HTTP 請求
        3. 服務(wù)器返回 XML 格式的字符串
        4. JS 解析 XML,并更新局部頁面
          不過隨著歷史進程的推進,XML 已經(jīng)被淘汰,取而代之的是?JSON。

        了解了屬性和方法之后,根據(jù) AJAX 的步驟,手寫最簡單的 GET 請求。
        version 1.0:

        myButton.addEventListener('click',?function?()?{
        ??ajax()
        })

        function?ajax()?{
        ??let?xhr?=?new?XMLHttpRequest()?//實例化,以調(diào)用方法
        ??xhr.open('get',?'https://www.google.com')??//參數(shù)2,url。參數(shù)三:異步
        ??xhr.onreadystatechange?=?()?=>?{??//每當?readyState?屬性改變時,就會調(diào)用該函數(shù)。
        ????if?(xhr.readyState?===?4)?{??//XMLHttpRequest?代理當前所處狀態(tài)。
        ??????if?(xhr.status?>=?200?&&?xhr.status?????????let?string?=?request.responseText
        ????????//JSON.parse()?方法用來解析JSON字符串,構(gòu)造由字符串描述的JavaScript值或?qū)ο?br>????????let?object?=?JSON.parse(string)
        ??????}
        ????}
        ??}
        ??request.send()?//用于實際發(fā)出?HTTP?請求。不帶參數(shù)為GET請求
        }

        promise實現(xiàn)

        function?ajax(url)?{
        ??const?p?=?new?Promise((resolve,?reject)?=>?{
        ????let?xhr?=?new?XMLHttpRequest()
        ????xhr.open('get',?url)
        ????xhr.onreadystatechange?=?()?=>?{
        ??????if?(xhr.readyState?==?4)?{
        ????????if?(xhr.status?>=?200?&&?xhr.status?<=?300)?{
        ??????????resolve(JSON.parse(xhr.responseText))
        ????????}?else?{
        ??????????reject('請求出錯')
        ????????}
        ??????}
        ????}
        ????xhr.send()??//發(fā)送hppt請求
        ??})
        ??return?p
        }
        let?url?=?'/data.json'
        ajax(url).then(res?=>?console.log(res))
        ??.catch(reason?=>?console.log(reason))

        12. 手寫節(jié)流防抖函數(shù)


        防抖:

        function?debounce(fn,?delay)?{
        ?????if(typeof?fn!=='function')?{
        ????????throw?new?TypeError('fn不是函數(shù)')
        ?????}
        ?????let?timer;?//?維護一個?timer
        ?????return?function?()?{
        ?????????var?_this?=?this;?//?取debounce執(zhí)行作用域的this(原函數(shù)掛載到的對象)
        ?????????var?args?=?arguments;
        ?????????if?(timer)?{
        ????????????clearTimeout(timer);
        ?????????}
        ?????????timer?=?setTimeout(function?()?{
        ????????????fn.apply(_this,?args);?//?用apply指向調(diào)用debounce的對象,相當于_this.fn(args);
        ?????????},?delay);
        ?????};
        }

        input1.addEventListener('keyup',?debounce(()?=>?{
        ?console.log(input1.value)
        }),?600)

        節(jié)流:

        function?throttle(fn,?delay)?{
        ??let?timer;
        ??return?function?()?{
        ????var?_this?=?this;
        ????var?args?=?arguments;
        ????if?(timer)?{
        ??????return;
        ????}
        ????timer?=?setTimeout(function?()?{
        ??????fn.apply(_this,?args);?//?這里args接收的是外邊返回的函數(shù)的參數(shù),不能用arguments
        ??????//?fn.apply(_this,?arguments);?需要注意:Chrome?14?以及?Internet?Explorer?9?仍然不接受類數(shù)組對象。如果傳入類數(shù)組對象,它們會拋出異常。
        ??????timer?=?null;?//?在delay后執(zhí)行完fn之后清空timer,此時timer為假,throttle觸發(fā)可以進入計時器
        ????},?delay)
        ??}
        }

        div1.addEventListener('drag',?throttle((e)?=>?{
        ??console.log(e.offsetX,?e.offsetY)
        },?100))

        13. 手寫Promise加載圖片


        function?getData(url)?{
        ??return?new?Promise((resolve,?reject)?=>?{
        ????$.ajax({
        ??????url,
        ??????success(data)?{
        ????????resolve(data)
        ??????},
        ??????error(err)?{
        ????????reject(err)
        ??????}
        ????})
        ??})
        }
        const?url1?=?'./data1.json'
        const?url2?=?'./data2.json'
        const?url3?=?'./data3.json'
        getData(url1).then(data1?=>?{
        ??console.log(data1)
        ??return?getData(url2)
        }).then(data2?=>?{
        ??console.log(data2)
        ??return?getData(url3)
        }).then(data3?=>
        ??console.log(data3)
        ).catch(err?=>
        ??console.error(err)
        )

        14. 函數(shù)實現(xiàn)一秒鐘輸出一個數(shù)


        for(let?i=0;i<=10;i++){???//用var打印的都是11
        ?setTimeout(()=>{
        ????console.log(i);
        ?},1000*i)
        }

        15. 創(chuàng)建10個標簽,點擊的時候彈出來對應的序號?


        var?a
        for(let?i=0;i<10;i++){
        ?a=document.createElement('a')
        ?a.innerHTML=i+'
        '

        ?a.addEventListener('click',function(e){
        ?????console.log(this)??//this為當前點擊的
        ?????e.preventDefault()??//如果調(diào)用這個方法,默認事件行為將不再觸發(fā)。
        ?????//例如,在執(zhí)行這個方法后,如果點擊一個鏈接(a標簽),瀏覽器不會跳轉(zhuǎn)到新的?URL?去了。我們可以用?event.isDefaultPrevented()?來確定這個方法是否(在那個事件對象上)被調(diào)用過了。
        ?????alert(i)
        ?})
        ?const?d=document.querySelector('div')
        ?d.appendChild(a)??//append向一個已存在的元素追加該元素。
        }



        點擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開更多互動和交流。

        -?END -

        瀏覽 46
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        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>
            美女逼逼图 | x88AV熟女系列 | 艹逼逼视频 | 婷婷性多多影院 | 黄色毛片电影 | 成人精品视频一区二区 | 91影院成人 | 久久综合久久鬼 | 亚洲无码高清视频 | 国产成人在线综合豆花 |