区别

call、apply、bind 本质都是改变 this 的指向,不同点 call、apply 是直接调用函数,bind 是返回一个新的函数。callapply 就只有参数上不同。

区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。

bind

实现

  • 箭头函数的 this 永远指向它所在的作用域
  • 函数作为构造函数用 new 关键字调用时,不应该改变其 this 指向,因为 new绑定 的优先级高于 显示绑定硬绑定

代码实现

Function.prototype.myBind = function (thisArg, ...args) {
if (typeof this !== 'function') {
throw Error('must be a function');
}
// 拿到参数传给调用者
const self = this,
// 构建一个干净的函数,用于保存原函数的原型
nop = function () {},
// 绑定的函数
bound = function (...selfArgs) {
return self.apply(
// this instanceof nop, 判断是否使用 new 来调用 bound
// 如果是 new 来调用的话,this的指向就是其实例,
// 如果不是 new 调用的话,就改变 this 指向到指定的对象 o
this instanceof nop ? this : thisArg,
args.concat(selfArgs)
);
};

// 箭头函数没有 prototype,箭头函数this永远指向它所在的作用域
if (this.prototype) {
nop.prototype = this.prototype;
}
// 修改绑定函数的原型指向
bound.prototype = new nop();
return bound;
};

测试

const bar = function() {
console.log(this.name, arguments);
};

bar.prototype.name = 'bar';

const foo = {
name: 'foo'
};

const bound = bar.mybind(foo, 22, 33, 44);
new bound(); // bar, [22, 33, 44]
bound(); // foo, [22, 33, 44]

Call & Apply

Call

bind 是封装了 call 的方法改变了 this 的指向并返回一个新的函数,那么 call 是如何做到改变 this 的指向呢?原理很简单,在方法调用模式下,this 总是指向调用它所在方法的对象,this 的指向与所在方法的调用位置有关,而与方法的声明位置无关(箭头函数特殊)。先写一个小 demo 来理解一下下。

const foo = { name: 'foo' };

foo.fn = function() {
// 这里的 this 指向了 foo
// 因为 foo 调用了 fn,
// fn 的 this 就指向了调用它所在方法的对象 foo 上
console.log(this.name); // foo
};

我们可以利用this的这个特性来实现call

Function.prototype.myCall = function (thisArg, ...args) {
if (typeof this !== 'function') {
throw new TypeError('Error');
}
thisArg = thisArg || window;
// 将调用call函数的对象添加到thisArg的属性中
thisArg.fn = this;
const result = thisArg.fn(...args);
delete thisArg.fn;
return result;
};

Apply

Function.prototype.myApply = function (thisArg) {
if (typeof this !== 'function') {
throw new TypeError('Error');
}
thisArg = thisArg || window;
thisArg.fn = this;
const args = arguments[1];
const result = thisArg.fn(...args);
delete thisArg.fn;
return result;
};

测试

const bar = function () {
console.log(this.name, arguments);
};

bar.prototype.name = 'bar';

const foo = {
name: 'foo',
};

bar.myCall(foo, 1, 2, 3); // foo [1, 2, 3]
bar.myApply(foo, [1, 2, 3]); // foo [1, 2, 3]

完~


参考