js的作用域與作用域鏈的理解(轉)


  一直對Js的作用域有點迷糊,今天偶然讀到Javascript權威指南,立馬被吸引住了,寫的真不錯。我看的是第六版本,相當的厚,大概1000多頁,Js博大精深,要熟悉精通需要大毅力大功夫。

一:函數作用域

   先看一小段代碼:

 var scope="global";
function t(){
    console.log(scope);
    var scope="local"
    console.log(scope);
}
t();

第一句輸出的是: "undefined",而不是 "global"

第二講輸出的是:"local"

  你可能會認為第一句會輸出:"global",因為代碼還沒執行var scope="local",所以肯定會輸出“global"。

  我說這想法完全沒錯,只不過用錯了對象。我們首先要區分Javascript的函數作用域與我們熟知的java等的塊級作用域。

  在java中,花括號內中的每一段代碼都具有各自的作用域,而且變量在聲明它們的代碼段之外是不可見的。

而Javascript壓根沒有塊級作用域,而是函數作用域.

所謂函數作用域就是說:-》變量在聲明它們的函數體以及這個函數體嵌套的任意函數體內都是有定義的。

所以根據函數作用域的意思,可以將上述代碼重寫如下:

 var scope="global";
function t(){
    var scope;
    console.log(scope);
    scope="local"
    console.log(scope);
}
t();

第一句輸出的是: "undefined",而不是 "global"

第二講輸出的是:"local"

t函數里面的 scope聲明覆蓋了全局的scope , 但是還沒有賦值,所以輸出:”undefined“。

為什么說Js沒有塊級作用域呢,有以下代碼為證:

var name="global";
if(true){
    var name="local";
    console.log(name)
}
console.log(name);

都輸出是“local",如果有塊級作用域,明顯if語句將創建局部變量name,

並不會修改全局name,可是沒有這樣,所以Js沒有塊級作用域。

理解下面代碼就很容易了

function t(flag){
    if(flag){
        var s="ifscope";
        for(var i=0;i<2;i++)
            ;
    }
    console.log(i);
    console.log(s);
}
t(true);

結果是 2 ifscope

 

二:變量作用域

還是首先看一段代碼:

function t(flag){
    if(flag){
        s="ifscope";  //window.s   這個變量不加var 默認是全局變量的,而且是頂層對象的屬性。
        for(var i=0;i<2;i++)
            ;
    }
    console.log(i);
}
t(true);
console.log(s);

就上面的代碼來看,將聲明s的var去掉以后,程序會報錯嗎?

最后結果是:ifscope

這主要是js中沒有用var聲明的變量都是全局變量,而且是頂層對象的屬性

所以你用console.log(window.s) 也是會輸出:ifcobfig

i = 2; // 沒有
alert(window.i);

 

當使用var聲明一個變量時候,創建的這個屬性是不可配置的,也就是說無法通過delete運算符刪除的

var name = 1 ->不可刪除

sex = 'girl'   ->可刪除

this.age = 22  ->可刪除

 

三:作用域鏈

先來看一段代碼:

name="lwy";
function t(){
    var name="tlwy";
    function s(){
        var name="slwy";
        console.log(name); //slwy
    }
    function ss(){
        console.log(name); //tlwy
    }
    s();
    ss();
}
t();

console.log(name); //lwy

當執行s時,將創建函數s的執行環境(調用對象),並將該對象置於鏈表開頭,然后將函數t的調用對象鏈接在之后,最后是全局對象。然后從鏈表開頭尋找變量name,很明顯

name是"slwy"。

但執行ss()時,作用域鏈是: ss()->t()->window,所以name是”tlwy"

下面看一個很容易犯錯的例子:

<html>
<head>
<script type="text/javascript">
function buttonInit(){
    for(var i=1;i<4;i++){
        var b=document.getElementById("button"+i);
        b.addEventListener("click",function(){ alert("Button"+i);},false);
    }
}
window.onload=buttonInit;
</script>
</head>
<body>
<button id="button1">Button1</button>
<button id="button2">Button2</button>
<button id="button3">Button3</button>
</body>
</html>

當文檔加載完畢,給幾個按鈕注冊點擊事件,當我們點擊按鈕時,會彈出什么提示框呢?

很容易犯錯,對是的,三個按鈕都是彈出:"Button4",你答對了嗎?

當注冊事件結束后,i的值為4,當點擊按鈕時,事件函數即function(){ alert("Button"+i);}這個匿名函數中沒有i,根據作用域鏈,所以到buttonInit函數中找,此時i的值為4,

所以彈出”button4“。

 

四:with語句

說到作用域鏈,不得不說with語句。with語句主要用來臨時擴展作用域鏈,將語句中的對象添加到作用域的頭部。

看下面代碼

person={name:"yhb",age:22,height:175,wife:{name:"lwy",age:21}};
with(person.wife){
    console.log(name);
}
with語句將person.wife添加到當前作用域鏈的頭部,所以輸出的就是:“lwy".

with語句結束后,作用域鏈恢復正常。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM