首頁 > 軟體

一文了解JavaScript中call/apply/bind的使用

2022-07-25 10:00:38

前言

在JavaScript中,經常會通過call / apply / bind 函數來改變this的指向,詳情可看一文帶你瞭解this指向,今天我們來研究下這三個函數的實現。

1. call

call()函數是什麼?

call() 方法使用一個指定的 this 值和單獨給出的一個或多個引數來呼叫一個函數。也就是說call() 改變了this指向並執行了函數

1.1 語法

func.call(thisArg, arg1, arg2, ...)
// thisArg為在 func 函數執行時使用的 this 值
// arg1, arg2等為指定的參數列
// 其返回值為呼叫有指定 this 值和引數的函數的結果

1.2 流程圖

一般來說,我們要模擬實現call,可以分為以下幾個步驟:

  • 將函數設定為物件的屬性, 當物件為null或undefined, 設為window物件
  • 取出函數執行所需引數,執行該函數
  • 如果函數存在返回值,在返回後刪除該函數

以下就是call()方法實現的流程圖:

1.3 程式碼實現

Function.prototype.call = function (thisArg, ...argsArray) {
  if (typeof this !== "function") {
    throw new TypeError(
      "Function.prototype.call was called on which is not a function"
    );
  }

  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }

  // 將 func 放入 thisArg 內,這樣在呼叫 thisArg[func] 時 this 自然就指向了 thisArg
  const func = Symbol("func");
  thisArg[func] = this;

  let result;

  if (argsArray.length) {
    result = thisArg[func](...argsArray);
  } else {
    result = thisArg[func]();
  }

  delete thisArg[func];

  return result;
};

2. apply

apply()函數是什麼?

apply() 方法呼叫一個具有給定 this 值的函數,以及以一個陣列(或一個類陣列物件)的形式提供的引數。同call()的功能,改變this指向的同時執行了函數。

2.1 語法

func.apply(thisArg, [argsArray]);
// thisArg為在 func 函數執行時使用的 this 值
// arg1, arg2等為指定的參數列
// 其返回值為呼叫有指定 this 值和引數的函數的結果

2.2 流程圖

apply()方法實現的流程基本與call的實現流程沒有太多差異,只需要對函數引數陣列進行判斷展開即可。

以下是apply()函數的流程圖:

2.3 程式碼實現

Function.prototype.apply = function (thisArg, argsArray) {
  if (typeof this !== "function") {
    throw new TypeError(
      "Function.prototype.apply was called on which is not a function"
    );
  }

  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }

  // 將 func 放入 thisArg 內,這樣在呼叫 thisArg[func] 時 this 自然就指向了 thisArg
  const func = Symbol("func");
  thisArg[func] = this;

  let result;

  if (argsArray && typeof argsArray === "object" && "length" in argsArray) {
    // 此處使用 Array.from 包裹讓其支援形如 { length: 1, 0: 1 } 這樣的類陣列物件,直接對 argsArray 展開將會執行出錯
    result = thisArg[func](...Array.from(argsArray));
  } else if (argsArray === undefined || argsArray === null) {
    result = thisArg[func]();
  } else {
    throw new TypeError("CreateListFromArrayLike called on non-object");
  }
  delete thisArg[func];
  return result;
};

3. bind


IT145.com E-mail:sddin#qq.com