解析 Call/Apply 原理,并手寫(xiě) Call/Apply 實(shí)現(xiàn)
本文轉(zhuǎn)載自微信公眾號(hào)「三分鐘學(xué)前端」,作者sisterAn 。轉(zhuǎn)載本文請(qǐng)聯(lián)系三分鐘學(xué)前端公眾號(hào)。
Function.prototype.call()
call() 方法調(diào)用一個(gè)函數(shù), 其具有一個(gè)指定的 this 值和多個(gè)參數(shù)(參數(shù)的列表)。
- func.call(thisArg, arg1, arg2, ...)
它運(yùn)行 func,提供的第一個(gè)參數(shù) thisArg 作為 this,后面的作為參數(shù)。
看一個(gè)簡(jiǎn)單的例子:
- function sayWord() {
- var talk = [this.name, 'say', this.word].join(' ');
- console.log(talk);
- }
- var bottle = {
- name: 'bottle',
- word: 'hello'
- };
- // 使用 call 將 bottle 傳遞為 sayWord 的 this
- sayWord.call(bottle);
- // bottle say hello
所以,call 主要實(shí)現(xiàn)了以下兩個(gè)功能:
- call 改變了 this 的指向
- bottle 執(zhí)行了 sayWord 函數(shù)
模擬實(shí)現(xiàn) call
模擬實(shí)現(xiàn) call 有三步:
- 將函數(shù)設(shè)置為對(duì)象的屬性
- 執(zhí)行函數(shù)
- 刪除對(duì)象的這個(gè)屬性
- Function.prototype.call = function (context) {
- // 將函數(shù)設(shè)為對(duì)象的屬性
- // 注意:非嚴(yán)格模式下,
- // 指定為 null 和 undefined 的 this 值會(huì)自動(dòng)指向全局對(duì)象(瀏覽器中就是 window 對(duì)象)
- // 值為原始值(數(shù)字,字符串,布爾值)的 this 會(huì)指向該原始值的自動(dòng)包裝對(duì)象(用 Object() 轉(zhuǎn)換)
- context = context ? Object(context) : window;
- context.fn = this;
- // 執(zhí)行該函數(shù)
- let args = [...arguments].slice(1);
- let result = context.fn(...args);
- // 刪除該函數(shù)
- delete context.fn
- // 注意:函數(shù)是可以有返回值的
- return result;
- }
Function.prototype.apply()
apply() 方法調(diào)用一個(gè)具有給定 this 值的函數(shù),以及作為一個(gè)數(shù)組(或[類(lèi)似數(shù)組對(duì)象)提供的參數(shù)。
- func.apply(thisArg, [argsArray])
它運(yùn)行 func 設(shè)置 this = context 并使用類(lèi)數(shù)組對(duì)象 args 作為參數(shù)列表。
例如,這兩個(gè)調(diào)用幾乎相同:
- func(1, 2, 3);
- func.apply(context, [1, 2, 3])
兩個(gè)都運(yùn)行 func 給定的參數(shù)是 1,2,3。但是 apply 也設(shè)置了 this = context。
call 和 apply 之間唯一的語(yǔ)法區(qū)別是 call 接受一個(gè)參數(shù)列表,而 apply 則接受帶有一個(gè)類(lèi)數(shù)組對(duì)象。
需要注意:Chrome 14 以及 Internet Explorer 9 仍然不接受類(lèi)數(shù)組對(duì)象。如果傳入類(lèi)數(shù)組對(duì)象,它們會(huì)拋出異常。
模擬實(shí)現(xiàn) apply
- Function.prototype.apply = function (context, arr) {
- context = context ? Object(context) : window;
- context.fn = this;
- let result;
- if (!arr) {
- result = context.fn();
- } else {
- result = context.fn(...arr);
- }
- delete context.fn
- return result;
- }