Javascript this 的一些學(xué)習(xí)總結(jié)
相信有C++、C#或Java等編程經(jīng)驗(yàn)的各位,對于this關(guān)鍵字再熟悉不過了。由于Javascript是一種面向?qū)ο蟮木幊陶Z言,它和C++、C#或Java一樣都包含this關(guān)鍵字,接下來我們將向大家介紹Javascript中的this關(guān)鍵字。
本文目錄
•全局代碼中的this
•函數(shù)中的this
•引用類型
•函數(shù)調(diào)用以及非引用類型
•引用類型以及this的null值
•函數(shù)作為構(gòu)造器被調(diào)用時(shí)this的值
•手動(dòng)設(shè)置函數(shù)調(diào)用時(shí)this的值
1.1.2 正文
由于許多面向?qū)ο蟮木幊陶Z言都包含this關(guān)鍵字,我們會(huì)很自然地把this和面向?qū)ο蟮木幊谭绞铰?lián)系在一起,this通常指向利用構(gòu)造器新創(chuàng)建出來的對象。而在ECMAScript中,this不僅僅只用來表示創(chuàng)建出來的對象,也是執(zhí)行上下文的一個(gè)屬性:
activeExecutionContext = {
// Variable object.
VO: {...},
this: thisValue
};
全局代碼中的this
// Global scope
// The implicit property of
// the global object
foo1 = "abc";
alert(foo1); // abc
// The explicit property of
// the global object
this.foo2 = "def";
alert(foo2); // def
// The implicit property of
// the global object
var foo3 = "ijk";
alert(foo3); // ijk
前面我們通過顯式和隱式定義了全局屬性foo1、foo2和foo3,由于this在全局上下文中,所以它的值是全局對象本身(在瀏覽器中是window object);接下來我們將介紹函數(shù)中的this。
函數(shù)中的this
當(dāng)this在函數(shù)代碼中,情況就復(fù)雜多了,并且會(huì)引發(fā)很多的問題。
函數(shù)代碼中this值的第一個(gè)特性(同時(shí)也是最主要的特性)就是:它并非靜態(tài)的綁定在函數(shù)上。
正如此前提到的,this的值是在進(jìn)入執(zhí)行上下文(Excution context)的階段確定的,并且在函數(shù)代碼中的話,其值每次都不盡相同。
然而,一旦進(jìn)入執(zhí)行代碼階段,其值就不能改變了。如果要想給this賦一個(gè)新的值是不可能的,因?yàn)樵谀菚r(shí)this根本就不是變量了。
接下來,我們通過具體的例子說明函數(shù)中的this。
首先我們定義兩個(gè)對象foo和person,foo包含一個(gè)屬性name,而person包含屬性name和方法say(),具體的定義如下:
// Defines foo object.
var foo = {
name: "Foo"
};
// Defines person object.
var person = {
name: "JK_Rush",
say: function() {
alert(this === person);
alert("My name is " + this.name);
}
};
person.say(); // My name is JK_Rush
// foo and person object refer to
// the same function say
foo.say = person.say;
foo.say(); // My name is Foo.
通過上面的代碼,我們發(fā)現(xiàn)調(diào)用person的say()方法時(shí),this指向person對象,當(dāng)通過賦值方式使得foo的say()方法指向peson中的say()方法時(shí)。我們調(diào)用foo的say()方法,發(fā)現(xiàn)this不是指向person對象,而不是指向foo對象,這究竟是什么原因呢?
首先,我們必須知道this的值在函數(shù)中是非靜態(tài)的,它的值確定在函數(shù)調(diào)用時(shí),具體代碼執(zhí)行前,this的值是由激活上下文代碼的調(diào)用者決定的,比如說,調(diào)用函數(shù)的外層上下文;更重要的是,this的值是由調(diào)用表達(dá)式的形式?jīng)Q定的,所以說this并非靜態(tài)的綁定在函數(shù)上。
由于this并非靜態(tài)地綁定在函數(shù)上,那么我們是否可以在函數(shù)中動(dòng)態(tài)地修改this的值呢?
// Defines foo object.
var foo = {
name: "Foo"
};
// Defines person object.
var person = {
name: "JK_Rush",
say: function() {
alert(this === person);
this = foo; // ReferenceError
alert("My name is " + this.name);
}
};
person.say(); // My name is JK_Rush
現(xiàn)在我們在方法say()中,動(dòng)態(tài)地修改this的值,當(dāng)我們重新執(zhí)行以上代碼,發(fā)現(xiàn)this的值引用錯(cuò)誤。這是由于一旦進(jìn)入執(zhí)行代碼階段(函數(shù)調(diào)用時(shí),具體代碼執(zhí)行前),this的值就確定了,所以不能改變了。
引用類型
前面我們提到this的值是由激活上下文代碼的調(diào)用者決定的,更重要的是,this的值是由調(diào)用表達(dá)式的形式?jīng)Q定的;那么表達(dá)式的形式是如何影響this的值呢?
首先,讓我們介紹一個(gè)內(nèi)部類型——引用類型,它的值可以用偽代碼表示為一個(gè)擁有兩個(gè)屬性的對象分別是:base屬性(屬性所屬的對象)以及該base對象中的propertyName屬性:
// Reference type.
var valueOfReferenceType = {
base: mybase,
propertyName : 'mybasepropertyName'
};
引用類型的值只有可能是以下兩種情況:
•當(dāng)處理一個(gè)標(biāo)識(shí)符的時(shí)候
•或者進(jìn)行屬性訪問的時(shí)候
標(biāo)識(shí)符其實(shí)就是變量名、函數(shù)名、函數(shù)參數(shù)名以及全局對象的未受限的屬性。
// Declares varible.
var foo = 23;
// Declares a function
function say() {
// Your code.
}
中間過程中,對應(yīng)的引用類型如下:
// Reference type.
var fooReference = {
base: global,
propertyName: 'foo'
};
var sayReference = {
base: global,
propertyName: 'say'
};
我們知道Javascript中屬性訪問有兩種方式:點(diǎn)符號(hào)和中括號(hào)符號(hào):
// Invokes the say method.
foo.say();
foo['say']();
由于say()方法是標(biāo)識(shí)符,所以它對應(yīng)于foo對象引用類型如下:
// Reference type.
var fooSayReference = {
base: foo,
propertyName: 'say'
};
我們發(fā)現(xiàn)say()方法的base屬性值為foo對象,那么它對應(yīng)的this屬性也將指向foo對象。
假設(shè),我們直接調(diào)用say()方法,它對應(yīng)的引用類型如下:
// Reference type.
var sayReference = {
base: global,
propertyName: 'say'
};
由于say()方法的base屬性值為global(通常來說是window object),那么它對應(yīng)的this屬性也將指向global。
函數(shù)上下文中this的值是函數(shù)調(diào)用者提供并且由當(dāng)前調(diào)用表達(dá)式的形式而定的。如果在調(diào)用括號(hào)()的左邊有引用類型的值,那么this的值就會(huì)設(shè)置為該引用類型值的base對象。 所有其他情況下(非引用類型),this的值總是null。然而,由于null對于this來說沒有任何意義,因此會(huì)隱式轉(zhuǎn)換為全局對象。
函數(shù)調(diào)用以及非引用類型
前面我們提到,當(dāng)調(diào)用括號(hào)左側(cè)為非引用類型的時(shí),this的值會(huì)設(shè)置為null,并最終隱式轉(zhuǎn)換為全局對象。
現(xiàn)在我們定義了一個(gè)匿名自執(zhí)行函數(shù),具體實(shí)現(xiàn)如下:
// Declares anonymous function
(function () {
alert(this); // null => global
})();
由于括號(hào)()左邊的匿名函數(shù)是非引用類型對象(它既不是標(biāo)識(shí)符也不屬于屬性訪問),因此,this的值設(shè)置為全局對象。
// Declares object.
var foo = {
bar: function () {
alert(this);
}
};
(foo.bar)(); // foo.
(foo.bar = foo.bar)(); // global?
(false || foo.bar)(); // global?
(foo.bar, foo.bar)(); // global
這里注意到四個(gè)表達(dá)式中,只有第一個(gè)表達(dá)式this是指向foo對象的,而其他三個(gè)表達(dá)式則執(zhí)行g(shù)lobal。
現(xiàn)在我們又有疑問了:為什么屬性訪問,但是最終this的值不是引用類型對象而是全局對象呢?
我們注意到表達(dá)式二是賦值(assignment operator),與表達(dá)式一組操作符不同的是,它會(huì)觸發(fā)調(diào)用GetValue方法(參見11.13.1中的第三步)。 最后返回的時(shí)候就是一個(gè)函數(shù)對象了(而不是引用類型的值了),這就意味著this的值會(huì)設(shè)置為null,最終會(huì)變成全局對象。
第三和第四種情況也是類似的——逗號(hào)操作符和OR邏輯表達(dá)式都會(huì)觸發(fā)調(diào)用GetValue方法,于是相應(yīng)地就會(huì)丟失原先的引用類型值,變成了函數(shù)類型,this的值就變成了全局對象了。
引用類型以及this的null值
對于前面提及的情形,還有例外的情況,當(dāng)調(diào)用表達(dá)式左側(cè)是引用類型的值,但是this的值卻是null,最終變?yōu)槿謱ο螅╣lobal object)。 發(fā)生這種情況的條件是當(dāng)引用類型值的base對象恰好為活躍對象(activation object)。
當(dāng)內(nèi)部子函數(shù)在父函數(shù)中被調(diào)用的時(shí)候就會(huì)發(fā)生這種情況,通過下面的示意代碼介紹活躍對象:
// Declares foo function.
function foo() {
function bar() {
alert(this); // global
}
// The same as AO.bar().
bar();
}
由于活躍對象(activation object)總是會(huì)返回this值為——null(用偽代碼來表示AO.bar()就相當(dāng)于null.bar()),然后,this的值最終會(huì)由null轉(zhuǎn)變?yōu)槿謱ο蟆?
當(dāng)函數(shù)調(diào)用包含在with語句的代碼塊中,并且with對象包含一個(gè)函數(shù)屬性的時(shí)候,就會(huì)出現(xiàn)例外的情況。with語句會(huì)將該對象添加到作用域鏈的最前面,在活躍對象的之前。 相應(yīng)地,在引用類型的值(標(biāo)識(shí)符或者屬性訪問)的情況下,base對象就不再是活躍對象了,而是with語句的對象。另外,值得一提的是,它不僅僅只針對內(nèi)部函數(shù),全局函數(shù)也是如此, 原因就是with對象掩蓋了作用域鏈中更高層的對象(全局對象或者活躍對象):
函數(shù)作為構(gòu)造器被調(diào)用時(shí)this的值
函數(shù)作為構(gòu)造函數(shù)時(shí),我們通過new操作符創(chuàng)建實(shí)例對象是,它會(huì)調(diào)用Foo()函數(shù)的內(nèi)部[[Construct]]方法;在對象創(chuàng)建之后,會(huì)調(diào)用內(nèi)部的[[Call]]方法,然后所有Foo()函數(shù)中this的值會(huì)設(shè)置為新創(chuàng)建的對象。
// Declares constructor
function Foo() {
// The new object.
alert(this);
this.x = 10;
}
var foo = new Foo();
foo.x = 23;
alert(foo.x); // 23
手動(dòng)設(shè)置函數(shù)調(diào)用時(shí)this的值
Function.prototype原型上定義了兩個(gè)方法,允許手動(dòng)指定函數(shù)調(diào)用時(shí)this的值。這兩個(gè)方法分別是:.apply()和.call()。這兩個(gè)方法都接受第一個(gè)參數(shù)作為調(diào)用上下文中this的值,而這兩個(gè)方法的區(qū)別是傳遞的參數(shù),對于.apply()方法來說,第二個(gè)參數(shù)接受數(shù)組類型(或者是類數(shù)組的對象,比如arguments), 而.call()方法接受任意多的參數(shù)(通過逗號(hào)分隔);這兩個(gè)方法只有第一個(gè)參數(shù)是必要的——this的值。
通過示例代碼介紹call()方法和apply()方法的使用:
var myObject = {};
var myFunction = function(param1, param2) {
//setviacall()'this'points to my Object when function is invoked
this.foo = param1;
this.bar = param2;
//logs Object{foo = 'foo', bar = 'bar'}
console.log(this);
};
// invokes function, set this value to myObject
myFunction.call(myObject, 'foo', 'bar');
// logs Object {foo = 'foo', bar = 'bar'}
console.log(myObject);
call()方法第一個(gè)參數(shù)是必要的this值,接著我們可以傳遞任意多個(gè)參數(shù),接著介紹apply()方法的使用。
var myObject = {};
var myFunction = function(param1, param2) {
//set via apply(), this points to my Object when function is invoked
this.foo=param1;
this.bar=param2;
// logs Object{foo='foo', bar='bar'}
console.log(this);
};
// invoke function, set this value
myFunction.apply(myObject, ['foo', 'bar']);
// logs Object {foo = 'foo', bar = 'bar'}
console.log(myObject);
通過與call()方法對比,我們發(fā)現(xiàn)apply()方法和call()方法沒有太大的區(qū)別,只是方法簽名不一樣。
1.1.3 總結(jié)
本文介紹Javascript中this的使用,更重要的是幫助我們能更好地理解this值在全局、函數(shù)、構(gòu)造函數(shù)以及一些特例的情況中值的變化。
對于在函數(shù)上下文中this的值是函數(shù)調(diào)用者提供并且由當(dāng)前調(diào)用表達(dá)式的形式而定的。如果在調(diào)用括號(hào)()的左邊有引用類型的值,那么this的值就會(huì)設(shè)置為該引用類型值的base對象。 所有其他情況下(非引用類型),this的值總是null。然而,由于null對于this來說沒有任何意義,因此會(huì)隱式轉(zhuǎn)換為全局對象。
對于特例情況,我們要記住賦值符、逗號(hào)操作符以及||邏輯表達(dá)式,會(huì)使this丟失原先的引用類型值,變成了函數(shù)類型,this的值就變成了全局對象了
參考
[1] http://dmitrysoshnikov.com/ecmascript/chapter-3-this/ 英文版
[2] http://blog.goddyzhao.me/post/11218727474/this 譯文
[3] https://net.tutsplus.com/tutorials/javascript-ajax/fully-understanding-the-this-keyword/
- Javascript this關(guān)鍵字使用分析
- javascript this用法小結(jié)
- 有關(guān)js的變量作用域和this指針的討論
- js報(bào)錯(cuò) Object doesn''t support this property or method的原因分析
- js原生態(tài)函數(shù)中使用jQuery中的 $(this)無效的解決方法
- javascript中onclick(this)用法介紹
- js綁定事件this指向發(fā)生改變的問題解決方法
- js中的this關(guān)鍵字詳解
- JS中的this變量的使用介紹
- 精通JavaScript的this關(guān)鍵字
相關(guān)文章
js報(bào)錯(cuò) Object doesn''t support this property or method的原因分析
運(yùn)行js是出現(xiàn)Object doesn't support this property or method 錯(cuò)誤的可能原因分析。2011-03-03JS、jquery實(shí)現(xiàn)幾分鐘前、幾小時(shí)前、幾天前等時(shí)間差顯示效果的代碼實(shí)例分享
在新浪微博首頁看到每條微博后邊顯示的時(shí)間并不是標(biāo)準(zhǔn)的年-月-日格式,而是經(jīng)過換算的時(shí)間差,如:發(fā)表于5分鐘前、發(fā)表于“2小時(shí)前”,比起標(biāo)準(zhǔn)的時(shí)間顯示格式,貌似更加直觀和人性化2014-04-04JavaScript 判斷指定字符串是否為有效數(shù)字
最近在做一個(gè)ColdFusion的項(xiàng)目,有一個(gè)業(yè)務(wù)Check,需要用JavaScript實(shí)現(xiàn):判斷指定字符串是否為有效數(shù)字。2010-05-05iscroll動(dòng)態(tài)加載數(shù)據(jù)完美解決方法
這篇文章主要為大家詳細(xì)介紹了iscroll動(dòng)態(tài)加載數(shù)據(jù)的完美解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07JS判斷數(shù)組四種實(shí)現(xiàn)方法詳解
這篇文章主要介紹了JS判斷數(shù)組四種實(shí)現(xiàn)方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06JavaScript中高級(jí)語法??表達(dá)式用法示例詳解
這篇文章主要為大家介紹了JavaScript中高級(jí)語法??表達(dá)式用法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04淺談html轉(zhuǎn)義及防止javascript注入攻擊的方法
下面小編就為大家?guī)硪黄獪\談html轉(zhuǎn)義及防止javascript注入攻擊的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12JavaScript入門系列之知識(shí)點(diǎn)總結(jié)
JavaScript 是屬于網(wǎng)絡(luò)的腳本語言。本文是小編日常收集整理些javascript入門基礎(chǔ)知識(shí),對js新手朋友非常有幫助,對js入門知識(shí)點(diǎn)感興趣的朋友一起學(xué)習(xí)吧2016-03-03- 這篇文章主要介紹了JS實(shí)現(xiàn)左邊列表移到到右邊列表功能,實(shí)現(xiàn)功能主要是左邊的下拉框內(nèi)容添加到右邊的下拉框,支持多選移動(dòng),且同時(shí)將右邊的下拉框?qū)ο笠瞥?,需要的朋友可以參考?/div> 2018-03-03
bootstrap日歷插件datetimepicker使用方法
這篇文章主要為大家詳細(xì)介紹了bootstrap日歷datetimepicker插件的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12最新評(píng)論