一文瞭解JavaScript中call/apply/bind的使用
前言
在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
💡bind() 函數是什麼?
bind()
方法創建一個新的函數,在 bind()
被調用時,這個新函數的 this
被指定為 bind()
的第一個參數,而其餘參數將作為新函數的參數,供調用時使用。
3.1 語法
func.bind(thisArg[, arg1[, arg2[, ...]]]) // thisArg 為調用綁定函數時作為 this 參數傳遞給目標函數的值, 如果使用 new 運算符構造綁定函數,忽略該值 // arg1, arg2為當目標函數被調用時,被預置入綁定函數的參數列表中的參數。 // 其返回值為一個原函數的拷貝,並擁有指定的 this 值和初始參數
3.2 流程圖
想要實現bind
函數,即需實現兩個特點:一為返回一個函數;二為可以傳入參數。
所以我們可從以下幾點入手:
- 通過使用`call`或者`apply`實現 `this`的指定;
- 實現在`bind`的時候可以傳參,在執行返回函數時傳參;
- 判斷是否使用 `new`操作符來確定`this`指向。
話不多說,下面就是bind
函數的流程圖:
3.3 代碼實現
Function.prototype.bind = function (thisArg, ...argsArray) { if (typeof this !== "function") { throw new TypeError( "Function.prototype.bind was called on which is not a function" ); } if (thisArg === undefined || thisArg === null) { thisArg = window; } else { thisArg = Object(thisArg); } const func = this; const bound = function (...boundArgsArray) { let isNew = false; // 如果 func 不是構造器,直接使用 instanceof 將出錯,所以需要用 try...catch 包裹 try { isNew = this instanceof func; } catch (error) {} return func.apply(isNew ? this : thisArg, argsArray.concat(boundArgsArray)); }; const Empty = function () {}; Empty.prototype = this.prototype; bound.prototype = new Empty(); return bound; };
4.全文總結
call、apply與bind有什麼區別?
- calll、apply 與 bind 都用於
this
綁定,但 call、apply 函數在改變this指向的同時還會執行函數;而 bind 函數在改變this後返回一個全新的綁定函數。 - bind 屬於硬綁定,返回的綁定函數的this指向不能再通過 bind、apply 或 call 修改,即
this
被永久綁定;call 與 apply 隻適用於當前調用,一次調用後就結束。 - call 和 apply 功能完全相同,但call 從第二個參數後的所有參數都是原函數的參數;而 apply 隻接受兩個參數,第二個參數必須是數組,該數組包含著原函數的參數列表。
到此這篇關於一文瞭解JavaScript中call/apply/bind的使用的文章就介紹到這瞭,更多相關JavaScript call,apply,bind內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 使用JS簡單實現apply、call和bind方法的實例代碼
- JavaScript中的this例題實戰總結詳析
- JS中call(),apply(),bind()函數的區別與用法詳解
- Javascript動手實現call,bind,apply的代碼詳解
- JS 函數的 call、apply 及 bind 超詳細方法