乐趣区

js中callapply和bind到底有什么区别

介绍

在 js 中,每个函数的原型都指向 Function.prototype 对象(js 基于原型链的继承)。因此,每个函数都会有 apply,call,和 bind 方法,这些方法继承于 Function。
它们的作用是一样的,都是用来改变函数中 this 的指向。

使用方法

apply 的用法可以表示如下:
A.apply(B, [x, y, z]);
此方法可以改变函数 A 的 this 指向,使之指向函数 B。第二个参数传的是一个函数参数列表的数组形式。

call 的用法和 apply 差不多,就只有传参方式不一样。类似于这样 :
A.apply(B, x, y, z)
可以把多个参数分开来传,而不是像 apply 一样,需要把所有参数放到一个数组里边传进来。

bind 的传参方式和 call 一样,只不过它的不同之处是,apply 和 call 方法调用之后会立即执行,而 bind 方法调用之后会返回一个新的函数,它并不会立即执行,需要我们手动执行。

举例

var name = "佚名";
var age = 20;
//global.name
//global.age

var p1 = {
    name: "张三",
    age: 12,
    func: function(){console.log(` 姓名:${this.name}, 年龄:${this.age}`)
    }
}

var p2 = {
    name: "李四",
    age: 15
}

p1.func();  // 姓名: 张三, 年龄:12
p1.func.call();  // 姓名: 佚名, 年龄:20
p1.func.apply(p2);  // 姓名: 李四, 年龄:15
p1.func.bind(p2)(); // 姓名: 李四, 年龄:15
  1. 当直接调用 p1.func 方法时,this 指向的是当前 p1 这个对象,因此 name 和 age 都是它本身对象的属性值。
  2. 当使用 call 方法,传入的第一个参数为空时,在浏览器环境下,this 指向 window;在 node 环境下,this 指向 global。读者可自行把 name 和 age 改为 global.name 和 global.age 验证一下。
  3. 使用 apply 方法时,把 p2 传进去,相当于把函数的 this 指向 p2 对象,因此,此时打印出来的属性都是 p2 对象的属性
  4. 使用 bind 方法时,会生成一个新的函数对象,因此需要手动执行一下这个函数(即在函数末尾加个括号执行)。

总结

例子讲完,call,apply 和 bind 的区别就已经很清楚了。它们就是为了改变 this 的上下文而存在。因此,有时候你会看到这样的用法。为了不改变 this 的指向,通常会在函数后边加上 bind(this),如下

function f(){}.bind(this)

这样的话,就不会出现莫名其妙的 this 指向问题了。明明我想在函数里边调用此函数所属对象的某个属性时,为什么用 this 却访问不到呢。(写 js 的新手肯定会遇到过这种问题)

其实,从 es6 开始,已经支持箭头函数了,我建议大家用箭头函数,就不会出现 this 指向的问题了。

另外,使用 call,apply 还可以实现函数的继承。在 es6 的 class 没有出现之前,就需要借助它们来实现继承关系。

退出移动版