“随心所欲”指定 this 的绑定对象

有的时候,函数的调用者(this关键字的绑定对象)并不是你期望的对象。面对这样情况,我们希望可以自定义this的绑定对象。在 javascript 中可以是用call apply bind方法来指定 this 关键字的绑定对象。  

a. call 方法

先看个例子:

eg-1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var 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方法来指定了,函数speakFullNamethis关键字的绑定者,所以打印的结果就是,被绑定对象的fullName

  关于call方法的参数,在eg-1的例子中调用call方法时,只给它传了一个参数。如果想传入多个参数,可以这样:func.call(obj, arg1, arg2, ....),第一个参数是 this关键字要绑定的对象,其它的参数则是被调用函数所需要的参数。下面的语法介绍是引用于 MDN

语法
fun.call(thisArg[, arg1[, arg2[, ...]]])

参数
thisArg

  • fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正this值,如果这个函数处于非严格模式下,则指定为nullundefinedthis值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。

arg1, arg2, ...

  • 指定的参数列表。

来看的例子:
eg-2:

1
2
3
4
5
6
7
8
9
10
11
12
13
var 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-1eg-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值,如果这个函数处于非严格模式下,则指定为nullundefinedthis值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。

argsArray

  • 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给fun函数。如果该参数的值为nullundefined,则表示不需要传入任何参数。

 

c. bind 方法

  1. 通过使用bind方法,可以返回一个新的函数,而这个函数的this关键字绑定的对象和参数可以通过bind方法来,在函数调用前预先指定。

  • bind方法会创建一个新函数,称为绑定函数.当调用这个绑定函数时,绑定函数会以创建它时传入bind方法的第一个参数作为this,传入bind方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数.

eg-4:

1
2
3
4
5
6
7
8
9
10
function add(b) {
console.log(this.a + b);
}
 
var obj = {
a: 'ai'
};
var bindedAdd = add.bind(obj);
 
bindedAdd('kin'); //=> aikin

  • 在上面的例子中,add函数通过调用bind方法,返回了一个绑定了obj对象的函数,并将返回的函数赋值给了binedAdd变量,这时函数binedAddthis关键字的绑定对象就是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
13
var 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)时,打印出来的scoreundefined。因为在函数体内调用回调函数时, 没有指定回调函数的调用者,也就是回调函数的this的绑定对象为全局对象(window)。

  • 当调用printScore(obj.getScore.bind(obj))时,函数体内的回调函数的this关键字的绑定对象为obj。所以this.score的值为objscore

 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 ...

参考