有的时候,函数的调用者(this
关键字的绑定对象)并不是你期望的对象。面对这样情况,我们希望可以自定义this
的绑定对象。在 javascript 中可以是用call apply bind
方法来指定 this 关键字的绑定对象。
a. call 方法
先看个例子:
eg-1:1
2
3
4
5
6
7
8
9
10
11
12
13
14var me = {
fullName: 'aikin'
};
var you = {
fullName: 'you'
};
function speakFullName() {
console.log(this.fullName);
}
speakFullName(); //=> undefined
speakFullName.call(me); //=> aikin
speakFullName.call(you); //=> you
在第一次直接调用函数
speakFullName()
后,函数的this
关键字的绑定对象为全局对象,也就是window
对象,同时在window
对象中没有fullName
这个属性,所以undefined
也就产生了。而第二次和第三次使用
call
方法来指定了,函数speakFullName
的this
关键字的绑定者,所以打印的结果就是,被绑定对象的fullName
。
关于call
方法的参数,在eg-1的例子中调用call
方法时,只给它传了一个参数。如果想传入多个参数,可以这样:func.call(obj, arg1, arg2, ....)
,第一个参数是 this
关键字要绑定的对象,其它的参数则是被调用函数所需要的参数。下面的语法介绍是引用于 MDN。
语法
fun.call(thisArg[, arg1[, arg2[, ...]]])
参数
thisArg
- 在
fun
函数运行时指定的this
值。需要注意的是,指定的this
值并不一定是该函数执行时真正this
值,如果这个函数处于非严格模式下,则指定为null
和undefined
的this
值会自动指向全局对象(浏览器中就是window
对象),同时值为原始值(数字,字符串,布尔值)的this
会指向该原始值的自动包装对象。
arg1, arg2, ...
- 指定的参数列表。
来看的例子:
eg-2:1
2
3
4
5
6
7
8
9
10
11
12
13var me = {
fullname : 'aikin',
friends : ['yuki', 'bobi']
};
function addFriend(friend) {
this.friends.push(friend);
}
addFriend.call(me, 'kuki');
console.log(me.friends);
//=> ["yuki", "bobi", "kuki"]
b. apply() 方法
apply
方法和 call
方法的区别在于参数的不同。
该方法的作用和
call
方法类似,只有一个区别就是,call
方法接受的是若干个参数的列表,而apply
方法接受的是一个包含多个参数的数组(或类数组对象)。
在eg-1和eg-2的例子同样可以使用apply
来实现,如下:
eg-3:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// eg-1 的例子,使用 apply 方法
var me = {
fullName: 'aikin'
};
var you = {
fullName: 'you'
};
function speakFullName() {
console.log(this.fullName);
}
speakFullName(); //=> undefined
speakFullName.apply(me); //=> aikin
speakFullName.apply(you); //=> you
//eg-2的例子,使用 apply 方法
var me = {
fullname : 'aikin',
friends : ['yuki', 'bobi']
};
function addFriend(friend) {
this.friends.push(friend);
}
addFriend.apply(me, ['kuki']);
console.log(me.friends);
//=> ["yuki", "bobi", "kuki"]
下面引用一下 MDN 对该方法语法的介绍。
语法
fun.apply(thisArg[, argsArray])
参数
thisArg
- 在
fun
函数运行时指定的this
值。需要注意的是,指定的this
值并不一定是该函数执行时真正this
值,如果这个函数处于非严格模式下,则指定为null
和undefined
的this
值会自动指向全局对象(浏览器中就是window
对象),同时值为原始值(数字,字符串,布尔值)的this
会指向该原始值的自动包装对象。
argsArray
- 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给
fun
函数。如果该参数的值为null
或undefined
,则表示不需要传入任何参数。
c. bind 方法
1. 通过使用bind
方法,可以返回一个新的函数,而这个函数的this
关键字绑定的对象和参数可以通过bind
方法来,在函数调用前预先指定。
bind
方法会创建一个新函数,称为绑定函数.当调用这个绑定函数时,绑定函数会以创建它时传入bind
方法的第一个参数作为this
,传入bind
方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数.
eg-4:1
2
3
4
5
6
7
8
9
10function add(b) {
console.log(this.a + b);
}
var obj = {
a: 'ai'
};
var bindedAdd = add.bind(obj);
bindedAdd('kin'); //=> aikin
- 在上面的例子中,
add
函数通过调用bind
方法,返回了一个绑定了obj
对象的函数,并将返回的函数赋值给了binedAdd
变量,这时函数binedAdd
的this
关键字的绑定对象就是obj
。当调用binedAdd()
时产生的结果,就相对于add.call(obj, 'kin')
。
关于bind
的语法:
语法
fun.bind(thisArg[, arg1[, arg2[, ...]]])
参数
thisArg
- 当绑定函数被调用时,该参数会作为原函数运行时的this指向,当使用
new
操作符调用绑定函数时,该参数无效。
arg1, arg2, ...
- 当绑定函数被调用时,这些参数加上绑定函数本身的参数会按照顺序作为原函数运行时的参数。
2. 当要将一个对象的方法,作为回调函数直接传递给高阶函数(可以理解为用函数作为参数的函数)时。如果希望,传入的对象的方法,的调用者,还为该对象,就可以使用bind
方法,让该对象绑定到回调函数的this
关键字。
eg-5:1
2
3
4
5
6
7
8
9
10
11
12
13var obj = {
score : 90,
getScore : function() {
return this.score;
}
};
function printScore(callback) {
console.log('我的分数:' + callback());
}
printScore(obj.getScore); //=> 我的分数:undefined
printScore(obj.getScore.bind(obj)); //=>我的分数:90
在eg-5的例子中,
printScore
函数接收一个回调函数作为参数。obj
对象的getScore
方法返回的是该对象的score
。当调用
printScore(obj.getScore)
时,打印出来的score
为undefined
。因为在函数体内调用回调函数时, 没有指定回调函数的调用者,也就是回调函数的this
的绑定对象为全局对象(window
)。当调用
printScore(obj.getScore.bind(obj))
时,函数体内的回调函数的this
关键字的绑定对象为obj
。所以this.score
的值为obj
的score
。
3. 还可以使用bind
实现“柯里化”(本人理解就是为函数预先传入某些参数)。
eg-6:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//bind 的第一个参数为 null 时
function sum (x, y, z) {
return x + y + z;
};
var sumBind = sum.bind(null, 3, 6);
sumBind(8); //=> 17
//bind 的第一个参数为 obj 时
function average (age1, age2) {
return (this.age + age1 + age2) / 3;
}
var obj = {
age: 23
};
var averageBind = average.bind(obj, 21);
averageBind(22); //=> 22
这三方法都好有趣啊,让this
绑定变得如此“随心所欲”。
this
is so cool. coding is so funny ...
参考
- MDN, call
- MDN, apply
- MDN, bind
- David Herman, 《Effective JavaScript》