你所看到的事實是全局對象(window
,在瀏覽器上)是兩個不同事物的混合,除了全局執行上下文外,它們是獨特的。
在第一塊,someVar
是window
對象的正常特性。屬性可以通過delete
刪除。
在第二塊中,someVar
是結合對象全局執行上下文 —這也是window
的可變上下文的的性質。您無法刪除綁定對象在其作爲綁定對象的角色中接收的屬性(即使您可以刪除以其他方式接收的屬性)。也就是說,您不能刪除使用var
聲明的變量(以及其他一些以相同方式添加的內容)。
(對不起,不是我的術語,它來自the spec,這的確具有一些非常有趣的語言。)
這只是我們有這個概念混爲一談全球執行上下文。其他執行上下文(例如函數調用)的變量綁定對象仍然是一件非常真實的事情(並且對於閉包的正常運行至關重要),但是沒有直接訪問它的程序化方法。然而,在全局執行上下文中,它是全局對象,我們當然可以訪問它。
如果我們先看看函數,然後再看看全局執行上下文,它有助於理解這一點。當你調用一個函數,這些事情發生:
- 設置
this
指向通過調用指定的對象(的this
值通常是隱式設置,但有辦法來設置它明確)。
- 爲此調用創建一個執行上下文。
- 創建該執行上下文變量上下文。
- 該變量上下文創建結合對象。
- 將函數的名稱(如果有)添加到綁定對象作爲引用該函數的屬性。
- 的
arguments
屬性添加到綁定對象,參照參數的僞陣列的功能。
- 將函數定義中聲明的任何命名參數添加爲綁定對象的屬性,並引用它們在參數中的條目。
- 與價值
undefined
添加的通過var
報表(任何地方函數體)作爲綁定對象的屬性聲明的任何變量的名稱,最初。
- 如果在函數中聲明瞭命名函數,請將它們的名稱作爲綁定對象的屬性添加,並引用這些函數。
- 把綁定對象在範圍鏈(詳見下文)的頂部。
...然後逐步執行函數體內的代碼開始。任何var
語句帶有初始值(例如,var a = 5;
而不僅僅是var a;
被視爲賦值語句(a = 5;
)當執行點到達他們。
縱觀上述,只要屬性添加「到綁定對象」,它的加入一個標誌,表明它不能被刪除。這就是爲什麼var
S(和聲明的函數等的名稱)不能被刪除。
任何不合格的參考通過作用域鏈擡頭。所以,當您在代碼中引用a
時,解釋器看起來的第一個位置是位於頂部的綁定對象範圍鏈。如果它有一個名爲a
的屬性,那就是被使用的;如果不是,我們查看範圍鏈中的下一個鏈接,如果我們找到它,則使用該屬性;依此類推,直到我們用盡範圍鏈上的鏈接。全局對象是該鏈最底層的鏈接(這就是全局變量工作的原因)。
那麼全球環境有什麼不同呢?其實,很少。下面是序列(大致):
- 爲此調用創建一個執行上下文。
- 創建該執行上下文變量上下文。
- 爲該變量上下文創建一個綁定對象。
- 設置
this
指向綁定對象;這使其成爲全球性的對象。
- 根據環境的定義在該對象上設置一些默認屬性(在瀏覽器中,例如,將屬性
window
添加到對象中,引用自身)。
...然後我們基本上拿起第8步中的函數的東西:
- 添加的通過
var
報表(任何地方在全球範圍內)作爲綁定/全局對象的屬性聲明的任何變量的名稱,最初值爲undefined
。
- 如果在全局範圍內聲明瞭命名函數,請將它們的名稱作爲綁定/全局對象的屬性添加,並引用這些函數。
- 將綁定/全局對象置於範圍鏈(更多下方)的頂部。
...並開始逐步執行代碼(同樣使用var
初始值設定項成爲賦值)。
謝謝TJ。隨着你的回答,結合ECMA規範,我開始明白,爲什麼'eval'-ed代碼變量可以被刪除。假設這觸及了它:*如果一個VariableDeclaration嵌套在with語句中,並且VariableDeclaration中的標識符是與with語句的對象環境記錄的綁定對象的屬性名稱相同的 ,則步驟4 將賦值給屬性,而不是標識符的變量環境綁定。* – KooiInc 2011-05-13 09:14:49
@KooiInc:LOL,這裏我避免談論'with'語句。順便說一下,我在上面添加了一些進一步的解釋。 – 2011-05-13 09:19:08
確實很有趣。也許我們應該把它歸結爲:*如果你不使用var,我們隱含地假設你的意思是:'with(this){somevar = [assignment];}',相當於'this.somevar = [賦值] '*?無論如何,讓我們先接受你的回答;) – KooiInc 2011-05-13 09:34:01