java script的變量聲明具有hoisting機制梢褐,JavaScript引擎在執(zhí)行的時候彻犁,會把所有變量的聲明都提升到當前作用域的最前面绰更。
先看一段代碼
var v ="hello";
(function(){
console.log(v);
var v ="world";
})();
這段代碼運行的結果是什么呢姨蟋?
答案是:undefined
這段代碼說明了兩個問題憨降,
第一,function作用域里的變量v遮蓋了上層作用域變量v母赵。代碼做少些變動
var v ="hello";
if(true){
console.log(v);
var v ="world";
}
輸出結果為”hello”,說明java script是沒有塊級作用域的逸爵。函數(shù)是JavaScript中唯一擁有自身作用域的結構。
第二凹嘲,在function作用域內师倔,變量v的聲明被提升了。所以最初的代碼相當于:
var v ="hello";
(function(){
var v; ? ? ? ? ? ? ?//declaration hoisting
console.log(v);
v ="world";
})();
聲明周蹭、定義與初始化
聲明宣稱一個名字的存在趋艘,定義則為這個名字分配存儲空間,而初始化則是為名字分配的存儲空間賦初值凶朗。
用C++來表述這三個概念
externinti;//這是聲明瓷胧,表明名字i在某處已經(jīng)存在了
inti;//這是聲明并定義名字i,為i分配存儲空間
i = 0;//這是初始化名字i,為其賦初值為0
javascript中則是這樣
var v;//聲明變量v
v ="hello";//(定義并)初始化變量v
因為 java script為動態(tài)語言,其變量并沒有固定的類型棚愤,其存儲空間大小會隨初始化與賦值而變化搓萧,所以其變量的“定義”就不像傳統(tǒng)的靜態(tài)語言一樣了,其定義顯得無關緊要宛畦。
聲明提升
當前作用域內的聲明都會提升到作用域的最前面瘸洛,包括變量和函數(shù)的聲明
(function(){
var a ="1";
var f =function(){};
var b ="2";
var c ="3";
})();
變量a,f,b,c的聲明會被提升到函數(shù)作用域的最前面,類似如下:
(function(){
var a,f,b,c;
a ="1";
f =function(){};
b ="2";
c ="3";
})();
請注意函數(shù)表達式并沒有被提升次和,這也是函數(shù)表達式與函數(shù)聲明的區(qū)別货矮。進一步看二者的區(qū)別:
(function(){
//var f1,function f2(){}; //hoisting,被隱式提升的聲明
f1();//ReferenceError: f1 is not defined
f2();
var f1 =function(){};
functionf2(){}
})();
上面代碼中函數(shù)聲明f2被提升,所以在前面調用f2是沒問題的斯够。雖然變量f1也被提升囚玫,但f1提升后的值為undefined,其真正的初始值是在執(zhí)行到函數(shù)表達式處被賦予的。所以只有聲明是被提升的读规。
名字解析順序
javascript中一個名字(name)以四種方式進入作用域(scope)抓督,其優(yōu)先級順序如下:
1、語言內置:所有的作用域中都有 this 和 arguments 關鍵字
2束亏、形式參數(shù):函數(shù)的參數(shù)在函數(shù)作用域中都是有效的
3铃在、函數(shù)聲明:形如function foo() {}
4、變量聲明:形如var bar;
名字聲明的優(yōu)先級如上所示碍遍,也就是說如果一個變量的名字與函數(shù)的名字相同定铜,那么函數(shù)的名字會覆蓋變量的名字,無論其在代碼中的順序如何怕敬。但名字的初始化卻是按其在代碼中書寫的順序進行的揣炕,不受以上優(yōu)先級的影響《颍看代碼:
(function(){
var foo;
console.log(typeoffoo);//function
function foo(){}
foo ="foo";
console.log(typeoffoo);//string
})();
如果形式參數(shù)中有多個同名變量畸陡,那么最后一個同名參數(shù)會覆蓋其他同名參數(shù)鹰溜,即使最后一個同名參數(shù)并沒有定義。
以上的名字解析優(yōu)先級存在例外丁恭,比如可以覆蓋語言內置的名字arguments曹动。
命名函數(shù)表達式
可以像函數(shù)聲明一樣為函數(shù)表達式指定一個名字,但這并不會使函數(shù)表達式成為函數(shù)聲明牲览。命名函數(shù)表達式的名字不會進入名字空間墓陈,也不會被提升。
f();//TypeError: f is not a function
foo();//ReferenceError: foo is not defined
var f =function foo(){console.log(typeof foo);};
f();//function
foo();//ReferenceError: foo is not defined
命名函數(shù)表達式的名字只在該函數(shù)的作用域內部有效第献。