call() 方法呼叫一個函式, 其具有一個指定的this值和分別地提供的引數(引數列表)。
apply() 方法呼叫一個具有給定this值的函式,以及作為一個陣列(或類陣列物件)提供的引數。
bind() 方法建立一個新的函式,在呼叫時設定this關鍵字為提供的值。並在呼叫新函式時,將給定引數列表作為原函式的引數序列的前若干項。
以上內容源自MDN
下面我用自己的理解說一下call,apply及bind都做了啥
foo.call(obj,argument1,augument2,argument3)複製程式碼
以上程式碼的作用:
執行函式foo,並將其this指定為obj,後面的引數為呼叫foo時傳入的引數
foo.apply(obj,[argument1,augument2,argument3])複製程式碼
以上程式碼的作用:
執行函式foo,並將其this指定為obj並將陣列
[
argument1,augument2,argument3]中的每一項作為呼叫foo時傳入的引數let bar = foo.bind(obj,argument1,augument2,argument3);
bar();複製程式碼
以上程式碼的作用:
返回一個繫結了this為obj,引數為argument1,augument2,argument3的函式,所以如果想要執行foo,需要把返回結果再執行一下
瞭解了方法的功能和原理,接下來讓我們分別實現它們
call的實現:
首先實現一個簡單的call方法
Function.prototype.call2 = function(obj){
obj.fn = this;
if(arguments.length>1){
obj.fn(...([...arguments].slice(1)));
}else{
obj.fn();
}
delete obj.fn;
}複製程式碼
以上程式碼僅僅實現了this的繫結以及函式呼叫傳參,但是忽略了函式的返回值,而且預設把當前函式賦給了obj.fn,而沒有考慮obj.fn存在的情況,改進後的程式碼如下:
Function.prototype.call2 = function(obj){
let _fn = "fn",result;
while (obj.hasOwnProperty(_fn)) {
_fn = "fn" + Math.random(); // 迴圈判斷並重新賦值
}
obj[_fn] = this;
if(arguments.length>1){
result = obj[_fn](...([...arguments].slice(1)));
}else{
result = obj[_fn]();
}
delete obj[_fn];
return result;
}
複製程式碼
這樣似乎完善了一些,但是還有一種情況需要考慮,即呼叫call方法沒有傳參的情況,或者第一個引數為null或者undefined
var name = 'window.name';
function foo(){
console.log(this);
console.log(this.name)
};
foo.call(); // window window.name
foo.call(1); // Number {1} undefined複製程式碼
通過上面的程式碼我們可以得出結論,如果呼叫call方法沒有傳入引數或者第一個引數為null或者undefined,會將函式的this繫結到window物件(注意如上測試程式碼,不可以使用let宣告name哦),而如果傳入的引數為基本資料型別,會將其轉換為物件
繼續完善後的call方法如下:
Function.prototype.call2 = function(obj){
obj = obj?Object(obj):window;
let _fn = "fn",result;
while (obj.hasOwnProperty(_fn)) {
_fn = "fn" + Math.random(); // 迴圈判斷並重新賦值
}
obj[_fn] = this;
if(arguments.length>1){
result = obj[_fn](...([...arguments].slice(1)));
}else{
result = obj[_fn]();
}
delete obj[_fn];
return result;
}
複製程式碼
而有了如上思考過程,實現apply就很簡單了,程式碼如下:
Function.prototype.apply2 = function(obj,arr){
obj = obj?Object(obj):window;
let _fn = "fn",result;
while (obj.hasOwnProperty(_fn)) {
_fn = "fn" + Math.random(); // 迴圈判斷並重新賦值
}
obj[_fn] = this;
if(arr){
result = obj[_fn](...arr);
}else{
result = obj[_fn]();
}
delete obj[_fn];
return result;
}
複製程式碼
bind實現:
Function.prototype.bind2 = function(obj){
obj = obj?Object(obj):window;
var myArguments = arguments,self = this;
if(arguments.length>1){
return function(){
self.apply(obj,[...myArguments].slice(1))
};
}else{
return function(){
self.apply(obj);
};
}
}
複製程式碼
bind的實現借用了apply方法,有興趣的朋友可以嘗試不借用apply實現bind
如果有錯誤或者不嚴謹的地方,請給予指正,十分感謝!