call
模擬實(shí)現(xiàn)
var foo = {
value: 1
};
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.call(foo, 'Tom', 20);
// Tom
// 20
// 1
分析:
- call改變了this的指向,指向了foo
- bar函數(shù)執(zhí)行
步驟:
- 函數(shù)設(shè)置為對象屬性
- 執(zhí)行函數(shù)
- 刪除函數(shù)
模擬實(shí)現(xiàn):
Function.prototype.call2 = function(context) {
var context = context || window;
context.fn = this;
// var args = Array.prototype.slice.call(arguments, 1);
// for(var i = 1, len = arguments.length; i < len; i++) {
// args.push('arguments[' + i + ']')
// }
// var result = eval('context.fn(' + args + ')');
var args = [...arguments].slice(1);
var result = context.fn(...args);
delete context.fn;
return result;
}
// 測試
var value = 2;
var foo = {
value: 1
};
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
bar.call2(null); // 2
console.log(bar.call2(foo, 'Tom', 20));
// 1
// Object {
// value: 1,
// name: 'Tom',
// age: 20
// }
apply
模擬實(shí)現(xiàn)
與call區(qū)別只是傳參不同
Function.prototype.apply2 = function(context) {
var context = context || window;
context.fn = this;
var result;
if(!arguments[1]) {
result = context.fn();
} else {
result = context.fn(...arguments[1])
}
delete context.fn;
return result;
}
// 測試
var value = 2;
var foo = {
value: 1
};
function bar(name, age) {
console.log(this.value);
return {
value: this.value,
name: name,
age: age
}
}
//bar.apply2(null); // 2
console.log(bar.apply2(foo, ['Tom', 20]));
// 1
// Object {
// value: 1,
// name: 'Tom',
// age: 20
// }
bind
模擬實(shí)現(xiàn)
分析:
- 返回一個(gè)函數(shù)
- 可以傳入?yún)?shù)
模擬實(shí)現(xiàn)版本1:
Function.prototype.bind2 = function (context) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
return function () {
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply(context, args.concat(bindArgs));
}
}
// 測試
var foo = {
value: 1
};
function bar(name, age) {
console.log(this.value);
console.log(name);
console.log(age);
}
var bindFoo = bar.bind2(foo);
console.log(bindFoo()); // 1
var bindFoo2 = bar.bind2(foo, 'Tom');
bindFoo2(20);
// 1
// Tom
// 20
此處忽略了bind還有一個(gè)特點(diǎn)瓷马,就是
一個(gè)綁定函數(shù)也能使用new操作符創(chuàng)建對象:這種行為就像把原函數(shù)當(dāng)成構(gòu)造器拴还。提供的 this 值被忽略跨晴,同時(shí)調(diào)用時(shí)的參數(shù)被提供給模擬函數(shù)欧聘。
也就是說當(dāng) bind 返回的函數(shù)作為構(gòu)造函數(shù)的時(shí)候,bind 時(shí)指定的 this 值會失效端盆,但傳入的參數(shù)依然生效怀骤。舉個(gè)例子:
var value = 2;
var foo = {
value: 1
};
function bar(name, age) {
this.habit = 'shopping';
console.log(this.value);
console.log(name);
console.log(age);
}
bar.prototype.friend = 'kevin';
var bindFoo = bar.bind2(foo, 'daisy');
var obj = new bindFoo('18');
// undefined
// daisy
// 18
console.log(obj.habit);
console.log(obj.friend);
// shopping
// kevin
分析:使實(shí)例obj可以根據(jù)原型鏈找到構(gòu)造函數(shù)bar的值
1.obj.proto = bindFoo.prototype
2.bindFoo.prototype.proto = bar.prototype
模擬實(shí)現(xiàn)版本2:
Function.prototype.bind2 = function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fBound = function () {
// 當(dāng)作為構(gòu)造函數(shù)時(shí),this 指向?qū)嵗烂睿藭r(shí)結(jié)果為 true蒋伦,將綁定函數(shù)的 this 指向該實(shí)例,可以讓實(shí)例獲得來自綁定函數(shù)的值
var bindArgs = Array.prototype.slice.call(arguments);
return self.apply( this instanceof fNOP ? this : context, args.concat(bindArgs));
}
// 修改返回函數(shù)的 prototype焚鹊,使:fBound.prototype.__proto__ = fNOP.prototype = this.prototype 實(shí)例就可以繼承綁定函數(shù)的原型鏈中的值
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
}