區別
相同點:
- 都可以改變函式的this指向
- 第一個引數都是this要指向的物件,如果沒有這個引數或引數為undefined或null,則預設指向全域性window
- 都可以利用後續引數傳參
不同點:
- call可以傳入多個引數;apply需要傳入一個引數物件(陣列或類陣列);bind可以分開傳參,例如:const foo = fn.bind(this, 1, 2); foo(3, 4)
- call、apply會立即執行,bind不會立即執行,而是返回一個修改this後的函式
實現call
Function.prototype._call = function (thisArg, ...args) {
// this指向呼叫_call的函式
const fn = this
// 如果要繫結的物件為undefined或null,則預設指向全域性物件
thisArg = thisArg !== undefined && thisArg !== null ? Object(thisArg) : window
// 利用this的隱式繫結規則,將函式的this繫結到指定物件上
thisArg.fn = fn
const res = thisArg.fn(...args)
// 刪除新增的fn方法
delete thisArg.fn
return res
}
測試:
function foo(a, b) {
console.log(this.name, a, b)
}
const obj = { name: 'xiaoming' }
foo.call(obj, 20, 30) // xiaoming 20 30
foo._call(obj, 20, 30) // xiaoming 20 30
實現apply
apply和call差別不大,只是傳入函式引數的形式不同
完整版:
Function.prototype._apply = function (thisArg, arrArg = []) {
const fn = this
thisArg = (thisArg !== undefined && thisArg != null) ? Object(thisArg) : window
thisArg.fn = fn
const res = thisArg.fn(...arrArg)
delete thisArg.fn
return res
}
簡化版:
Function.prototype._apply = function (thisArg, arrArg = []) {
const fn = this;
return fn._call(thisArg, ...arrArg);
}
測試:
function foo(a, b) {
console.log(this.name, a, b)
}
const obj = { name: 'xiaohong' }
foo.apply(obj, [10, 20]) // xiaohong 10 20
foo._apply(obj, [10, 20]) // xiaohong 10 20
實現bind
完整版:
Function.prototype._bind = function (thisArg, ...args) {
const fn = this
thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
// 返回一個新函式作為繫結函式
return function (...otherArgs) {
thisArg.fn = fn
const res = thisArg.fn(...args, ...otherArgs)
delete thisArg.fn
return res
}
}
簡化版:
Function.prototype._bind = function (thisArg, ...args) {
const fn = this;
return function (...otherArgs) {
return fn._apply(thisArg, args.concat(otherArgs));
};
};
測試:
function foo(a, b) {
console.log(this.name, a, b)
}
const obj = { name: 'xiaoli' }
const res = foo.bind(obj, 30)
const res1 = foo._bind(obj, 30)
res(40) // xiaoli 30 40
res1(40) // xiaoli 30 40