本博客 hjy-xh,转载请申明出处
方法用途和异同点
先总结一下,这三个方法的主要作用是改变函数中this
的指向。
关于this的指向,需要记住this永远指向最后调用它的那个对象。
那三个方法使用起来的异同点是什么呢?
相同点:
- 第一个参数都是用来指定
this
,即上下文 - 都可以用后续参数进行传参
不同点:
bind
方法返回一个新的函数,call
、apply
则是立即调用call
和apply
基本类似,区别在于除了第一个参数之外的其它参数不同。前者接收若干个参数列表,后者接收一个包含多个参数的数组- 非严格模式下,
call
方法的第一个参数如果没传,或者是null
、undefined
时,this
指向全局对象
模拟实现
call
1
2
3
4
5
6
7
8
9
10
11
12
13Function.prototype.imitateCall = function (context) {
// 如果没有则默认为 window,即访问全局作用域对象
context = context || window;
// 获取调用call的函数,用this可以获取
context.fn = this;
// 截取作用域对象参数后面的参数
let args = [...arguments].slice(1);
// 执行调用函数,记录返回值
let result = context.fn(...args);
// 销毁调用函数,以免作用域污染
Reflect.deleteProperty(context, 'fn');
return result;
}apply
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15Function.prototype.myApply = function (context) {
// 如果没有则默认为 window,即访问全局作用域对象
context = context || window;
// 获取调用call的函数,用this可以获取
context.fn = this;
let result;
if (arguments[1]) {
result = context.fn(...arguments[1]);
} else {
result = context.fn();
}
// 销毁调用函数,以免作用域污染
Reflect.deleteProperty(context, 'fn');
return result;
}bind
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24Function.prototype.myBind = function (context) {
// 获取绑定时的传参
let args = [...arguments].slice(1),
// 定义中转构造函数,用于通过原型连接绑定后的函数和调用bind的函数
F = function () { },
// 记录调用函数,生成闭包,用于返回函数被调用时执行
self = this,
// 定义返回(绑定)函数
bound = function () {
// 合并参数,绑定时和调用时分别传入的
let finalArgs = [...args, ...arguments]
// 改变作用域,注:aplly/call是立即执行函数,即绑定会直接调用
// 这里之所以要使用instanceof做判断,是要区分是不是new xxx()调用的bind方法
return self.call((this instanceof F ? this : context), ...finalArgs)
};
// 将调用函数的原型赋值到中转函数的原型上
F.prototype = self.prototype;
// 通过原型的方式继承调用函数的原型
bound.prototype = new F();
return bound;
}
参考
Understanding JavaScript Bind ()
JavaScript’s Apply, Call, and Bind Methods are Essential for JavaScript Professionals
MDN