本内容来自《你不晓得的JavaScript(上卷)》,做了简朴的总结。
this关键字是javascript最庞杂的机制之一。它是一个很迥殊的关键字,被自动定义在所有的函数作用域中。然则纵然黑白常有履历的javascript开发者也很难说出他究竟指向什么。本节将分三个部份解说javascript中的this:
为何要应用this
两种罕见的关于this的误会
this究竟是什么
一、为何要应用this
在书中经由历程两段代码的对照来讲明为何要应用this。第一段代码以下:这段代码在差别的上下问对象(me和you)中重复应用函数identify()和speak(),不必针对差别的对象编写差别版本的函数。
function identify(){
return this.name.toUpperCase();
}
function speak(){
var greeting = "hello, I am " + identify.call(this);
console.log(greeting);
}
var me = {
name: "zhou"
}
var you = {
name: "reader"
}
identify.call(me); //ZHOU
identify.call(you); //READER
speak.call(me); // hello, I am ZHOU
speak.call(you); // hello, I am READER
假如不应用this, 这段代码该如何写呢?那就须要给identify()和speak()显现传入一个上下文对象
function identify(cxt){
return cxt.name.toUpperCase();
}
function speak(cxt){
var greeting = "hello, I am " + identify(cxt);
console.log(greeting);
}
identify(you); //READER
speak(me); //hello, I am ZHOU
对照发明:this供应了额一种更文雅的体式格局来隐式“通报”一个对象援用。由于,跟着你应用的形式愈来愈庞杂,显式通报上下文对象会让代码变得愈来愈杂沓。因而,经由历程应用this能够将API设想的越发简约而且易于复用。
二、两种罕见的关于this的误会
误会1.指向函数自身
把this邃晓为指向函数自身,这个揣摸从英语语法角度是说的通的。
罕见的在函数内部援用自身的状况有:递归或者是一个在第一次被挪用后自身打仗绑定的事宜处理器。 JavaScript的新手开发者(比方说我)一般以为:既然能够把函数看做一个对象,那就能够在挪用函数时存储状况(属性的值)。
如今我们来剖析这个形式,让人人看到this并不像所想的那样指向函数自身。下面这段代码,我们想要纪录函数foo被挪用的次数:
function foo(num){
console.log("foo: " + num);
this.count++; //纪录foo被挪用的次数
}
foo.count = 0;
for(var i=0; i<10; i++){
if(i > 5){
foo(i)
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
console.log(foo.count); // 0 为何会是0呢?
foo()函数中的console.log语句产生了4条输出,证实foo()确切被挪用了4次,但foo.count仍然是0,所以,仅从字面上来邃晓,this指向函数自身是毛病的。那末,题目的缘由是什么呢?
foo()函数是在全局作用域下实行的,this在这段代码中实在指向window,而且这段代码在无意中建立了一个全局变量count,他的值为NaN。
那末,碰到如许的题目很多的开发者(包括我),不会深切的思索为何this的行动和预期的不一致,也不会回复那些很难处理,但非常重要的题目。这里供应了三种处理这个题目的要领,个中前两种要领回避了this的寄义和事情道理。代码以下:
要领一 应用作用域(词法作用域)要领,该要领处理了我们碰到的题目,然则却没有直面this。
function foo(num){
console.log("foo: " + num);
data.count++; //纪录foo被挪用的次数
}
var data ={
count: 0
};
for(var i=0; i<10; i++){
if(i > 5){
foo(i);
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
console.log(data.count);// 4
要领二 建立一个指向函数对象的词法标识符(变量)来援用它。一样该要领依旧回避了this的题目。
function foo(num){
console.log("foo: " + num);
foo.count++; // foo指向它自身
}
foo.count = 0;
for(var i=0; i<10; i++){
if(i > 5){
foo(i);
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
console.log(foo.count);// 4
要领三 既然我们晓得this在foo函数实行时指向了别处,那末我们须要做的就是强迫this指向foo函数.
function foo(num){
console.log("foo: " + num);
this.count++;
}
foo.count = 0;
for(var i=0; i<10; i++){
if(i > 5){
foo.call(foo, i); //应用call()能够确保this指向函数自身
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
console.log(foo.count);// 4
此次我们从this的角度处理了题目。
误会2.指向函数作用域
第二种罕见的误会是:this指向函数作用域。这个题目有点庞杂,由于在某种状况下它是准确的,但在其他状况下他倒是毛病的。
但一定要邃晓,this在任何状况下都不指向函数的作用域,在javascript内部作用域和对象确切很类似,可见的标识符都是他的属性,但作用域“对象”没法经由历程JavaScript代码接见,它存在于JavaScript引擎内部
在文中给出了如许一段代码:
function foo(){
var a = 2;
this.bar();
}
function bar(){
console.log(this.a);
}
foo(); // ReferenceError: a is not defined
这段代码试图经由历程this联通foo()和bar()的词法作用域,从而让bar()能够接见foo()作用域的变量a。but it’s impossible!
三、this究竟是个什么玩意?
经由历程消除以上各种的误会,我们能够得出以下结论:
this是在运行时举行绑定的,并非在编写时绑定的。
this的绑定和函数声明的位置没有关系,只取决于函数的挪用体式格局。
详细细节是:当一个函数被挪用时,会建立一个运动纪录(也称实行上下文(context))。这个纪录会包括一些信息,比方: 函数在那里被挪用(挪用栈), 函数的挪用体式格局, 传入的参数等,而this就是这个纪录的一个属性,会在函数实行历程中被用到。
四、总结
跟着你应用的形式愈来愈庞杂,显式通报上下文对象会让代码变得愈来愈杂沓。因而,经由历程应用this隐式通报能够将API设想的越发简约而且易于复用。
this既不指向函数自身,也不指向函数的作用域。
this实际上是函数被挪用时发作的绑定,它的指向完整取决于函数在那里被挪用。