在JavaScript中,函数运行时都具有特定的作用域,如下代码:
var name = 'Jack';
var person = {
name: 'Bob',
sayName: function(code, msg){
console.log(code, msg + this.name);
}
}
person.sayName(0, 'hello ');// 0 "hello Bob"
var newSay = person.sayName;
newSay(0, 'hello ');// 0 "hello Jack"
先声明一个name变量,然后声明一个person对象,person包含name和sayName属性。当直接在对象上进行方法的调用时:person.sayName(),函数的作用域遵循“谁调用就是谁”的原则,sayName的作用域(也就是this)指向的就是person。
当把person.sayName赋值给新变量newSay时,调用newSay()为直接的函数调用,此时函数的作用域变为了window。本质上来讲,newSay()相当于window.newSay(),所以同样也是遵循“谁调用就是谁”的原则。
但是函数有三个方法可以用来设置作用域,分别为apply、call、bind。
还是上面的例子,使用如下方式调用:
person.sayName.apply(window, [0, 'hello ']);//0 "hello Jack"
apply接收两个参数,第一个参数为函数运行时的作用域,也就是this的值,这样就可以人为的改变函数运行时的作用域了。
使用call方法也是一样:
person.sayName.call(window, 0, 'hello ');//0 "hello Jack"
区别在于apply接收一个数组用于函数的参数,而call需要把参数一一列出。
对于bind,来看下面的代码:
var newSay2 = person.sayName.bind(person);
newSay2(0, 'hello');//0 "hello Bob"
console.log(person.sayName === newSay);// true
console.log(person.sayName === newSay2);// false
bind也是改变了作用域,但bind创建了一个新的函数,这个函数具有和原函数相同的代码体。而apply和call都是调用的原函数。