關於js中變數作用於和變數提升的那些事
一個變數的作用域(scope)是程式原始碼中定義這個變數的區域。
全域性變數擁有全域性作用域,在javaScript程式碼中任何地方都有定義的。
然而在函式內宣告的變數只是在函式內部有定義,他們是區域性變數,作用域也只是在區域性。
在函式體內,區域性變數的優先順序要高於全域性變數。如果在函式體內重新宣告一個與區域性變數重名的變數,區域性變數就會覆蓋全域性變數的值。
var scope="全域性變數";
function checkscope(){
var scope="區域性變數";
function nested(){
var scope = "巢狀作用域內的區域性變數";
alert(scope);//輸出:巢狀作用域內的區域性變數
}
nested();
alert(scope);//輸出:區域性變數
}
checkscope();
alert(scope);//輸出:全域性變數
從上面的例子中可以看出,區域性變數的作用域僅僅在函式內部,出了函式體之後,區域性變數就會被銷燬。
在nested()函式中,雖然又聲明瞭一個scope,但是nested()中的scope是區域性變數,只是與全域性變數的名字相同,並不是全域性變數,所以,雖然在該函式中把scope賦值為”巢狀作用域內的區域性變數”,但這僅僅是一個與全域性變數名稱相同的一個變數而已,並沒有改變全域性變數的值。
我們可以通過以下這個例子來進一步理解函式作用域的問題。
var scope="全域性變數";
function checkscope(){
var scope="區域性變數";
function nested(){
scope = "巢狀作用域內的區域性變數";
alert(scope);//輸出:巢狀作用域內的區域性變數
}
nested();
alert(scope);//輸出:巢狀作用域內的區域性變數
}
checkscope();
alert(scope);//輸出:全域性變數
看到這裡是不是有一些懵逼了,這和剛才不是一樣的嗎,為什麼第二次彈框不一樣了呢?
上面這部分程式碼中,在nested()函式中,我們並沒有用var來宣告scope,所以,在這裡的scope的作用域就被提升了,即我們將checkscope中的scope的值重置了,所以在輸出的時候輸出的結果為巢狀作用域內的區域性變數。
之前學習過c或java等其他程式語言的童鞋會知道,在c語言中會有塊級作用域這個概念。
C語言中塊級作用域是以成對的花括號來界定的,也就是說除了函式程式碼塊外,if、for等結構也屬於塊級作用域。
下面這個例子可以幫助我們理解一下:
#include <stdio.h>
int main() {
int x = 1;
printf("%d, ", x); // 1
if (1) {
int x = 2;
printf("%d, ", x); // 2
}
printf("%d\n", x); // 1
}
在c或c++、java中,變數的作用域是由成對的花括號來界定的,比如if中的x,在if中,x的值為2。但是,當程式執行出了if花括號以後,if中的變數x的作用域就結束了,並不會對if以外的x造成影響。但是這在js中是不一樣的~
弄明白了變數的作用域之後再來考慮變數提升就簡單多了。
在Javascript中,函式及變數的宣告都將被提升到函式的最頂部。
在js中,變數的宣告會被解析器悄悄的提升到方法體的最頂部,但是需要注意的是,提升的僅僅是變數的宣告,變數的賦值並不會被提升。
function foo() {
if (false) {
var x = 1;
}
return;
var y = 1;
}
function foo() {
var x, y;
if (false) {
x = 1;
}
return;
y = 1;
}
其實上面兩段程式碼是一模一樣的。
變數的宣告會被提升,賦值不會被提前。我們需要注意的是,函式的宣告與變數的宣告是不一樣的。函式的函式體也會被一起提升。但是,函式的宣告我們可以用兩種方法。
function test() {
var test1 = function () { // 變數指向函式表示式
alert("this is test1!");
}
function test2() { // 函式宣告 函式名為test2
alert("this is test2!");
}
}
test();
在上面這個例子中,對於test1來說,是聲明瞭一個變數,這個變數指向這個函式表示式,所以解析是會將var test1 提升,而後面對變數的賦值不會被提升。對於test2,解析是會把整個函式體一起提升。所以如果我們在函式體的開始執行兩個函式,test1報錯TypeError “test1 is not a function” ,test2則會彈出this is test2!。
我們可以用這個例子加深一下理解。
<script language="javascript" type="text/javascript">
//在全域性物件中宣告兩個全域性函式,反模式
function foo()
{
alert("global foo");
}
function bar()
{
alert("global bar");
}
//定義全域性變數
var v = "global var";
function hoistMe()
{
alert(typeof foo); //function
alert(typeof bar); //undefined
alert(v); //undefined
//為什麼bar函式和變數v是未定義而不是全域性變數中定義的相應的函式變數呢?
//因為函式裡面定義了同名的函式和變數,無論在函式的任何位置定義這些函式和
//和變數,它們都將被提升到函式的最頂部。
foo(); //local foo
bar(); //報錯,TypeError "bar is not a function"
//函式宣告,變數foo以及其實現被提升到hoistMe函式頂部
function foo()
{
alert("local foo");
}
//函式表示式,僅變數bar被提升到函式頂部,實現沒有被提升
var bar = function()
{
alert("local bar");
};
//定義區域性變數
var v = "local";
}
(function()
{
hoistMe();
})();
//函式表示式和變量表達式只是其宣告被提升,函式宣告是函式的宣告和實現都被提升。
/**由於函式提升的效果,hoistMe方法相當於
function hoistMe()
{
//函式宣告,變數foo以及其實現被提升到hoistMe函式頂部
function foo()
{
alert("local foo");
}
//函式表示式,僅變數bar被提升到函式頂部,實現沒有被提升(同變數提升)
var bar = undefined;
//變數宣告被提升
var v = undefined;
alert(typeof foo); //function
alert(typeof bar); //undefined
foo(); //local foo
bar(); //報錯,缺少物件
bar = function()
{
alert("local bar");
};
v = "local";
}
*/
</script>