摘要:本文總結了的各種情況,并從規范的角度探討了的具體實現,希望對大家理解有所幫助。規范規范里面詳細介紹了的實現細節,通過閱讀規范,我們可以更準確的理解上述四種情況到底是怎么回事。由于本人能力有限,如有理解錯誤的地方還望指出。
this是面向對象編程中的一個概念,它一般指向當前方法調用所在的對象,這一點在java、c++這類比較嚴格的面向對象編程語言里是非常明確的。但是在javascript中,this的定義要靈活許多,如果未準確掌握,非常容易混淆。本人在面試過程中也發現,面試者很少有由能夠回答得非常全面的。本文總結了this的各種情況,并從Ecma規范的角度探討了this的具體實現,希望對大家理解this有所幫助。
this指向的四中情況在javascript里面,this的指向可以歸納為以下四種情況。只要能牢記這四種情況,大部分情況下就已經夠用了。
1.在全局代碼或者普通的函數調用中,this指向全局對象,在瀏覽器里面既為window對象。
console.log(this);//輸出window function foo(){ console.log(this); } foo();//輸出window
在瀏覽器環境里運行上述代碼,兩處輸出結果均為window對象。
2.通過call或apply方法調用函數,this指向方法調用的第一個參數。
var obj = {name:"test"}; function foo(){ console.log(this); } foo.call(obj);//輸出obj foo.apply(obj);//輸出obj
在瀏覽器環境里執行以上代碼,輸出結果均為對象obj。call和apply除了參數形式不一樣外其他都一樣,call采用逗號分割,apply采用數組。說到這里,順便介紹一個小技巧。如何在不生成新數組的情況下實現兩個數組的連接?請看下面的代碼。
var arr1 = [1, 2 , 3], arr2 = [4, 5, 6]; Array.prototype.push.apply(arr1, arr2); console.log(arr1);//輸出[1, 2, 3, 4, 5, 6]
執行上述代碼后,輸出結果為[1, 2, 3, 4, 5, 6]。這是一個非常實用的小技巧,由于apply第二個參數為數組形式,所以我們可以把push方法“借”過來,從而實現兩個數組的連接。
3.調用對象的方法,this指向該對象。
var obj = {name:"test"}; function foo(){ console.log(this); } obj.foo = foo; obj.foo();//輸出obj
執行以上代碼后,控制臺輸出為obj對象。這就是我們常說的“誰調用,指向誰”。
4.構造方法中的this,指向新構造的對象。
function C(){ this.name = "test"; this.age = 18; console.log(this); } var c = new C();//輸出 c console.log(c);//輸出 c
執行以上代碼后,控制臺輸出均為c所指向的對象。當new操作符用于函數時,會創建一個新對象,并用this指向它。
Ecma規范Ecma規范里面詳細介紹了this的實現細節,通過閱讀規范,我們可以更準確的理解上述四種情況到底是怎么回事。
函數對象有一個叫[[Call]]內部方法,函數的執行其實是通過[[Call]]方法來執行的。[[Call]]方法接收兩個參數thisArg和argumentList,thisArg和this的指向有直接關系,argumentList為函數的實參列表。thisArg又是怎么來的呢?我們可以和前面討論的四種情況對應起來:
普通方法調用thisArg為undefined。
通過call或apply調用,thisArg既為第一個參數。
通過對象調用,thisArg指向該對象。
在構造方法中,thisArg為新構造的對象。
thisArg和this是什么關系?規范里的描述是這樣的:
If the function code is strict code, set the ThisBinding to thisArg.
Else if thisArg is null or undefined, set the ThisBinding to the global object.
Else if Type(thisArg) is not Object, set the ThisBinding to ToObject(thisArg).
Else set the ThisBinding to thisArg.
在嚴格模式下,thisArg和this是一一對應的。
function foo(){ "use strict"; console.log(this); } foo();//輸出undefined
該示例輸出的結果為undefined。
第二點是說如果thisArg為null或者undefined則this指向全局對象。
function foo(){ console.log(this); } foo.call(null);//輸出window
該示例的輸出結果為window。
第三點說如果thisArg為非對象類型,則會強制轉型成對象類型。
function foo(){ console.log(this); } var aa = 2; console.log(aa);//輸出2 foo.call(aa);//輸出 Number
這里的輸出結果分別為2和Number,它將基本類型轉型成了對象包裝類型。
第四點說明剩下的情況thisArg和this為一一對應的關系。
規范里面對this指向的描述還是比較明確的。只要你搞清楚thisArg怎么確定,thisArg和this的對應關系,那么你就能搞定所有this的情況了。
確保this的指向在實際使用this的過程中,遇到得最多得一個問題可能就是上下文丟失的問題了。因為javascript中的函數是可以作為參數傳遞的,那么其他對象在執行回調函數時就可能造成回調函數原來的上下文丟失,也就是this的指向改變了。
var C = function(){ this.name = "test"; this.greet = function(){ console.log("Hello,I am "+this.name+"!"); }; } var obj = new C(); obj.greet();//輸出 Hello,I am test! setTimeout(obj.greet, 1000);//輸出 Hello,I am !
可見第二條輸出中this的值改變了,其實我們是希望this能夠指向obj的。解決該問題的方法有兩種。
1.bind方法。
bind方法通過閉包巧妙地實現了上下文的綁定,它實際上是將原方法包裝成了一個新方法。一般的實現如下:
Function.prototype.bind = function(){ var args = arguments, thisArg = arguments[0], func = this; return function(){ var arg = Array.prototype.slice.call(args, 1); Array.prototype.push.apply(args, arguments); return func.apply(thisArg, arg); } }
前面的示例代碼我們只需要加上bind,就能夠得到我們希望的結果了。
... setTimeout(obj.greet.bind(obj), 1000);//輸出 Hello,I am test! ...
2.es6箭頭函數。
es6里面提供了一個新的語法糖,箭頭函數。箭頭函數的this不再變幻莫測,它永遠指向函數定義時的this值。
var C = function(){ this.name = "test"; this.greet = ()=>{ console.log("Hello,I am "+this.name+"!"); }; } var obj = new C(); obj.greet();//輸出 Hello,I am test! setTimeout(obj.greet, 1000);//輸出 Hello,I am test!
我們將前面的示例該成箭頭函數后,兩處的輸出結果一樣了。this的值不再改變了,這是我們想要的。
小結this看起來是個非常小的知識點,其實挖起來還是有很多細節的,特別是規范里面的一些定義,本人覺得對于一個js程序員來說是非常重要的。本人后面也準備找些ecma規范里面的知識點和大家分享,希望能對大家有所幫助。由于本人能力有限,如有理解錯誤的地方還望指出。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/91544.html
摘要:不包括作為其嵌套函數的被解析的源代碼。作用域鏈當代碼在一個環境中執行時,會創建變量對象的一個作用域鏈。棧結構最頂層的執行環境稱為當前運行的執行環境,最底層是全局執行環境。無限制函數上下文。或者拋出異常退出一個執行環境。 前言 其實規范這東西不是給人看的,它更多的是給語言實現者提供參考。但是當碰到問題找不到答案時,規范往往能提供想要的答案 。偶爾讀一下能夠帶來很大的啟發和思考,如果只讀一...
摘要:本文不是標準的中文翻譯,也不是的入門教程,本文雖然以的常見問題切入,但并不適合想要快速了解這些問題的人才是快速了解問題的正解。盡量以英文原版為基礎,為了流暢,可能會使用某些名詞的中文翻譯,但會將匹配的英文名詞以此種樣式中出現一次以避免誤解。 簡單易懂的ECMA規范導讀1 序 最近混SF,恰巧又逢工作方面有了NodeJS的機會,迫切地有教別人怎么寫JS的需求, 我發現JS這個東西其實...
摘要:另一方面,我不建議初次接觸的開發人員閱讀規范。在維護語言的最新規范。在這一點上,我想指出的是,絕對沒有人從上到下閱讀規范。拓展閱讀由于的定義,中的細節如冒泡錯誤,直到塊在規范中不存在。換句話說,會轉發中拋出的錯誤,并終止其余的步驟。 翻譯自:How to Read the ECMAScript Specification Ecmascript 語言規范 The ECMAScr...
摘要:不同執行上下文中的變量對象對于所有的執行上下文類型,變量對象的一些操作比如變量實例化和行為是共通的。從這點看,將變量對象看作是抽象的基礎的東西很便利。全局上下文的變量對象現在,應該先給出全局對象的定義。 原文地址 作者的話 有很多文章已經對ECMAScript的核心概念做了詳盡解讀。本系列文章翻譯自Dmitry Soshnikov的個人網站,相信不少人已經看過原文或者譯文。原文簡潔易懂...
摘要:如下所示在規范中,已經正式把屬性添加到規范中也可以通過設置和獲取對象的原型對象對象之間的關系可以用下圖來表示但規范主要介紹了如何利用構造函數去構建原型關系。 前言 在軟件工程中,代碼重用的模式極為重要,因為他們可以顯著地減少軟件開發的成本。在那些主流的基于類的語言(比如Java,C++)中都是通過繼承(extend)來實現代碼復用,同時類繼承引入了一套類型規范。而JavaScript是...
閱讀 2109·2023-04-25 17:23
閱讀 2922·2021-11-17 09:33
閱讀 2518·2021-08-21 14:09
閱讀 3595·2019-08-30 15:56
閱讀 2608·2019-08-30 15:54
閱讀 1628·2019-08-30 15:53
閱讀 2132·2019-08-29 13:53
閱讀 1147·2019-08-29 12:31