JavaScript函數與變量機制與大部分語言完全不同,在JS中,所有的局部變量和函數都是特定內部對象的屬性,即LexicalEnvironment(LE)
在瀏覽器中頂級的LE是window對象,也叫做全局變量
頂級變量初始化
當js即將執行時,有一個預處理階段叫做變量實例化
1.首先,解釋器掃描Function Declarations,也就是function name{},解釋器將對每個聲明創建一個函數並作為Window的一個變量
var a = 5 function f(arg) { alert('f:'+arg) } var g = function(arg) { alert('g:'+arg) }
此時,瀏覽器發現函數f,於是創建Function,將其存為window.f:
// 1. Function Declarations are initialized before the code is executed. // so, prior to first line we have: window = { f: function } var a = 5 function f(arg) { alert('f:'+arg) } // <-- FunctionDeclaration var g = function(arg) { alert('g:'+arg) }
而這也導致,f可以在聲明之前調用
f() function f() { alert('ok') }
2.解釋器掃描var declarations,作為window的屬性。但是此時變量並沒有被賦值,所有的變量此時都是undefined。
// 1. Function declarations are initialized before the code is executed. // window = { f: function } // 2. Variables are added as window properties. // window = { f: function, a: undefined, g: undefined } var a = 5 // <-- var function f(arg) { alert('f:'+arg) } var g = function(arg) { alert('g:'+arg) } // <-- var
雖然g是一個函數表達式,但是解釋器不關心這些,只要使用var聲明,都會在此階段處理,這也導致變量不能和函數同名
總結一下:
- FunctionDeclarations become ready-to-use functions. That allows to call a function before it’s declaration.
- Variables start as
undefined
. - All assignments happen later, when the execution reaches them.
3.代碼執行
當變量活函數被訪問時,解釋器從window中獲取
alert("a" in window) // true, because window.a exists alert(a) // undefined, because assignment happens below alert(f) // function, because it is Function Declaration alert(g) // undefined, because assignment happens below var a = 5 function f() { /*...*/ } var g = function() { /*...*/ }
5.賦值過后,a=5,g成為了函數,注意下面代碼的區別:
var a = 5 var g = function() { /*...*/ } alert(a) // 5 alert(g) // function
如果變量聲明時未用var,那么在初始化時就不會被創建(或者說成為window的屬性),解釋器看不到的:
alert("b" in window) // false, there is no window.b alert(b) // error, b is not defined b = 5
但賦值之后,b成為window的全局變量:window.b
b = 5 alert("b" in window) // true, there is window.b = 5
考慮一下:下面代碼的執行結果:
if ("a" in window) { var a = 1 } alert(a)

// window = {a:undefined} if ("a" in window) { var a = 1 } alert(a)
if ("a" in window) { a = 1 } alert(a)

The answer is “Error: no such variable”, because there is no variable a at the time of "a" in window check. So, the if branch does not execute and there is no a at the time of alert.
函數變量
當函數執行時,每一個函數調用都會產生新的包含參數、變量、嵌套函數聲明的LexicalEnvironment
變量是內部用來讀寫變量的,和window不同,函數的LexicalEnvironment
是不能直接訪問的
下面我們看一下函數執行的細節:
function sayHi(name) { var phrase = "Hi, " + name alert(phrase) } sayHi('John')
1.在解釋器開始執行函數代碼時,會創建一個填充參數、局部變量、嵌套函數的LexicalEnvironment
,很顯然參數具有初始值,而局部變量則是未定義的
2.函數代碼開始執行,最后執行賦值語句。內部的變量賦值意味着LexicalEnvironment相應的屬性獲得了新值
so,phrase="Hi, "+name 改變了LexicalEnvironment
function sayHi(name) { // LexicalEnvironment = { name: 'John', phrase: undefined } var phrase = "Hi, " + name // LexicalEnvironment = { name: 'John', phrase: 'Hi, John'} alert(phrase) } sayHi('John')
最后一行alert(phrase)從LexicalEnvironment
搜獲phrase
3.函數執行之后,LexicalEnvironment會廢棄他的內容,因為這些變量不再需要,但是情況並不總是這樣:
Specification peculiarities If we look into the recent ECMA-262 specification, there are actually two objects. The first is a VariableEnvironment object, which is actually populated by variables and functions, declared by FunctionDeclaration, and then becomes immutable. The second is a LexicalEnvironment object, which is almost same as VariableEnvironment, but it is actually used during the execution. A more formal description can be found in the ECMA-262 standard, sections 10.2-10.5 and 13. It is also noted that in JavaScript implementations, these two objects can be merged into one. So, we evade irrelevant details and use the term LexicalEnvironment everywhere.
沒有scope的塊
下面的代碼沒有任何區別
var i = 1 { i = 5 }
i = 1 { var i = 5 }
所有的變量聲明在塊執行之前就已經初始化
這一點和Java、C++不用,在JS中,變量在循環之后依舊存在,因為他們的scope是函數
for(var i=0; i<5; i++) { } alert(i) // 5, variable survives and keeps value
在循環中聲明變量很方便,但不要認為循環之后變量就不存在了,不要把循環當作其scope
下面的代碼的結果是什么:
function test() { alert(window) var window = 5 } test()

var指令在預處理階段就執行了 因此,window在alert之前成了局部變量 LexicalEnvironment = { window: undefined } 所以當執行到第一行時,變量window存在但是未定義