摘要:函數上下文中的值是函數調用者提供并且由當前調用表達式的形式而定的。然而,由于對于來說沒有任何意義,因此會隱式轉換為全局對象。這里注意到四個表達式中,只有第一個表達式是指向對象的,而其他三個表達式則執行。
摘要
相信有C++、C#或Java等編程經驗的各位,對于this關鍵字再熟悉不過了。由于Javascript是一種面向對象的編程語言,它和C++、C#或Java一樣都包含this關鍵字,接下來我們將向大家介紹Javascript中的this關鍵字。
正文
由于許多面向對象的編程語言都包含this關鍵字,我們會很自然地把this和面向對象的編程方式聯系在一起,this通常指向利用構造器新創建出來的對象。而在ECMAScript中,this不僅僅只用來表示創建出來的對象,也是執行上下文的一個屬性:
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);接下來我們將介紹函數中的this。
函數中的this
當this在函數代碼中,情況就復雜多了,并且會引發很多的問題。
函數代碼中this值的第一個特性(同時也是最主要的特性)就是:它并非靜態的綁定在函數上。
正如此前提到的,this的值是在進入執行上下文(Excution context)的階段確定的,并且在函數代碼中的話,其值每次都不盡相同。
然而,一旦進入執行代碼階段,其值就不能改變了。如果要想給this賦一個新的值是不可能的,因為在那時this根本就不是變量了。
接下來,我們通過具體的例子說明函數中的this。
首先我們定義兩個對象foo和person,foo包含一個屬性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.
通過上面的代碼,我們發現調用person的say()方法時,this指向person對象,當通過賦值方式使得foo的say()方法指向peson中的say()方法時。我們調用foo的say()方法,發現this不是指向person對象,而不是指向foo對象,這究竟是什么原因呢?
首先,我們必須知道this的值在函數中是非靜態的,它的值確定在函數調用時,具體代碼執行前,this的值是由激活上下文代碼的調用者決定的,比如說,調用函數的外層上下文;更重要的是,this的值是由調用表達式的形式決定的,所以說this并非靜態的綁定在函數上。
由于this并非靜態地綁定在函數上,那么我們是否可以在函數中動態地修改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
現在我們在方法say()中,動態地修改this的值,當我們重新執行以上代碼,發現this的值引用錯誤。這是由于一旦進入執行代碼階段(函數調用時,具體代碼執行前),this的值就確定了,所以不能改變了。
引用類型
前面我們提到this的值是由激活上下文代碼的調用者決定的,更重要的是,this的值是由調用表達式的形式決定的;那么表達式的形式是如何影響this的值呢?
首先,讓我們介紹一個內部類型——引用類型,它的值可以用偽代碼表示為一個擁有兩個屬性的對象分別是:base屬性(屬性所屬的對象)以及該base對象中的propertyName屬性:
// Reference type. var valueOfReferenceType = { base: mybase, propertyName : "mybasepropertyName" };
引用類型的值只有可能是以下兩種情況:
當處理一個標識符的時候
或者進行屬性訪問的時候
標識符其實就是變量名、函數名、函數參數名以及全局對象的未受限的屬性。
// Declares varible. var foo = 23; // Declares a function function say() { // Your code. }
中間過程中,對應的引用類型如下:
// Reference type. var fooReference = { base: global, propertyName: "foo" }; var sayReference = { base: global, propertyName: "say" };
我們知道Javascript中屬性訪問有兩種方式:點符號和中括號符號:
// Invokes the say method. foo.say(); foo["say"]();
由于say()方法是標識符,所以它對應于foo對象引用類型如下:
// Reference type. var fooSayReference = { base: foo, propertyName: "say" };
我們發現say()方法的base屬性值為foo對象,那么它對應的this屬性也將指向foo對象。
假設,我們直接調用say()方法,它對應的引用類型如下:
// Reference type. var sayReference = { base: global, propertyName: "say" };
由于say()方法的base屬性值為global(通常來說是window object),那么它對應的this屬性也將指向global。
函數上下文中this的值是函數調用者提供并且由當前調用表達式的形式而定的。如果在調用括號()的左邊有引用類型的值,那么this的值就會設置為該引用類型值的base對象。 所有其他情況下(非引用類型),this的值總是null。然而,由于null對于this來說沒有任何意義,因此會隱式轉換為全局對象。
函數調用以及非引用類型
前面我們提到,當調用括號左側為非引用類型的時,this的值會設置為null,并最終隱式轉換為全局對象。
現在我們定義了一個匿名自執行函數,具體實現如下:
// Declares anonymous function (function () { alert(this); // null => global })();
由于括號()左邊的匿名函數是非引用類型對象(它既不是標識符也不屬于屬性訪問),因此,this的值設置為全局對象。
// 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
這里注意到四個表達式中,只有第一個表達式this是指向foo對象的,而其他三個表達式則執行global。
現在我們又有疑問了:為什么屬性訪問,但是最終this的值不是引用類型對象而是全局對象呢?
我們注意到表達式二是賦值(assignment operator),與表達式一組操作符不同的是,它會觸發調用GetValue方法(參見11.13.1中的第三步)。 最后返回的時候就是一個函數對象了(而不是引用類型的值了),這就意味著this的值會設置為null,最終會變成全局對象。
第三和第四種情況也是類似的——逗號操作符和OR邏輯表達式都會觸發調用GetValue方法,于是相應地就會丟失原先的引用類型值,變成了函數類型,this的值就變成了全局對象了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88096.html
摘要:總結本博文通過介紹執行上下文和作用域的異同的使用以及變量對象,讓我們加深對語言特性的理解。首先,我們介紹了執行上下文和的的關系,并且執行上下文是具有對象的然后,介紹了作用域使變量在作用域范圍內可見,并且作用域是基于函數的。 接上一篇Javascript Context和Scope的學習總結01【轉自cnblogs的JKhuang】(可能是segmentfault對單篇文章發布字數有限制...
摘要:正文執行環境也稱為環境是中最為重要的一個概念。執行環境定義了變量或函數有權訪問的其他數據,決定了它們各自的行為。簡而言之,執行環境是基于對象的,而作用域是基于函數的。 前述 在我們學習Javascript過程中,常常會遇到作用域(Scope)和執行上下文(Context)等概念。其中,執行上下文與this關鍵字的關系密切。 有面向對象編程經驗的各位,對于this關鍵字再熟悉不過了,因此...
摘要:發生這種情況的條件是當引用類型值的對象恰好為活躍對象??偨Y本文介紹中的使用,更重要的是幫助我們能更好地理解值在全局函數構造函數以及一些特例的情況中值的變化。然而,由于對于來說沒有任何意義,因此會隱式轉換為全局對象。 接上一篇Javascript this 的一些學習總結02【轉自cnblogs的JKhuang】 引用類型以及this的null值 對于前面提及的情形,還有例外的情況,當調...
插件顧名思義就是能在一個頁面多處使用, 各自按自己的參數配置運行, 并且相互不會沖突.會寫javascript插件是進階js高級的必經之路, 也是自己所學知識的一個典型的綜合運用. 如果你還沒頭緒, 無從下手的話, 不用著急, 今天我們就一起來探討一下插件的一般寫法.所需技能: 1.面向對象用法 2.閉包的理解 3.變量作用域的理解 以一個tab選項卡的為例: 第一步: 我們需要寫html結...
摘要:總結上面的大部分方式都可以互相組合使用的,一般來說如果要設計系統,可能會用到松耦合擴展,私有狀態和子模塊這樣的方式。 簡介 Module模式是JavaScript編程中一個非常通用的模式,一般情況下,大家都知道基本用法,本文嘗試著給大家更多該模式的高級使用方式。 首先我們來看看Module模式的基本特征: 模塊化,可重用 封裝了變量和function,和全局的namaspace不接觸...
閱讀 1660·2021-11-16 11:41
閱讀 2458·2021-11-08 13:14
閱讀 3106·2019-08-29 17:16
閱讀 3079·2019-08-29 16:30
閱讀 1843·2019-08-29 13:51
閱讀 356·2019-08-23 18:38
閱讀 3223·2019-08-23 17:14
閱讀 630·2019-08-23 15:09