原生實現call、apply方法
大學的時候,一位大佬跟我說,讓我用原生寫一下call、apply方法,我沒寫。直到現在。。。。拖延症真的不是吹的。
call
先來看官方解釋:
call()方法呼叫一個函式,其具有一個指定的this值和分別地提供的引數(引數的列表)。
也就是說使用call方法,你可以指定被呼叫函式的this值,並且給他傳引數。
接下來我們自己實現一個call方法。
1、模擬如何指定this的值
先來看一段程式碼
function func() { console.log(this.value) } var obj = { value: 233 } func.call(obj); //233
就是在呼叫func的時候,我們設定了當前this為obj,所以輸出的this.value 就是233。不多說了。
試想一下,call方法是要改變this的值,我們可以給obj物件加一個屬性fn,把func函式賦值給它,呼叫obj.fn,this自然就指向物件obj了,也起到了呼叫func函式的作用。所以我們可以這麼實現:
1. 給物件obj新增fn屬性,並賦值func函式
2、呼叫它–> obj.fn()
3、delete obj.fn
ok,現在我們已經實現瞭如何指定的this的值。function func() { console.log(this.value) } var obj = { value: 233 } Function.prototype.mycall = function(obj) { obj.fn = this; //這裡的this就是呼叫mycall的func函式 obj.fn(); delete.fn; } func.mycall(obj); //233
2、實現可傳參
Function.prototype.mycall = function(obj) {
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
obj.fn = this;
eval('obj.fn('+ args +')’);
delete obj.fn;
}
多說一句,我們拿到的args陣列其實是[‘arguments[0]’, ‘arguments[1]’,…]這樣子的,如果我們直接去拿arguments[0]的值的話,你測試一下傳一個字串引數,像這樣,func.mycall(obj, ‘dd’);就會報錯,告訴你dd is not defined。 因為eval會把字串解析成一個變數。
這裡遇到了一個問題,如何給一個函式傳遞不定參? 或者說怎麼把不定長的陣列轉換成多個引數傳遞給函式??
三種辦法: eval、apply和es6 的解構語法
1、eval('obj.fn('+ args +')');
2、obj.fn.apply(obj, args);
3、obj.fn(…args);//es6解構語法
3、完善小細節
到這裡,我們基本已經實現了call方法的功能,還有兩個點需要注意:
– 如果第一個引數為null,this指向window
– 函式呼叫是有返回值的
呈上終極程式碼。
Function.prototype.mycall = function(obj) {
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
obj = obj || window;
obj.fn = this;
var result = eval('obj.fn('+ args +')’);
delete obj.fn;
return result;
}
apply
講完了call方法,我們來說一下apply方法,apply方法和call方法非常類似。同樣,先來看一下官方的解釋:apply()方法呼叫一個函式,其具有一個指定的this值,以及作為一個數組(或類陣列物件)提供的引數。 注意:call()方法的作用和 apply() 方法類似,只有一個區別,就是 call()方法接受的是若干個引數的列表,而apply()方法接受的是一個包含多個引數的陣列。
好,我們知道apply和call方法的區別就在於,call是傳引數列表,apply是傳一個數組。話不多說,開始寫程式碼。
Function.prototype.myapply = function(obj, arr) {
obj = obj || window;
obj.func = this;
var result;
if(!arr) {
result = obj.func();
} else {
var args = [];
for (var i = 0, len = arr.length; i < len; i++) {
args.push('arr[' + i + ']');
}
result = eval('obj.func('+ args +')');
}
delete obj.func;
return result;
}
本文參考掘金上面的一篇文章JavaScript深入之call和apply的模擬實現
寫這篇部落格呢,是為了整理一下call和apply的實現方法,也梳理一下自己的思路,能更好的理解和掌握。
如有錯誤,懇請指正。