一個變量的作用域(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>