標籤:www. 不能 賦值 無法 操作 控制台 err efi 原理
前面的話
一般認為,javascript代碼在執行時是由上到下一行一行執行的。但實際上這並不完全正確,主要是因為聲明提升的存在。本文是深入理解javascript範圍系列第三篇——聲明提升(hoisting)
變數聲明提升
a = 2 ;var a;console.log( a );
直覺上,會認為是undefined,因為var a聲明在a = 2;之後,可能變數被重新賦值了,因為會被賦予預設值undefined。但是,真正的輸出結果是2
console.log( a ) ;var a = 2 ;
鑒於上面的特點,可能會認為這個程式碼片段也會同樣輸出2。但,真正的輸出結果是undefined
所有這些和觀感相違背的原因是在於編譯器的編譯過程
第一篇介紹過範圍的內部原理。引擎會在解釋javascript代碼之前首先對其進行編譯。編譯階段中的一部分工作就是找到所有的聲明,並用合適的範圍將它們關聯起來
包括變數和函數在內的所有聲明都會在任何代碼被執行前首先被處理
var a = 2 ;
這個程式碼片段實際上包括兩個操作:var a 和 a = 2
第一個定義聲明是在編譯階段由編譯器進行的。第二個賦值操作會被留在原地等待引擎在執行階段執行
//對變數a的聲明提升到最上面後,再執行代碼時,控制台輸出2var a;a = 2 ;console.log(a);
聲明從它們在代碼中出現的位置被“移動”到了最上面,這個過程就叫作提升(hoisting)
[注意]每個範圍都會進行提升操作
console.log(a);var a = 0;function fn(){ console.log(b); var b = 1; function test(){ console.log(c); var c = 2; } test();}fn();
//變數聲明提升後,變成下面這樣var a ;console.log(a);a = 0;function fn(){ var b; console.log(b); b = 1; function test(){ var c ; console.log(c); c = 2; } test();}fn();
函式宣告提升
聲明包括兩種:變數聲明和函式宣告。不僅變數聲明可以提升,函式宣告也有提升操作
foo();function foo(){ console.log(1);//1}
上面這個程式碼片段之所以能夠在控制台輸出1,就是因為foo()函式宣告進行了提升,如下所示:
function foo(){ console.log(1);}foo();
函式宣告會提升,但函數運算式卻不會提升
foo();var foo = function(){ console.log(1);//TypeError: foo is not a function}
上面這段程式中的變數標識符foo被提升並分配給全域範圍,因此foo()不會導致ReferenceError。但是foo此時並沒有賦值,foo()由於對undefined值進行函數調用而導致非法操作,因此會拋出TypeError異常
//變數提升後,代碼如下所示:var foo;foo();foo = function(){ console.log(1);}
即使是具名的函數運算式也無法被提升
foo();//TypeError: foo is not a functionvar foo = function bar(){ console.log(1);};
//聲明提升後,代碼變為:var foo;foo();//TypeError: foo is not a functionfoo = function bar(){ console.log(1);};
[注意]函數運算式的名稱只能在函數體內部使用,而不能在函數體外部使用
var bar;var foo = function bar(){ console.log(1);};bar();//TypeError: bar is not a function
函數覆蓋
函式宣告和變數聲明都會被提升。但是,函式宣告會覆蓋變數聲明
var a;function a(){}console.log(a);//‘function a(){}‘
但是,如果變數存在賦值操作,則最終的值為變數的值
var a=1;function a(){}console.log(a);//1
var a;function a(){};console.log(a);//‘function a(){}‘a = 1;console.log(a);//1
[注意]變數的重複聲明是無用的,但函數的重複聲明會覆蓋前面的聲明(無論是變數還是函式宣告)
【1】變數的重複聲明無用
var a = 1;var a;console.log(a);//1
【2】由於函式宣告提升優先於變數聲明提升,所以變數的聲明無作用
var a;function a(){ console.log(1);}a();//1
【3】後面的函式宣告會覆蓋前面的函式宣告
a();//2function a(){ console.log(1);}function a(){ console.log(2);}
所以,應該避免在同一範圍中重複聲明
深入理解javascript範圍系列第三篇