学过 js 的朋友都知道 bind、call、apply 都是 function 原型上的方法,每一个函数作为 function 的实例都可以调用这些方法来改变函数中 this 的指向,那么它们是如何使用的呢,本文着重帮助你理解它们的使用区别及如何用原生 js 实现这三个方法。
bind、call、apply 的使用区别
传递参数类型的区别
在使用 bind、call、apply 的时候,我们不仅可以传递一个上下文作为 this 的新指向,也可以传递一些参数在函数中进行使用。
使用 bind、call 的时候,我们需要把参数一个个添加到上下文后面。
const func = function() {};
func.call(context, '参数1', '参数2', ...);
func.bind(context, '参数1', '参数2', ...)();
使用 apply 的时候,我们只能把参数以数组的形式添加到上下文后面。
const func = function() {};
func.apply(context, ['参数1', '参数2', ...]);
tip:这并不意味着使用 bind、call 时不能以数组的方式传递参数,我们可以使用扩展运算符来传递数组。
const func = function() {};
let arr = ['参数1', '参数2', ...];
func.call(context, ...arr);
func.bind(context, ...arr)();
ps:一般来说,call 的性能要比 apply 好那么一点。
返回值类型的区别
如果仔细看上面的几行代码的话,可以发现在每个 bind 方法后面都加了一对小括号,这是为什么呢? 这是因为 bind 方法在执行后的返回值是一个新函数(可以定义变量对其接收),而 call、apply 执行后返回的是执行结果。所以我们需要加一对小括号来执行新函数。
手写 bind、call、apply 方法
具体实现步骤请看注释
// 实现 js bind 方法
Function.prototype.myBind = function(context) {
// 判断 this 指向,如果不为函数则报错
if (typeof this !== 'function') {
throw new TypeError('error');
}
let _this = this; // 保存执行函数
let args = [...arguments].slice(1); // 取出执行函数的剩余参数列表
return function() {
_this.apply(context, args.concat(...arguments)); // 改变 this 指向并拼接参数列表
}
}
// 实现 js call 方法
Function.prototype.myCall = function(context) {
// 判断 this 指向,如果不为函数则报错
if (typeof this !== 'function') {
throw new TypeError('error');
}
let context = context || window; // 如果不传入参数,默认将 this 指向 window 对象
context.fn = this; // 将执行函数赋值为上下文对象的属性
let args = [...arguments].slice(1); // 取出执行函数的剩余参数列表
let result = context.fn(...args); // 执行函数
delete context.fn; // 删除上下文属性,避免内存泄漏
return result; // 返回执行结果
}
// 实现 js apply 方法
Function.prototype.myApply = function(context) {
// 判断 this 指向,如果不为函数则报错
if (typeof this !== 'function') {
throw new TypeError('error');
}
let context = context || window; // 如果不传入参数,默认将 this 指向 window 对象
context.fn = this; // 将执行函数赋值为上下文对象的属性
let result;
// 判断是否有剩余参数,如果有则带参执行,没有则直接执行
if (arguments[1]) {
result = context.fn(...arguments[1]);
} else {
result = context.fn();
}
delete context.fn; // 删除上下文属性,避免内存泄漏
return result; // 返回执行结果
}
你们的阅读是对我最大的鼓励!