1. 程式人生 > >Javascript 進階 作用域 作用域鏈

Javascript 進階 作用域 作用域鏈

轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/25076713

一直覺得Js很強大,由於長期不寫js程式碼,最近剛好溫故溫故。

1、Javascript沒有程式碼塊作用域的概念,區域性作用域是針對函式來說的。

        function fun()
        {
            for( var i = 0 ; i < 10 ; i++)
            {}
            //如果在Java中i此時應當屬於未宣告的變數,但是Js中i的作用域依然存在
            console.log(i);//10

            if(true)
            {
                var b = "helloworld";
            }
            console.log(b);//helloworld
        }
        fun();
2、如果不使用var宣告的變數,預設為全域性變數
        function fun02()
        {
            a = "helloworld";
            var b = "welcome";
        }
        fun02();
        console.log(a); //     helloworld
        console.log(b); //   b is not defined

3、Js中的作用域鏈

先看個簡單的例子:只有一個函式物件,函式物件和其它物件一樣,擁有可以通過程式碼訪問的屬性和一系列僅供JavaScript引擎訪問的內部屬性。其中一個內部屬性是[[Scope]],由ECMA-262標準第三版定義,該內部屬性包含了函式被建立的作用域中物件的集合,這個集合被稱為函式的作用域鏈,它決定了哪些資料能被函式訪問。

  var a = "hello";

        function fun04()
        {
             a = "world";
              var b ="welcome";
        }
作用域鏈的圖:


注:圖中省略了,Global Scope中的window,document等,每個函式物件中的arguments,this等均未畫出。

  function fun03()
        {
            var a = 10;
            return function(){
                a*= 2 ;
                return a ;
            };
        }

        var f = fun03();
        f();
        var x = f();
       console.log(x);  //40

        var g = fun03();
        var y = g();
        console.log(y); //20

觀察上面程式碼,存在fun03,f,g三個函式物件。

下面是作用域鏈的圖:


注:每個函式物件一個作用域鏈,這裡直接畫在了一起;對於變數的查詢,先從鏈的0開始找。

函式物件 f 在程式碼中執行了2 次,所以a*2*2 = 40 ; 函式物件 g 在程式碼中執行了1次, 所以 a *2 = 20 ; 

4、閉包

上面的例子可以看到,在fun03執行完成後,a的例項並沒有被銷燬,這就是閉包。個人對閉包的理解是:函式執行完成後,函式中的變數沒有被銷燬,被它返回的子函式所引用。

下面以一個特別經典的例子,同時使用作用域鏈解析:

window.onload = function()
        {
            var elements = document.getElementsByTagName("li");
            for(var i = 0; i < elements.length ; i ++)
            {
                elements[i].onclick = function()
                {
                    alert(i);
                }
            }

        }

相信上面的程式碼肯定大家都寫過,本意是點選每個li,打印出它們的索引,可是事實上打印出的都是elements.length。這是為什麼呢?


看下上面的簡易的作用域鏈(省略了很多部分,主要是理解),此時每個onclick函式的i,指向的都是 onload 中的i 此時的 i = element.length.

下面看解決方案:

 window.onload = function ()
        {
            var elements = document.getElementsByTagName("li");
            for (var i = 0; i < elements.length; i++)
            {
                (function (n)
                {
                    elements[n].onclick = function ()
                    {
                        alert(n);
                    }
                })(i);
            }

        }

在onclick函式的外層,包了一層立即執行的函式,所以此時的n指向的 n 是立即執行的,所有都是1~elements.length 。