<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高頻手寫(xiě)大全

          共 32734字,需瀏覽 66分鐘

           ·

          2021-09-15 22:13

          來(lái)自:SegmentFault ,作者:晚起的蟲(chóng)兒
          鏈接:https://segmentfault.com/a/1190000038910420

          點(diǎn)擊上方 前端Q,關(guān)注公眾號(hào)

          回復(fù)加群,加入前端Q技術(shù)交流群




          本文涵蓋了前端面試常考的各種重點(diǎn)手寫(xiě)。


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


          • instanceof (考察對(duì)原型鏈的理解)
          • new (對(duì)創(chuàng)建對(duì)象實(shí)例過(guò)程的理解)
          • call&apply&bind (對(duì)this指向的理解)
          • 手寫(xiě)promise (對(duì)異步的理解)
          • 手寫(xiě)原生ajax (對(duì)http請(qǐng)求方式的理解,重點(diǎn)是get和post請(qǐng)求)

          1、手寫(xiě)instanceof


          instanceof作用:

          判斷一個(gè)實(shí)例是否是其父類(lèi)或者祖先類(lèi)型的實(shí)例。

          instanceof 在查找的過(guò)程中會(huì)遍歷左邊變量的原型鏈,直到找到右邊變量的 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. 實(shí)現(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實(shí)現(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. 手寫(xiě)數(shù)組的reduce方法


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

          參數(shù):

          • callback(一個(gè)在數(shù)組中每一項(xiàng)上調(diào)用的函數(shù),接受四個(gè)函數(shù):)
            • previousValue(上一次調(diào)用回調(diào)函數(shù)時(shí)的返回值,或者初始值)
            • currentValue(當(dāng)前正在處理的數(shù)組元素)
            • currentIndex(當(dāng)前正在處理的數(shù)組元素下標(biāo))
            • array(調(diào)用reduce()方法的數(shù)組)
          • initialValue(可選的初始值。作為第一次調(diào)用回調(diào)函數(shù)時(shí)傳給previousValue的值)
          function reduce(arr, cb, initialValue){
               var num = initValue == undefined? num = arr[0]: initValue;
               var i = initValue == undefined? 1: 0
               for (i; i< arr.length; 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í)還有一種更簡(jiǎn)單的辦法,無(wú)需知道數(shù)組的維度,直接將目標(biāo)數(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 < length; i++) {
               if (Array.isArray(arr[i])) {
               res = res.concat(flatten(arr[i])); //concat 并不會(huì)改變?cè)瓟?shù)組
               //res.push(...flatten(arr[i])); //擴(kuò)展運(yùn)算符 
               } else {
                   res.push(arr[i]);
                 }
               }
               return res;
           }
           flatten(arr1); //[1, 2, 3, 1, 2, 3, 4, 2, 3, 4]

          6. 函數(shù)柯里化


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

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

          有兩種思路:

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

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

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

          /**
           * 中轉(zhuǎn)函數(shù)
           * @param fn    待柯里化的原函數(shù)
           * @param len   所需的參數(shù)個(gè)數(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)
                   }
              }
          }

          我們來(lái)驗(yàn)證一下:

          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

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

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

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


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

          對(duì)于 lodash 的 curry 函數(shù)來(lái)說(shuō),curry 函數(shù)掛載在 lodash 對(duì)象上,所以將 lodash 對(duì)象當(dāng)做默認(rèn)占位符來(lái)使用。

          而我們的自己實(shí)現(xiàn)的 curry 函數(shù),本身并沒(méi)有掛載在任何對(duì)象上,所以將 curry 函數(shù)當(dāng)做默認(rèn)占位符

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

          直接上代碼:

          /**
           * @param  fn           待柯里化的函數(shù)
           * @param  length       需要的參數(shù)個(gè)數(shù),默認(rèn)為函數(shù)的形參個(gè)數(shù)
           * @param  holder       占位符,默認(rèn)當(dāng)前柯里化函數(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ù)個(gè)數(shù)
           * @param holder        接收的占位符
           * @param args          已接收的參數(shù)列表
           * @param holders       已接收的占位符位置列表
           * @return {Function}   繼續(xù)柯里化的函數(shù) 或 最終結(jié)果
           */
          function _curry(fn,length,holder,args,holders){
           return function(..._args){
           //將參數(shù)復(fù)制一份,避免多次操作同一函數(shù)導(dǎo)致參數(shù)混亂
           let params = args.slice();
           //將占位符位置列表復(fù)制一份,新增加的占位符增加至此
           let _holders = holders.slice();
           //循環(huán)入?yún)ⅲ芳訁?shù) 或 替換占位符
           _args.forEach((arg,i)=>{
           //真實(shí)參數(shù) 之前存在占位符 將占位符替換為真實(shí)參數(shù)
           if (arg !== holder && holders.length) {
               let index = holders.shift();
               _holders.splice(_holders.indexOf(index),1);
               params[index] = arg;
           }
           //真實(shí)參數(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)
           }
           }
          }

          驗(yàn)證一下:;

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

          let _ = {}; // 定義占位符
          let _fn = curry(fn,5,_);  // 將函數(shù)柯里化,指定所需的參數(shù)個(gè)數(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)完整實(shí)現(xiàn)了一個(gè) curry 函數(shù)~~

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


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

          淺拷貝:只拷貝一層,更深層的對(duì)象級(jí)別的只拷貝引用

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

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

          let obj={
           id:1,
           name:'Tom',
           msg:{
           age:18
           }
          }
          let o={}
          //實(shí)現(xiàn)深拷貝  遞歸    可以用于生命游戲那個(gè)題對(duì)二維數(shù)組的拷貝,
          //但比較麻煩,因?yàn)橐阎囟际侵担苯訌?fù)制就行,無(wú)需判斷
          function deepCopy(newObj,oldObj){
               for(var k in oldObj){
                   let item=oldObj[k]
                   //判斷是數(shù)組?對(duì)象?簡(jiǎn)單類(lèi)型?
                   if(item instanceof Array){
                       newObj[k]=[]
                       deepCopy(newObj[k],item)
                   }else if(item instanceof Object){
                       newObj[k]={}
                       deepCopy(newObj[k],item)
                   }else{  //簡(jiǎn)單數(shù)據(jù)類(lèi)型,直接賦值
                       newObj[k]=item
                   }
               }
          }

          8. 手寫(xiě)call, apply, bind


          手寫(xiě)call

          Function.prototype.myCall=function(context=window){  // 函數(shù)的方法,所以寫(xiě)在Fuction原型對(duì)象上
           if(typeof this !=="function"){   // 這里if其實(shí)沒(méi)必要,會(huì)自動(dòng)拋出錯(cuò)誤
              throw new Error("不是函數(shù)")
           }
           const obj=context||window   //這里可用ES6方法,為參數(shù)添加默認(rèn)值,js嚴(yán)格模式全局作用域this為undefined
           obj.fn=this      //this為調(diào)用的上下文,this此處為函數(shù),將這個(gè)函數(shù)作為obj的方法
           const arg=[...arguments].slice(1)   //第一個(gè)為obj所以刪除,偽數(shù)組轉(zhuǎn)為數(shù)組
           res=obj.fn(...arg)
           delete obj.fn   // 不刪除會(huì)導(dǎo)致context屬性越來(lái)越多
           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'}) //打出來(lái)的是 Spike

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

          Function.prototype.myApply=function(context){  // 箭頭函數(shù)從不具有參數(shù)對(duì)象!!!!!這里不能寫(xiě)成箭頭函數(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]

          手寫(xiě)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) // '家里蹲大學(xué)'
          }
          var result = bar.bind(foo, 'An') //預(yù)置了部分參數(shù)'An'
          result(22, '家里蹲大學(xué)') //這個(gè)參數(shù)會(huì)和預(yù)置的參數(shù)合并到一起放入bar中

          簡(jiǎn)單版本

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

          new失敗的原因:

          例:

          // 聲明一個(gè)上下文
          let thovino = {
           name: 'thovino'
          }

          // 聲明一個(gè)構(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')  //實(shí)際上orange放到了thovino里面
          console.log('instance:', instance) // {}

          生成的實(shí)例是個(gè)空對(duì)象

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

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

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

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

          可new可繼承版本

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

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

          9. 手動(dòng)實(shí)現(xiàn)new


          new的過(guò)程文字描述:

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

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

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

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

          細(xì)節(jié):

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

          10. 手寫(xiě)promise(常見(jiàn)promise.all, promise.race)


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

          class MyPromise {
           // 構(gòu)造函數(shù)接收一個(gè)執(zhí)行回調(diào)
           constructor(executor) {
               this._status = STATUS.PENDING // Promise初始狀態(tài)
               this._value = undefined // then回調(diào)的值
               this._resolveQueue = [] // resolve時(shí)觸發(fā)的成功隊(duì)列
               this._rejectQueue = [] // reject時(shí)觸發(fā)的失敗隊(duì)列
              
           // 使用箭頭函數(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 // 儲(chǔ)存當(dāng)前值,用于then回調(diào)
                      
                       // 執(zhí)行resolve回調(diào)
                       while (this._resolveQueue.length) {
                           const callback = this._resolveQueue.shift()
                           callback(value)
                       }
                   }
               }
               //把resolve執(zhí)行回調(diào)的操作封裝成一個(gè)函數(shù),放進(jìn)setTimeout里,以實(shí)現(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()時(shí)立即執(zhí)行executor,并傳入resolve和reject
               executor(resolve, reject)
           }

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

            // then 返回一個(gè)新的promise
            return new MyPromise((resolve, reject) => {
              const resolveFn = value => {
                try {
                  const x = onFulfilled(value)
                  // 分類(lèi)討論返回值,如果是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. 手寫(xiě)原生AJAX


          步驟

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

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

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

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

          promise實(shí)現(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('請(qǐng)求出錯(cuò)')
                  }
                }
              }
              xhr.send()  //發(fā)送hppt請(qǐng)求
            })
            return p
          }
          let url = '/data.json'
          ajax(url).then(res => console.log(res))
            .catch(reason => console.log(reason))

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


          防抖:

          function debounce(fn, delay) {
               if(typeof fn!=='function') {
                  throw new TypeError('fn不是函數(shù)')
               }
               let timer; // 維護(hù)一個(gè) timer
               return function () {
                   var _this = this; // 取debounce執(zhí)行作用域的this(原函數(shù)掛載到的對(duì)象)
                   var args = arguments;
                   if (timer) {
                      clearTimeout(timer);
                   }
                   timer = setTimeout(function () {
                      fn.apply(_this, args); // 用apply指向調(diào)用debounce的對(duì)象,相當(dāng)于_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 仍然不接受類(lèi)數(shù)組對(duì)象。如果傳入類(lèi)數(shù)組對(duì)象,它們會(huì)拋出異常。
                timer = null; // 在delay后執(zhí)行完fn之后清空timer,此時(shí)timer為假,throttle觸發(fā)可以進(jìn)入計(jì)時(shí)器
              }, delay)
            }
          }

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

          13. 手寫(xiě)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ù)實(shí)現(xiàn)一秒鐘輸出一個(gè)數(shù)


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

          15. 創(chuàng)建10個(gè)標(biāo)簽,點(diǎn)擊的時(shí)候彈出來(lái)對(duì)應(yīng)的序號(hào)?


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

          聲明:文章著作權(quán)歸作者所有,如有侵權(quán),請(qǐng)聯(lián)系小編刪除。


          往期推薦


          大廠面試過(guò)程復(fù)盤(pán)(微信/阿里/頭條,附答案篇)
          面試題:說(shuō)說(shuō)事件循環(huán)機(jī)制(滿(mǎn)分答案來(lái)了)
          專(zhuān)心工作只想搞錢(qián)的前端女程序員的2020

          最后


          • 歡迎加我微信,拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...

          • 歡迎關(guān)注「前端Q」,認(rèn)真學(xué)前端,做個(gè)專(zhuān)業(yè)的技術(shù)人...

          點(diǎn)個(gè)在看支持我吧
          瀏覽 80
          點(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>
                  美国A级毛片 | h片网站免费 | www.成人高清 | 毛片入口| 国产女人叫床高潮大片免费 |