1. 程式人生 > >關於js中變數作用於和變數提升的那些事

關於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>