call、bind 和 apply 的介绍
三者的作用都很类似,都是改变函数的 this 指向
但是 call 和 apply 用来改变 this 并立即调用函数;bind 用来返回一个永久绑定 this 的新函数。
实例:
展开 / 收起示例代码
1 2 3 4 5 6
| function greet(greeting, punctuation) { console.log(greeting + ', ' + this.name + punctuation); }
const person = { name: 'Alice' }; const anotherPerson = { name: 'Bob' };
|
call 改变 this 并立即调用
1 2 3
| greet.call(person, 'Hello', '!');
|
apply 改变 this 并立即调用(调用参数不同)
1 2 3
| greet.apply(person, ['Hi', '!!!']);
|
bind 改变 this,但不会立即调用 → 返回一个新函数
1 2 3 4
| const greetBob = greet.bind(anotherPerson, 'Hey'); greetBob('?');
|
手写call
接下来将会手写 call apply 和 bind
1 2 3 4 5 6 7 8 9 10 11 12
| Function.prototype.mycall = function (ctx, ...args) { const key = Symbol() ctx = ctx === undefined || ctx === null ? globalThis : Object(ctx) ctx[key] = this var result = ctx[key](...args); delete ctx[key]
return result }
|
整体看下来其实很简单,就是将调用函数的this指向放到ctx里。接下来进行简单测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function fn(a, b) {
return a + b }
const res1 = fn.mycall({}, 2, 3) const res2 = fn.mycall(null, 2, 3) const res3 = fn.mycall(undefined, 2, 3) const res4 = fn.mycall(1, 2, 3) console.log("res1", res1); console.log("res2", res2); console.log("res3", res3); console.log("res4", res4);
|
下面是测试结果
1 2 3 4
| res1 5 res2 5 res3 5 res4 5
|
手写apply
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Function.prototype.myapply = function (ctx, args) { const key = Symbol() ctx = ctx === undefined || ctx === null ? globalThis : Object(ctx) ctx[key] = this let result
if (args) { args = Array.prototype.slice.call(args) result = ctx[key](...args); } else { result = ctx[key]() } delete ctx[key]
return result }
|
apply 与 call 比较类似。主要区别就是传入参数的不同。从此区别来更改 call 的写法就可以实现
测试
1 2 3 4 5 6 7 8 9 10
| const res5 = fn.myapply({}, [2, 3]) const res6 = fn.myapply(null, [2, 3]) const res7 = fn.myapply(undefined, [2, 3]) const res8 = fn.myapply(1, [2, 3]) const applyempty = fn.myapply({}, [2, 3]) console.log("res5", res5); console.log("res6", res6); console.log("res7", res7); console.log("res8", res8); console.log("applyempty", applyempty);
|
结果为:
1 2 3 4 5
| res5 5 res6 5 res7 5 res8 5 applyempty 5
|
手写bind
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Function.prototype.myBind = function (context) { var args = [...arguments].slice(1), fn = this; return function Fn() { return fn.myapply( this instanceof Fn ? this : context, args.concat(...arguments) ); }; };
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13
| function testbind(a, b, c) { console.log(a, b, c);
return a + b + c
}
const newfn = testbind.myBind({}, 1) console.log("newfn2", newfn(2, 5)); console.log("newfn2", newfn(3, 7));
|
结果为: