在编写JavaScript应用的时候,我们经常会使用this
关键字。那么this
关键字究竟是怎样工作的?它的设计有哪些好的地方,有哪些不好的地方?本文带大家全面系统地认识这个老朋友。
小明正在跑步,他看起来很开心
这里的小明是主语,如果没有这个主语,那么后面的代词『他』将毫无意义。有了主语,代词才有了可以指代的事物。
类比到JavaScript的世界中,我们在调用一个对象的方法的时候,需要先指明这个对象,再指明要调用的方法。
1 2 3 4 5 6 7 8 |
var xiaoming = { name: 'Xiao Ming', run: function() { console.log(`${this.name} seems happy`); }, }; xiaoming.run(); |
在上面的例子中,第8行中的xiaoming
指定了run
方法运行时的主语。因此,在run
中,我们才可以用this
来代替xiaoming
这个对象。可以看到this
起了代词的作用。
同样的,对于一个JavaScript类,在将它初始化之后,我们也可以用类似的方法来理解:类的实例在调用其方法的时候,将作为主语,其方法中的this
就自然变成了指代主语的代词。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class People { constructor(name) { // 在用new关键字实例化一个对象的时候,相当于在说, // “创建一个People类实例(主语),它(this)的name是……” // 所以这里的this就是新创建的People类实例 this.name = name; } run() { console.log(`${this.name} seems happy.`) } } // new关键字实例化一个类 var xiaoming = new People('xiaoming'); xiaoming.run(); |
这就是我认为this关键字设计得精彩的地方!如果将调用方法的语句(上面代码的第16行)和方法本身的代码连起来,像英语一样读,其实是完全通顺的。
this
的绑定
句子的主语是可以变的,例如在下面的场景中,run
被赋值到小芳(xiaofang
)身上之后,调用xiaofang.run
,主语就变成了小芳!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var xiaofang = { name: 'Xiao Fang', }; var xiaoming = { name: 'Xiao Ming', run: function() { console.log(`${this.name} seems happy`); }, }; xiaofang.run = xiaoming.run; // 主语变成了小芳 xiaofang.run(); |
在这种情况下,句子还是通顺的。所以,非常完美!
但是如果小明很抠门,不愿意将run
方法借给小芳以后,this
就变成了小芳的话,那么小明要怎么做呢?他可以通过Function.prototype.bind让run
运行时候的this
永远为小明自己。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var xiaofang = { name: 'Xiao Fang', }; var xiaoming = { name: 'Xiao Ming', run: function() { console.log(`${this.name} seems happy`); }, }; // 将小明的run方法绑定(bind)后,返回的还是一个 // 函数,但是这个函数之后被调用的时候就算主语不是小明, // 它的this依然是小明 xiaoming.run = xiaoming.run.bind(xiaoming); xiaofang.run = xiaoming.run; // 主语虽然是小芳,但是最后this还是小明 xiaofang.run(); |
那么同一个函数被多次bind
之后,到底this
是哪一次bind
的对象呢?你可以自己尝试看看。
call
与apply
Function.prototype.call允许你在调用一个函数的时候指定它的this
的值。