call,apply和bind

call和apply

call和aplly作用完全一样,都是在特定的上下文中调用函数,或者说改变函数内部的this指向;区别仅在于接收参数的方式不同。

var dog = {
  name: "dog"
};
var cat = {
  name: "cat"
};
var sayName = function (age, gender) {
  console.log(this.name + "," + age + "," + gender)
};
sayName(); //undefined
sayName.call(dog, 2, "male"); //dog,2,male
sayName.call(cat, 3, "female"); //cat,3,female
sayName.apply(dog, [2, "male"]); //dog,2,male
sayName.apply(cat, [3, "female"]); //cat,3,female

我们在全局环境定义了一个sayName方法,调用该方法时会将当前环境的name属性(this.name)显示到控制台上。当我们在全局环境调用sayName()时,由于全局环境并没有this.name属性(注意,浏览器window对象上有name属性,这里指node环境),因此显示了undefined。
使用call或者apply,就可以改变函数的执行环境,或者说改变函数内部的this指向。例如sayName.call(dog),表示将sayName内部的this指向了dog对象,因此this.name显示为"dog"。当我们调用sayName.call(cat)时,sayName内部的this又指向了cat对象。
可以看出来,call与apply的作用完全相同,区别仅在于接收参数的方式不同。

fn.call(context, arg1, arg2, ...)
fn.apply(context, [arg1, arg2, ...])

call和apply接收的第一个参数都是函数的运行环境。使用call时传递给函数的参数必须逐个传入,而使用apply时函数的参数应该是一个数组,或者类数组对象(如arguments对象)。至于使用call还是apply,完全取决于采用哪种方法给函数传递参数更方便。在不需要给函数传递参数的情况下,使用哪个方法都无所谓。

bind

bind也可以改变函数内部的this指向,区别在于bind不会直接调用该函数,而是返回一个绑定了this的函数,由你来决定什么时候调用。

var dog = {
  name: "dog"
};
var cat = {
  name: "cat"
};
var sayName = (function (age, gender) {
  console.log(this.name + "," + age + "," + gender)
}).bind(dog, 3, "male");

sayName.call(cat, 2, "female"); //dog,3,male

我们创建sayName函数时,使用bind将该函数的this指向了dog对象,同时还绑定了传入函数的参数。
现在我们调用sayName.call(cat, 2, "female"),发现不论是this对象,还是传入函数的参数,都是之前被绑定的值。
bind像call一样接受多个参数,第一个参数为context(即内部this值),后面的参数依次为函数接收的参数

用途

call和apply适合用在只需要临时改变函数运行环境的地方,可以用来共享一些方法。最经典、实用的莫过于Array.prototype.slice.call(arguments)了,利用call可以直接在类数组对象上调用Array的slice方法,方便的将类数组对象转换成真正的数组对象。

bind方法适合用在将某个函数以值的形式传递,同时该函数必须在特定的环境中执行的时候,例如一些事件处理程序,回调函数和setTimeout()、setInterval()等。例如:

var alarmClock = {
  time: function () {
    return new Date();
  },
  alarm: function () {
    console.log(this.time())
  }
};
setInterval(alarmClock.alarm,1000); //TypeError: this.time is not a function
setInterval(alarmClock.alarm.bind(alarmClock),1000); //Thu May 18 2017 12:32:44 GMT+0800 (中国标准时间)

当直接将alarmClock.alarm传递给setInterval()时,因为alarmClock.alarm调用的环境是setInterval,即全局环境,其中没有time方法,于是TypeError。
通过使用bind(alarmClock),就可以将alarmClock.alarm的运行环境绑定到alarmClock对象上,这样每次执行都可以获得正确的结果。

另外,bind还可以用在函数柯里化。

总结

call,apply和bind其实很简单,无非就是改变了函数内部的this指向而已,区别仅仅是使用的方法和场景不同。

posted @ 2017-05-18 13:33  huanglei-  阅读(605)  评论(0编辑  收藏  举报