JS變量初始化過程


  1. 頂級變量初始化
  2. 函數變量
  3. 沒有scope的塊

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聲明,都會在此階段處理,這也導致變量不能和函數同名

總結一下:

 

  1. FunctionDeclarations become ready-to-use functions. That allows to call a function before it’s declaration.
  2. Variables start as undefined.
  3. 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)
View Answer
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.
View Answer

函數變量

當函數執行時,每一個函數調用都會產生新的包含參數、變量、嵌套函數聲明的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存在但是未定義
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM