Javascript閉包與作用域

jopen 10年前發布 | 15K 次閱讀 JavaScript開發 JavaScript

1.Javascript的作用域是函數作用域而非塊級作用域

//C語言
#include <stdio.h>
void main()
{
   int i=2;
   i--;
   if(i)
   {
       int j=3;
   }
   printf("%d/n",j);      //use an undefined variable:j
}

這是因為c中的作用域是塊級的,j是在if后的{ }中定義的,所以無法訪問,然而在js中會是什么情況?

    (function(){
        var i=1;
        if(i==1){
            var j=3;
        }
        console.log(j);     //3
    })()

在這里,j是可以訪問的,也就是說在一個函數中的任何位置定義的變量在該函數中的任何地方都是可見的

這里提及一句Javascript的作用域鏈(scope chain),每個函數定義時都會將他的作用域鏈定設為他定義的環境

function a(){
    function b(){
        //code
    }
}

這段代碼中,b的環境為a,a的環境為全局(window),在b中查找變量時會先搜索自身函數內部,如果不存在就去a的內部查找,還不存在就去全局中查找,若還是找不到就是undefined,這就構成一條鏈

2.Javascript中變量的作用域分為全局變量和局部變量

在函數內部可以訪問全局變量和函數內的局部變量,而在函數外部訪問不到函數內的變量,看代碼

var p=11;
function f1(){
   console.log(p);
}
f1();   //11

function f1(){
    var p=11;
}
f1();
console.log(p);  //ReferenceError: p is not defined

通過這倆段代碼可以理解全局變量和局部變量,但是定義局部變量時一定要注意加上var,如果不加上其實定義的是一個全局變量,看代碼

function f1(){
    p=11;
}
f1();
console.log(p);       //11

3.那如何訪問函數內部的變量并對它進行操作呢?這里就需要用到閉包

先看看閉包的官方解釋:閉包是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分

看到這句戶我不禁想問,這是個啥?

后來參考了一些博客和《Javascript秘密花園》才開始理解,閉包大概就是函數內部的一個函數被外部調用,這樣就可以調用內部變量了,比如下面這段

function f1(){
    var p=11;
        return {
            increment: function() {
                p++;
            },
    
            show: function() {
                alert(p)
            }
        }
}
var f=f1();
f.show();      //11
f.increment();
f.show();      //12

先看一下控制臺,f是什么樣的

這里可以看到,f包含increment和show兩個函數,而這兩個函數是f1的內部函數所以可以訪問p這個變量,在我理解,這里的increment和show就是f1()的兩個閉包,用他們就可以從外部調用這個變量

4.閉包可以做些什么?

首先我覺得可以模擬private,就像上面那段代碼,這個變量只能在這個函數內部訪問,也只有使用了閉包才能訪問

第二,和Javascript的垃圾回收有關,這里我還不是很清楚,等到搞明白了再來補上

5.這里有一個要注意的就是循環中使用閉包的問題,這里借用《Javascript秘密花園》里的一個例子

function f1(){
  for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);  
    }, 1000);
  }
}
f1();

這段代碼輸出的是10個10而不是期望的0到9,因為閉包內是對i的引用,然后函數執行時i已經變成了10,這里可以使用自執行的匿名函數

function f1(){
  for(var i = 0; i < 10; i++) {
     (function(e) {
        setTimeout(function() {
            console.log(e);  
        }, 1000);
    })(i);

  }
}
f1();

這里的匿名函數將i作為參數,這里的e會有i的一個拷貝,而引用時是對e的引用,這就避免了上述的問題

來自:http://my.oschina.net/369679209/blog/311849

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!