<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>

          前端面試js高頻手寫大全

          共 13274字,需瀏覽 27分鐘

           ·

          2021-01-12 02:13

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



          本文涵蓋了前端面試常考的各種重點手寫。


          建議優(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. 實現數組的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實現數組的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. 手寫數組的reduce方法


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

          參數:

          • callback(一個在數組中每一項上調用的函數,接受四個函數:)
            • previousValue(上一次調用回調函數時的返回值,或者初始值)
            • currentValue(當前正在處理的數組元素)
            • currentIndex(當前正在處理的數組元素下標)
            • array(調用reduce()方法的數組)
          • initialValue(可選的初始值。作為第一次調用回調函數時傳給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. 數組扁平化


          數組扁平化就是把多維數組轉化成一維數組

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

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

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

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

          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?并不會改變原數組
          ?????//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. 函數柯里化


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

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

          有兩種思路:

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

          將這兩點結合一下,實現一個簡單 curry 函數:

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

          /**
          ?*?中轉函數
          ?*?@param?fn????待柯里化的原函數
          ?*?@param?len???所需的參數個數
          ?*?@param?args??已接收的參數列表
          ?*/
          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 功能,通過占位符的方式來改變傳入參數的順序。

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

          直接看一下官網的例子:


          接下來我們來思考,如何實現占位符的功能。

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

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

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

          直接上代碼:

          /**
          ?*?@param??fn???????????待柯里化的函數
          ?*?@param??length???????需要的參數個數,默認為函數的形參個數
          ?*?@param??holder???????占位符,默認當前柯里化函數
          ?*?@return?{Function}???柯里化后的函數
          ?*/
          function?curry(fn,length?=?fn.length,holder?=?curry){
          ?return?_curry.call(this,fn,length,holder,[],[])
          }
          /**
          ?*?中轉函數
          ?*?@param?fn????????????柯里化的原函數
          ?*?@param?length????????原函數需要的參數個數
          ?*?@param?holder????????接收的占位符
          ?*?@param?args??????????已接收的參數列表
          ?*?@param?holders???????已接收的占位符位置列表
          ?*?@return?{Function}???繼續(xù)柯里化的函數?或?最終結果
          ?*/
          function?_curry(fn,length,holder,args,holders){
          ?return?function(..._args){
          ?//將參數復制一份,避免多次操作同一函數導致參數混亂
          ?let?params?=?args.slice();
          ?//將占位符位置列表復制一份,新增加的占位符增加至此
          ?let?_holders?=?holders.slice();
          ?//循環(huán)入參,追加參數?或?替換占位符
          ?_args.forEach((arg,i)=>{
          ?//真實參數?之前存在占位符?將占位符替換為真實參數
          ?if?(arg?!==?holder?&&?holders.length)?{
          ?????let?index?=?holders.shift();
          ?????_holders.splice(_holders.indexOf(index),1);
          ?????params[index]?=?arg;
          ?}
          ?//真實參數?之前不存在占位符?將參數追加到參數列表中
          ?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í)行函數
          ?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,_);??//?將函數柯里化,指定所需的參數個數,指定所需的占位符

          _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

          至此,我們已經完整實現了一個 curry 函數~~

          7. 實現深拷貝


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

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

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

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

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

          8. 手寫call, apply, bind


          手寫call

          Function.prototype.myCall=function(context=window){??//?函數的方法,所以寫在Fuction原型對象上
          ?if(typeof?this?!=="function"){???//?這里if其實沒必要,會自動拋出錯誤
          ????throw?new?Error("不是函數")
          ?}
          ?const?obj=context||window???//這里可用ES6方法,為參數添加默認值,js嚴格模式全局作用域this為undefined
          ?obj.fn=this??????//this為調用的上下文,this此處為函數,將這個函數作為obj的方法
          ?const?arg=[...arguments].slice(1)???//第一個為obj所以刪除,偽數組轉為數組
          ?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, [參數1,參數2.....] ])

          Function.prototype.myApply=function(context){??//?箭頭函數從不具有參數對象!!!!!這里不能寫成箭頭函數
          ?let?obj=context||window
          ?obj.fn=this
          ?const?arg=arguments[1]||[]????//若有參數,得到的是數組
          ?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')?//預置了部分參數'An'
          result(22,?'家里蹲大學')?//這個參數會和預置的參數合并到一起放入bar中

          簡單版本

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

          new失敗的原因:

          例:

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

          //?聲明一個構造函數
          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函數可以看作是這樣:

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

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

          換句話說,我們希望的是new操作符將eat內的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. 手動實現new


          new的過程文字描述:

          1. 創(chuàng)建一個空對象 obj;
          2. 將空對象的隱式原型(proto)指向構造函數的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)

          ////手動實現new
          function?create(){
          ?let?obj={}
          ?//獲取構造函數
          ?let?fn=[].shift.call(arguments)??//將arguments對象提出來轉化為數組,arguments并不是數組而是對象????!!!這種方法刪除了arguments數組的第一個元素,!!這里的空數組里面填不填元素都沒關系,不影響arguments的結果??????或者let?arg?=?[].slice.call(arguments,1)
          ?obj.__proto__=fn.prototype
          ?let?res=fn.apply(obj,arguments)????//改變this指向,為實例添加方法和屬性
          ?//確保返回的是一個對象(萬一fn不是構造函數)
          ?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能調用數組方法,第一個參數為構造函數
          ?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?{
          ?//?構造函數接收一個執(zhí)行回調
          ?constructor(executor)?{
          ?????this._status?=?STATUS.PENDING?//?Promise初始狀態(tài)
          ?????this._value?=?undefined?//?then回調的值
          ?????this._resolveQueue?=?[]?//?resolve時觸發(fā)的成功隊列
          ?????this._rejectQueue?=?[]?//?reject時觸發(fā)的失敗隊列
          ????
          ?//?使用箭頭函數固定this(resolve函數在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回調
          ????????????
          ?????????????//?執(zhí)行resolve回調
          ?????????????while?(this._resolveQueue.length)?{
          ?????????????????const?callback?=?this._resolveQueue.shift()
          ?????????????????callback(value)
          ?????????????}
          ?????????}
          ?????}
          ?????//把resolve執(zhí)行回調的操作封裝成一個函數,放進setTimeout里,以實現promise異步調用的特性(規(guī)范上是微任務,這里是宏任務)
          ?????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方法,接收一個成功的回調和一個失敗的回調
          ?function?then(onFulfilled,?onRejected)?{
          ??//?根據規(guī)范,如果then的參數不是function,則忽略它,?讓值繼續(xù)往下傳遞,鏈式調用繼續(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. 服務器返回 XML 格式的字符串
          4. JS 解析 XML,并更新局部頁面
            不過隨著歷史進程的推進,XML 已經被淘汰,取而代之的是?JSON。

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

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

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

          promise實現

          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é)流防抖函數


          防抖:

          function?debounce(fn,?delay)?{
          ?????if(typeof?fn!=='function')?{
          ????????throw?new?TypeError('fn不是函數')
          ?????}
          ?????let?timer;?//?維護一個?timer
          ?????return?function?()?{
          ?????????var?_this?=?this;?//?取debounce執(zhí)行作用域的this(原函數掛載到的對象)
          ?????????var?args?=?arguments;
          ?????????if?(timer)?{
          ????????????clearTimeout(timer);
          ?????????}
          ?????????timer?=?setTimeout(function?()?{
          ????????????fn.apply(_this,?args);?//?用apply指向調用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接收的是外邊返回的函數的參數,不能用arguments
          ??????//?fn.apply(_this,?arguments);?需要注意:Chrome?14?以及?Internet?Explorer?9?仍然不接受類數組對象。如果傳入類數組對象,它們會拋出異常。
          ??????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. 函數實現一秒鐘輸出一個數


          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()??//如果調用這個方法,默認事件行為將不再觸發(fā)。
          ?????//例如,在執(zhí)行這個方法后,如果點擊一個鏈接(a標簽),瀏覽器不會跳轉到新的?URL?去了。我們可以用?event.isDefaultPrevented()?來確定這個方法是否(在那個事件對象上)被調用過了。
          ?????alert(i)
          ?})
          ?const?d=document.querySelector('div')
          ?d.appendChild(a)??//append向一個已存在的元素追加該元素。
          }



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

          -?END -

          瀏覽 46
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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 | 香蕉网狼人| 全部免费A片在线在线观看 | 91精品国产91久久久久 | 影音先锋青青草AV |