作用域鏈和閉包

作用域鏈

作用域就是變量和函數的可訪問範圍,他包含全局變量和局部變量~我們知道的是JavaScript在執行語句的時候是有預解析的。

var a=3; //全局變量
    function fn(b){ //局部變量
        c=2; //全局變量
        var d=5; //局部變量
        function subFn(){
            var e=d; //父函數的局部變量對子函數可見
            for(var i=0;i<3;i++){
                console.write(i);
            }
            alert(i);//3, 在for循環內聲明,循環外function內仍然可見,沒有塊作用域
        }
    }
    alert(c); //在function內聲明但不帶var修飾,仍然是全局變量

javascript 是沒有塊級作用域的,但是他有預解析

    console.log(a); //undefined
    var a = 3;
    console.log(a); //3
    console.log(b); //Uncaught ReferenceError: b is not defined

引言——執行環境

執行環境(execution context)定義了變量或函數有權訪問的其它數據,決定了它們的各自行爲。每個執行環境都有一個與之關聯的變量對象(variable object, VO)
執行環境中定義的所有變量和函數都會保存在這個對象中,解析器在處理數據的時候就會訪問這個內部對象。
在這裏插入圖片描述
全局執行環境是最外層的一個執行環境,在web瀏覽器中全局執行環境是window對象,因此所有全局變量和函數都是作爲window對象的屬性和放大創建的。
每個函數都有自己的執行環境,當執行流進入一個函數的時候,函數的環境會被推入一個函數棧中,而在函數執行完畢後執行環境出棧並被銷燬,保存在其中的所有變量和函數定義隨之銷燬,控制權返回到之前的執行環境中,全局的執行環境在應用程序退出(瀏覽器關閉)纔會被銷燬。

例如上面的圖片~當我執行一次Fn的時候,就會產生一個需要執行Fn一次的任務,這個任務產生一個Fn的執行環境,裏面有變量b = 2,c = 3; 當這個任務被執行完,這些變量就會一起被銷燬

現在來說說作用域鏈

當代碼在一個環境中執行時,會創建變量對象的一個作用域鏈(scope chain,不簡稱sc)來保證對執行環境有權訪問的變量和函數的有序訪問。作用域第一個對象始終是當前執行代碼所在環境的變量對象(VO)

function a(x,y){
    var b = x + y;
    return b;
}
// 在這裏函數 a 的執行環境就是一個包含在 window 作用域下的一個分支

再來看看這段代碼:

	function a(x,y){
	    var b = x + y;
	    function c() {
			var d = 4;
		}
	    return b;
	 }
	var total = a(5,10);

在這裏插入圖片描述

再來看看閉包

只要存在調用內部函數的可能,JavaScript就需要保留被引用的函數。而且JavaScript運行時需要跟蹤引用這個內部函數的所有變量,直到最後一個變量廢棄,JavaScript的垃圾收集器才能釋放相應的內存空間。

	for(var i = 0;i < data.length; i++){
	    data[i].onclick = function (){
	          alert(i);
	      }
	  }

每次循環產生一個 onclick 的函數,這些函數隨時都有可能被執行,並且這些函數裏面需要被打印的變量 i 是父級作用域的值
在這裏插入圖片描述
所以最後打印出來的 i 都是 3,就是因爲子函數在執行的時候應用了父執行環境的變量~所以導致父級函數在執行完之後沒有被銷燬,而是繼續保留。

總結

例如在javascript中,只有函數內部的子函數才能讀取局部變量,所以閉包可以理解成“定義在一個函數內部的函數“。在本質上,閉包是將函數內部和函數外部連接起來的橋樑。

作用域 是針對變量的,比如我們創建了一個函數,函數裏面又包含了一個函數,那麼現在就有三個作用域

  • 全局作用域==>函數1作用域==>函數2作用域
    作用域的特點就是,先在自己的變量範圍中查找,如果找不到,就會沿着作用域往上找。

原型鏈 是針對構造函數的,比如我先創建了一個函數,然後通過一個變量new了這個函數,那麼這個被new出來的函數就會繼承創建出來的那個函數的屬性,然後如果我訪問new出來的這個函數的某個屬性,但是我並沒有在這個new出來的函數中定義這個變量,那麼它就會往上(向創建出它的函數中)查找,這個查找的過程就叫做原型鏈。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章