call
call() 方法在使用一個指定的 this 值和若干個指定的參數值的前提下調用某個函數或方法。
舉個栗子:
var foo ={
value:1
}
function bar(){
console.log(this.value)
}
bar.call(foo);//1
需要注意兩點:
- call改變了this的指向,指向到foo;
- 調用了bar函數
模擬實現第一步
試想當調用 call 的時候,把 foo 對象改造成如下:
//我們希望把bar里的this指向foo;那我們把bar放進foo里面能實現這個效果
var foo = {
value: 1,
bar: function() {
console.log(this.value)
}
};
foo.bar(); // 1
這樣我們就實現了this指向foo;
但是我們給foo添加了一個屬性才實現了這個效果,那我們用完可以刪除掉這個屬性。
模擬步驟可分為:
// 第一步
foo.fn = bar
// 第二步
foo.fn()
// 第三步
delete foo.fn
//fn 是對象的屬性名,反正最后也要刪除它,所以起成什么都無所謂。
根據這個思路,我們可以嘗試著去寫第一版的 call2 函數:
Function.Prototype.call2 = function(context){
//獲取調用call2的函數,用this
context.fn = this;
context.fn();
delete context.fn;
}
// 測試一下
var foo = {
value: 1
};
function bar() {
console.log(this.value);
}
bar.call2(foo); // 1
這樣我們就輕松模擬了call指定this指向的功能;
模擬實現第二步
call 函數還能給定參數執行函數。
舉個例子:
var foo = {
value: 1
};
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call(foo, 'Cherry', 18);
// Cherry
// 18
// 1
注意:傳入的參數并不確定,這可咋辦?
不急,我們可以從 Arguments 對象中取值,取出第二個到最后一個參數,然后放到一個數組里。
比如這樣:
// 以上個例子為例,此時的arguments為:
// arguments = {
// 0: foo,
// 1: 'Cherry',
// 2: 18,
// length: 3
// }
// 因為arguments是類數組對象,所以可以用for循環
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
// 執行后 args為 ["arguments[1]", "arguments[2]", "arguments[3]"]
不定長的參數問題解決了,我們接著要把這個參數數組放到要執行的函數的參數里面去。
eval('context.fn(' + args +')')
所以我們的第二版克服了兩個大問題,代碼如下:
// 第二版
Function.prototype.call2 = function(context) {
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
eval('context.fn(' + args +')');
delete context.fn;
}
// 測試一下
var foo = {
value: 1
};
function bar(name, age) {
console.log(name)
console.log(age)
console.log(this.value);
}
bar.call2(foo, 'Cherry', 18);
// Cherry
// 18
// 1
模擬實現第三步
模擬代碼已經完成 80%,還有兩個小點要注意:
1.this 參數可以傳 null,當為 null 的時候,視為指向 window
2.函數是可以有返回值的!
解決方法:
// 第三版
Function.prototype.call2 = function (context) {
var context = context || window;
context.fn = this;
var args = [];
for(var i = 1, len = arguments.length; i < len; i++) {
args.push('arguments[' + i + ']');
}
var result = eval('context.fn(' + args +')');
delete context.fn
return result;
}
// 測試一下
var value = 2;
var obj = {
value: 1
}
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.call(null); // 2
console.log(bar.call2(obj, 'Cherry', 18));
// 1
// Object {
// value: 1,
// name: 'Cherry',
// age: 18
// }
到此,我們完成了 call 的模擬實現。
文章非原創,有侵權請告知,文章出處:
https://github.com/mqyqingfeng/Blog/issues/11