原生JS實現call,apply,bind函數


1. 前言

使用原生JS實現callapply函數,充分了解其內部原理。callapply都是為了解決改變this的指向。作用都相同,只是傳參的方式不同。除了第一個參數外,call可以接受一個參數列表,apply只接受一個參數數組。

2. call函數

2.1 描述

call() 方法使用一個指定的 this 值和單獨給出的一個或多個參數來調用一個函數。

2.2 語法

fun.call(thisArg, arg1, arg2, ...)

2.3 參數

  • thisArg:可選的。在 fun 函數運行時指定的 this需要注意的是,指定的 this 值並不一定是該函數執行時真正的 this 值,如果這個函數在非嚴格模式下運行,則指定為 nullundefinedthis 值會自動指向全局對象(瀏覽器中就是 window 對象),同時值為原始值(數字,字符串,布爾值)的 this 會指向該原始值的自動包裝對象。
  • arg1, arg2, ...:可選的。指定的參數列表。

2.4 返回值

使用調用者提供的 this 值和參數調用該函數的返回值。若該方法沒有返回值,則返回 undefined

2.5 實現

Function.prototype.myCall = function (context){
  if (typeof this !== 'function') {
     throw new TypeError('Error')
  }
  if (context === null || context === undefined) {
     context = window    // 指定為 null 和 undefined 的 this 值會自動指向全局對象(瀏覽器中為window)
  } else {
     context = Object(context) // 值為原始值(數字,字符串,布爾值)的 this 會指向該原始值的實例對象
  }

  context.fn = this
  //通過參數偽數組將context后面的參數取出來
  let args = [...arguments].slice(1)
  let result = context.fn(...args)
  //刪除 fn
  delete context.fn
  return result
}

實現思路:

  • 首先,判斷調用mycall的是不是函數,如果不是,則直接拋出異常;
  • 接着,判斷是否傳入了第一個參數context,也就是要指定的this值,如果沒有傳入,則默認為window全局對象;
  • 然后,誰將來調用mycall,那么this就是誰,將其賦給context.fn;
  • 然后,通過參數偽數組將context后面的參數取出來,並傳給context.fn獲得執行結果result
  • 最后,刪除掉context.fn,並將result返回;

3. aplly函數

3.1 描述

apply() 方法調用一個具有給定this值的函數,以及作為一個數組(或類似數組對象)提供的參數。

3.2 語法

func.apply(thisArg, [argsArray])

3.3 參數

  • thisArg:可選的。在 func 函數運行時使用的 this 值。請注意,this可能不是該方法看到的實際值:如果這個函數處於非嚴格模式下,則指定為 nullundefined 時會自動替換為指向全局對象,原始值會被包裝。
  • argsArray:可選的。一個數組或者類數組對象,其中的數組元素將作為單獨的參數傳給 func 函數。如果該參數的值為 nullundefined,則表示不需要傳入任何參數。從ECMAScript 5 開始可以使用類數組對象。

3.4 返回值

調用有指定**this**值和參數的函數的結果。

3.5 實現

Function.prototype.myApply = function (context) {
   if (typeof this !== 'function') {
      throw new TypeError('Error')
   }
   if (context === null || context === undefined) {
        context = window     // 指定為 null 和 undefined 的 this 值會自動指向全局對象(瀏覽器中為window)
    } else {
        context = Object(context) // 值為原始值(數字,字符串,布爾值)的 this 會指向該原始值的實例對象
    }

    context.fn = this
    let result 
    //判斷是否存在第二個參數
    //如果存在就將第二個參數也展開
    if(arguments[1]) {
        result = context.fn(...arguments[1])
    } else {
        result = context.fn()
    }
    delete context.fn
    return result
}

實現思路:

  • 首先,判斷調用myapply的是不是函數,如果不是,則直接拋出異常;
  • 接着,判斷是否傳入了第一個參數context,也就是要指定的this值,如果沒有傳入,則默認為window全局對象;
  • 然后,誰將來調用myapply,那么this就是誰,將其賦給context.fn;
  • 然后,判斷是否傳入了第二個參數,如果傳入了則將其使用展開運算符...傳給context.fn獲得執行結果result,如果沒有傳入,則直接調用context.fn獲得執行結果result
  • 最后,刪除掉context.fn,並將result返回;

4. bind函數

4.1 描述

bind()方法創建一個新的函數,在bind()被調用時,這個新函數的thisbind的第一個參數指定,其余的參數將作為新函數的參數供調用時使用。

4.2 語法

func.bind(thisArg[, arg1[, arg2[, ...]]])

4.3 參數

  • thisArg:調用綁定函數時作為this參數傳遞給目標函數的值。 如果使用new運算符構造綁定函數,則忽略該值。當使用bindsetTimeout中創建一個函數(作為回調提供)時,作為thisArg傳遞的任何原始值都將轉換為object。如果bind函數的參數列表為空,執行作用域的this將被視為新函數的thisArg

  • arg1, arg2, ...:當目標函數被調用時,預先添加到綁定函數的參數列表中的參數。

4.4 返回值

返回一個原函數的拷貝,並擁有指定的this值和初始參數。

4.5 實現

Function.prototype.mybind = function (context) {
    if (typeof this !== 'function') {
        throw new TypeError('Error')
    }
    const _this = this 
    const args = [...arguments].slice(1)
    //返回一個函數
    return function F () {
        if (this instanceof F) {           // this是否是F的實例 也就是返回的F是否通過new調用
            return new _this(...args, ...arguments)
        }
        return _this.apply(context,args.concat(...arguments))
    }
}

實現思路:

  • 首先,判斷調用mybind的是不是函數,如果不是,則直接拋出異常;
  • 接着,誰將來調用mybind,那么this就是誰,將其賦給_this,緩存一下;
  • 然后,通過參數偽數組將context后面的參數(預先添加到綁定函數的參數)取出來,記作args,
  • 然后,返回一個函數,並判斷如果使用new運算符構造綁定函數,則忽略傳入的第一個參數context,並將預先添加到綁定函數的參數args和將來傳入新函數的參數arguments分別通過展開運算符...依次傳入給調用mybind的調用者,並將結果返回。
  • 最后,如果不是使用new運算符構造綁定函數,則對調用者使用apply方法,將傳入的第一個參數以及預先添加到綁定函數的參數args和將來傳入新函數的參數arguments分別通過展開運算符...依次傳入給調用mybind的調用者,並將結果返回;

(完)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM