在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
都是调用的原函数。