標籤:
javascript變數聲明提升(hoisting)
javascript的變數聲明具有hoisting機制,JavaScript引擎在執行的時候,會把所有變數的聲明都提升到當前範圍的最前面。
先看一段代碼
| 12345 |
var v = "hello";(function(){ console.log(v); var v = "world";})(); |
這段代碼啟動並執行結果是什麼呢?
答案是:undefined
這段代碼說明了兩個問題,
第一,function範圍裡的變數v遮蓋了上層範圍變數v。代碼做少些變動
| 12345 |
var v = "hello";if(true){ console.log(v); var v = "world";} |
輸出結果為"hello",說明javascript是沒有塊級範圍的。函數是JavaScript中唯一擁有自身範圍的結構。
第二,在function範圍內,變數v的聲明被提升了。所以最初的代碼相當於:
| 123456 |
var v = "hello";(function(){ var v; //declaration hoisting console.log(v); v = "world";})(); |
聲明、定義與初始化
聲明宣稱一個名字的存在,定義則為這個名字分配儲存空間,而初始化則是為名字分配的儲存空間賦初值。
用C++來表述這三個概念
| 123 |
extern int i;//這是聲明,表明名字i在某處已經存在了int i;//這是聲明並定義名字i,為i分配儲存空間i = 0;//這是初始化名字i,為其賦初值為0 |
javascript中則是這樣
| 12 |
var v;//聲明變數vv = "hello";//(定義並)初始設定變數v |
因為javascript為動態語言,其變數並沒有固定的類型,其儲存空間大小會隨初始化與賦值而變化,所以其變數的“定義”就不像傳統的靜態語言一樣了,其定義顯得無關緊要。
聲明提升
當前範圍內的聲明都會提升到範圍的最前面,包括變數和函數的聲明
| 123456 |
(function(){ var a = "1"; var f = function(){}; var b = "2"; var c = "3";})(); |
變數a,f,b,c的聲明會被提升到函數範圍的最前面,類似如下:
| 1234567 |
(function(){ var a,f,b,c; a = "1"; f = function(){}; b = "2"; c = "3";})(); |
請注意函數運算式並沒有被提升,這也是函數運算式與函式宣告的區別。進一步看二者的區別:
| 123456789 |
(function(){ //var f1,function f2(){}; //hoisting,被隱式提升的聲明 f1(); //ReferenceError: f1 is not defined f2(); var f1 = function(){}; function f2(){}})(); |
上面代碼中函式宣告f2被提升,所以在前面調用f2是沒問題的。雖然變數f1也被提升,但f1提升後的值為undefined,其真正的初始值是在執行到函數運算式處被賦予的。所以只有聲明是被提升的。
名字解析順序
javascript中一個名字(name)以四種方式進入範圍(scope),其優先順序順序如下:
1、語言內建:所有的範圍中都有 this 和 arguments 關鍵字
2、形式參數:函數的參數在函數範圍中都是有效
3、函式宣告:形如function foo() {}
4、變數聲明:形如var bar;
名字聲明的優先順序如上所示,也就是說如果一個變數的名字與函數的名字相同,那麼函數的名字會覆蓋變數的名字,無論其在代碼中的順序如何。但名字的初始化卻是按其在代碼中書寫的順序進行的,不受以上優先順序的影響。看代碼:
| 123456789 |
(function(){ var foo; console.log(typeof foo); //function function foo(){} foo = "foo"; console.log(typeof foo); //string})(); |
如果形式參數中有多個同名變數,那麼最後一個同名參數會覆蓋其他同名參數,即使最後一個同名參數並沒有定義。
以上的名字解析優先順序存在例外,比如可以覆蓋語言內建的名字arguments。
命名函數運算式
可以像函式宣告一樣為函數運算式指定一個名字,但這並不會使函數運算式成為函式宣告。命名函數運算式的名字不會進入名字空間,也不會被提升。
| 12345 |
f();//TypeError: f is not a functionfoo();//ReferenceError: foo is not definedvar f = function foo(){console.log(typeof foo);};f();//functionfoo();//ReferenceError: foo is not defined |
命名函數運算式的名字只在該函數的範圍內部有效。
轉自:http://openwares.net/js/javascript_declaration_hoisting.html
javascript變數聲明提升