Ahonn's BlogAhonn's Blog

apply, call 与 bind 的区别

#JavaScript#前端

最近在看 《JavaScript 高级程序设计》,也就是所谓的高程3。正好看到 Function 部分,看到了几个熟悉的字眼,apply()call()bind()。好像是有些面试题里面有,遂搞清楚了记录下。

apply() 与 call()

每个函数都包含两个非继承的方法:apply()call()。两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内 this 对象的值。这是书中的说法,大概意思就是 apply()call() 这两个方法是用来改变函数中 this 的指向的。而 apply()call() 的区别就在与接收参数的方式。

apply() 方法接收两个参数,一个是在其中运行函数的作用域,函数中的 this 的值,另一个是参数数组。 而 call() 方法的第一个参数跟 apply() 相同,不同的是除了第一个参数,后面的其他参数将会直接传递给函数。

function sum(num1, num2) {
  return num1 + num2;
}

// 使用 apply()
function applySum(num1, num2) {
  return sum.apply(this, arguments);
}

// 使用 call()
function callSum(num1, num2) {
  return sum.call(this, num1, num2);
}

alert(applySum(1, 2)); // 3
alert(callSum(1, 2)); // 3

这里使用 apply()call() 的效果都是一样的,不同的只是除了 this 的指向之外的参数的传递形式不同。在不给函数传递参数的时候则完全一样,当不知道需要传递的函数参数的个数时,使用 apply() 更好。

实际上 apply()call() 的用法并不只是这些。他们可以扩充函数赖以运行的作用域,即可以改变函数运行时的上下文。

name = "unknown";
var person = {
  name = "ahonn";
}

function sayName() {
  console.log(this.name);
}

sayName() // unknown
// 函数中的 this 为全局环境
sayName.apply(this); // unknown
// 函数中的 this 指向了 person
sayName.apply(person) // ahonn

上面的例子中的 apply() 换成 call() 的话效果相同,因为我这样只给 apply() 传了第一个参数。

bind()

bind() 方法与 apply()call() 相似,同样是可以改变函数内 this 的指向。但与 apply(), call() 不同的是,bind() 方法会创建一个新函数,称为绑定函数,当调用绑定函数时,函数会以创建时的第一个参数 this 作为函数运行的作用域。

name = "unknown";
var person = {
  name = "ahonn";
}

function sayName() {
  console.log(this.name);
}

var sayAhonnName = sayName.bind(person);

// 函数中的 this 指向 person
sayAhonnName() // ahonn

也就是说,bind() 方法可以绑定一个上下文到函数中,产生一个新的绑定函数。这样就可以在需要的时候去运行有指定 this 对象的函数。

使用变量保存 this 的做法:

var foo = {
  bar: 1,
  eventBind: function() {
    var _this = this;
    $('.someClass').on('click', function(event) {
      console.log(_this.bar);
    });
  }
}

因为在 JavaScript 中函数也是对象,使用如果不使用 _this 来保存上下文环境的话,在绑定的事件函数中将找不到 bar 这个值,因为上下文环境发生了改变。更优雅的做法就是使用 bind() 来完成。

var foo = {
    bar : 1,
    eventBind: function(){
        $('.someClass').on('click',function(event) {
            console.log(this.bar);
        }.bind(this));
    }
}

这样就把 $('.someClass').on('click',function(event) {} 外的上下文环境给绑定到了函数内了。