2014-11-04 134 views
2

我對Javascript中變量提升有疑問。Javascript變量提升示例

考慮下面的例子;

var myName = "Richard"; // Variable assignment (initialization) 
​ 
​function myName() { 
console.log ("Rich"); 
} 
​ 
console.log(typeof myName); // string 

其實我困惑,爲什麼typeof myName返回爲字符串

根據我的理解,這個例子將按照如下進行;

  1. 首先,函數聲明(function myName())會得到提升到頂部,然後
  2. JS解釋會讀取線var myName = "Richard"(因爲函數聲明越過變量聲明優先)。但是,由於已經有一個名爲「myName」的屬性,因此該語句將被忽略。

因此typeof myName應該得到返回功能(而不是字符串)

請讓我知道我的理解是不正確。

回答

1

JavaScript有一個動態類型系統,即變量的類型可以隨時間變化。基本上,你寫的是正確的:首先,函數聲明得到運行(加載文件時),但是存儲在變量myName中的函數被字符串覆蓋。

唯一被忽略的是對var的調用,因爲該變量實際上已經被聲明。

但它是完全有效的重新定義一個變量(這就是你在這裏做的,通過分配一個新的值)。

最後,您的樣品也不過是這樣的:

var x = 23; 
x = 'foo'; 

這也能發揮作用,x'foo',其類型將是string。與您的示例唯一的區別在於,在您的示例中,涉及到function類型的值。

-1

因爲Hoisting變量和函數定義移動到頂部,所以你必須:

var myName; 
// moved to top 
function myName() { 
    console.log ("Rich"); 
} 

// next your code 
myName = "Richard"; 

console.log(typeof myName); // string 

如果你重寫你的代碼是這樣的:

var myName = "Richard"; // Variable assignment (initialization) 
​ 
myName = ​function() { // Variable redefinition 
    console.log ("Rich"); 
} 
​ 
console.log(typeof myName); // function 

myName變量現在是一個功能,因爲只有myName變量是hoisted

var myName; 

myName = "Richard";  // Variable assignment (initialization) 
myName = ​function() { // Variable redefinition 
    console.log ("Rich"); 
} 
​ 
console.log(typeof myName); // outputs 'function' 
+0

您從一個函數聲明更改爲函數表達式在您的示例中,並且 - 不幸的是 - 它們被JavaScript區別對待:在加載函數聲明時掃描文件,函數表達式及其賦值在運行時得到解決。因此,不幸的是你的例子解釋了一些事情,但沒有解釋問題。 – 2014-11-04 10:38:50

+0

@GoloRoden - 我已經解釋了爲什麼'myName'變量的類型是'string'而不是'function',接下來我做了一個例子,其中變量的類型是'function' ;-) – 2014-11-04 13:20:06

-1

「提升」的想法是瞭解發生了什麼的不好方法。換句話說,在我看來,「提升」對吊裝來說是一個不好的解釋。

真正發生的並不是「提升」。真正發生的是JavaScript以兩個階段執行代碼:編譯階段和eval階段。 javascript社區稱這種「提升」的症狀,但大多數人不明白爲什麼提起升降機,因爲他們認爲JavaScript解釋器有這個功能,稱爲「提升」(他們不這樣做)。

實際發生的事情比起吊的想法更容易解釋。規則是:

  1. JavaScript總是從上往下解析代碼。它從不對代碼進行重新排序(它從不提升)。

  2. 有兩個階段的執行:編譯和評估。

  3. 所有聲明都在編譯階段處理,在編譯階段不會評估任何表達式(因爲「評估」事情是在評估階段完成的)。

  4. 在評估階段處理所有需要評估的表達式和其他任何內容。

記住規則1,所有的解析都是自上而下的,沒有回溯,沒有提升。

讓我們以你的榜樣,並嘗試記住JavaScript的編譯和評估階段保持理解它:

var myName = "Richard"; // Variable assignment (initialization) 
​  
​ function myName() { 
     console.log ("Rich"); 
    } 

​ console.log(typeof myName); // string 
  1. 在編譯階段,解釋看你聲明一個變量。它爲這個變量分配一個內存區域,併爲其賦值undefined

  2. 在compliation階段,解釋器會看到你聲明一個函數。它還注意到函數名稱會隱藏變量名稱。所以它創建一個函數「myName」(這意味着此時變量myName指向該函數)。

  3. 編譯階段結束。現在我們進入評估階段。

  4. 在評估階段,解釋器會看到您將字符串分配到myName

  5. 當我們到達函數聲明時沒有什麼可以評估的,因爲聲明是在編譯階段處理的。

  6. 在評估階段,解釋器會看到你console.logging的類型myName。由於分配給它的最後一件事是一個字符串,它會打印「字符串」。

請注意,如果您刪除字符串賦值,然後myName將typeof運算「功能」。那是因爲在這種情況下,分配給它的最後一件事是聲明的功能。

參見造成執行的兩個階段其他微妙之處此相關的問題:JavaScript function declaration and evaluation order

+1

我不'你認爲你的解釋是正確的。 「提升」是一種語言功能,雖然標準沒有使用這個詞,但程序是精確記錄的 - 請參閱「聲明綁定實例化」http://ecma-international.org/ecma-262/5.1/#sec -10.5 – georg 2014-11-04 07:55:38

0

除了其他的答案更要注意的是,如果你申報你的函數以下列方式:

var myName = function() { 
    console.log('Rich'); 
} 

或者乾脆

myName = function(){ 
    console.log('Rich') 
} 

而不是在 「功能MYNAME。」 語法,然後typeof運算MYNAME將返回「功能「

0

2年過去了,但也許它仍然是相關的人 你是正確的,解釋的對象時,的確是這樣:

  • 掃描範圍內的函數聲明

    • 對於找到每個函數,在變量對象中創建一個屬性
    • 如果函數名已存在,則參考指針值將被覆蓋
  • 掃描變量聲明

    • 對於每個找到的變量聲明的背景下,創建在可變對象,它是變量名稱的屬性,並初始化該值作爲未定義
    • 如果變量名已經存在的變量對象,什麼也不做,繼續掃描

但是看起來這是不是因此對於全球範圍。 也就是說當我定義一個對象的輸出是完全按照它應該是,當我在全球範圍內定義它不是... 仍試圖圍繞這一個我的頭