重新理解call、apply和bind

虽然我之前写过浅析 javascript 的 call 和 apply 方法,现在看来,对 call 和 apply 的理解并不深刻,故重新理解 call、apply 和 bind.

call 和 apply

call 和 apply 的用法这里就不再赘述,前面的文章已经讲过.而 call 和 apply 只是调用方法的不同,其他可以说是基本一致.下面看看 call 和 apply 的作用:

1、 改变 this 指向
2、 借用别的对象的方法
3、 调用函数(call 和 apply 方法都会使函数立即执行)

call 和 bind 的区别

bind 的用法详见Function.prototype.bind() ,这里只是一个简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
let obj = {
name: "jack",
};

let sayHi = function (name) {
console.log(`${this.name} say hi to ${name}`);
};

let say = sayHi.bind(obj);

say("tom"); // jack say hi to tom
sayHi.call(obj, "tom"); // jack say hi to tom

通过这个示例可以得出 call 和 call 的区别

1、 bind 方法不会立即执行,而是返回一个改变了上下文 this 后的函数
2、 call 是把第二个及以后的参数实参传入,而 bind 返回的函数中是将实参直接传入

怎么实现

在我接触 js 的时候就已经是 es5 了,es5 已经实现了 bind 方法,但更多的时候需要我们去理解,以及实现的方法.大概思路是在 Function 的原型上添加 bind 方法,然后在 bind 方法内部用 call 和 apply 来绑定 this 的指向.

看下面这段代码(摘自:JavaScript 中 apply 、call 的详解):

1
2
3
4
5
6
7
8
9
10
11
if (!Function.prototype.bind) {
Function.prototype.bind = function () {
var self = this, // 保存原函数
context = [].shift.call(arguments), // 保存需要绑定的this上下文
args = [].slice.call(arguments); // 剩余的参数转为数组
return function () {
// 返回一个新函数
self.apply(context, [].concat.call(args, [].slice.call(arguments)));
};
};
}

上面这段代码看起来没什么大不了的,但是细细体会的化,这段代码很有灵性.

[].shift、[].slice

咋一看,有点陌生呢, 可以理解为(new Array()).shift=>[].shift,那么这些数组方法都是继承于Array.prototype的,所以在控制台中打印Array.prototype.shift === [].shift的值为 true.

所以也可以理解为只是调用方式不同,一个是通过原型的方式直接在类上调用;一个是通过实例化,继承过来,然后再调用.这个两中调用方式的区别就很明了了,他们的 this 指向不同,在 [].slice 中,this 指向的是该实例 [],而在 Array.prototype.slice 中,this 指向的是 Array.prototype.而且从理论上来说,Array.prototype 的效率更高一下,毕竟少了一次实例化的过程.

那 arguments 为什么要通过 call 和 apply 使用数组的方法呢,因为 arguments 是一个伪数组对象.

相信看了这段有灵性的代码,上面的代码就不难理解了.虽然面试不会在问怎么实现一个 bind 方法,以及一些古老的方法了,但学习理解这些巧妙的、优雅的、有灵性的代码,才能加深我们对基础知识的理解,在将来才能更上一层楼.

参考文章:JavaScript 中 apply 、call 的详解
参考文章:js 中自己实现 bind 函数的方式

[越努力,越幸运!]