【JavaScript核心技術卷】函數代碼與執行模型和對象模型

函數代碼與執行模型和對象模型

一、[[Call]]代碼的三種調用方式

JavaScript是根據使用來區分[[Call]]代碼的,相當於Java中的實例構造函數、靜態方法、實例方法(而Java是在定義時區分的,JavaScript是根據調用方式來區分的;JavaScript只有實例方法,所以JavaScript沒有靜態構造函數調用)

new 實例構造函數調用
new FunctionName(…)—> [[Call]]
1)new obj 生成實例對象
2)[[Call]] :this = obj,構造函數初始化

函數調用
functionName(…):[[Call]]
1)非嚴格模式下的函數調用,this = window
2)嚴格模式下函數調用, this = undefined

方法調用
1)obj. functionName(…) :[[Call]]
2)this = obj

二、作用域鏈

只有在查找(訪問、賦值)標識符(變量)的時候纔會用到作用域鏈,使用var創建新的變量的時候不使用作用域鏈。

1)變量聲明語句

var obj;

① 如果本地變量對象中,不存在該變量,則在本地變量對象中增加該變量。變量初始化的值爲undefined,表示未知。
② 如果本地變量對象中,存在該變量,則忽略變量聲明語句。

2) 變量訪問表達式

obj;

① 如果在作用域鏈中,存在該變量,則返回該變量的值
② 如果在作用域鏈中,不存在該變量,則拋出異常
編程建議:變量應該先聲明後使用

3) 變量賦值語句

obj = value;

① 如果在作用域鏈中,存在該變量,則將值賦於該變量
② 如果在作用域鏈中,不存在該變量:

  • 非嚴格模式下,則在window對象中,增加該變量,並將值賦於該變量
  • 嚴格模式下,則拋出異常

編程建議:變量應該先聲明後使用

變量與屬性的對比:

1) 屬性訪問表達式

obj.prop

① 如果在該對象中,存在該屬性,則返回該屬性的屬性值。
② 如果在該對象中,不存在該屬性,則返回undefined

2) 屬性賦值語句

obj.prop = value;

① 如果在該對象中,存在該屬性,則將值賦於該屬性
② 如果在該對象中,不存在該屬性,則在該對象中,增加該屬性,並將值賦於該屬性

三、活動對象

與函數執行環境對象關聯的變量對象稱爲活動對象(Activation Object),函數執行結束,函數執行環境對象將退棧。函數的活動對象:

  1. 是JS引擎內部的特殊數據結構
  2. 我們編寫的JS代碼無法訪問這個變量對象,但解釋器在處理數據時會在後臺使用它。
  3. 沒有原型對象:活動對象的隱式屬性[[Prototype]] = null
  4. 程序可以操作活動對象中的變量成員

四、函數執行過程

函數執行過程:
1、創建函數執行環境
2、掃描函數代碼,提升函數聲明、變量聲明
3、執行函數代碼
4、函數執行完畢,函數的執行環境對象出棧
5、產生垃圾,等待垃圾回收器GC回收。當堆中的一個值失去引用之後,就會被標記爲垃圾。
6、滿足條件,GC啓動,開始回收垃圾
① 回收垃圾並釋放空間
② 然後進行碎片整理

這就是爲什麼沒有GC的語言中,地址叫指針,指針是靜態地址;而含有GC的語言中,由於GC將進行碎片整理,所以地址叫引用的原因所在,引用是動態地址。

(1)代碼清單

FEC.html

<!DOCTYPE html>
<html>
<head>
    <title>Execution Context Example 2</title>
    <script type="text/javascript">
          
 		//創建全局執行環境(由引擎自動創建)
		//預編譯/掃描全局代碼,提升函數聲明、變量聲明
		//執行全局代碼1    
		var color = "blue";
		var name  = "藍色";
        
        function changeColor(){
	   
 	    	//預編譯/掃描changeColor()函數體代碼,提升函數聲明、變量聲明
 	    	
            //執行changeColor()函數體代碼1
            var anotherColor = "red";
        
 	    	function swapColors(name){
	    	 	//預編譯/掃描swapColors()函數代碼,提升函數聲明、變量聲明
		     	//執行swapColors()函數體代碼
                var tempColor = anotherColor;
                anotherColor = color;
                color = tempColor;
				window.name = name;
                
                //color, anotherColor, and tempColor are all accessible here

           		//swapColors()函數執行完畢,swapColors()函數執行環境對象出棧
				//等待垃圾回收
            } 
        
            //color and anotherColor are accessible here, but not tempColor   
     
            //調用swapColors()函數代碼
	    	//創建swapColors()函數執行環境
            swapColors("紅色");

	    //changeColor()函數執行完畢,changeColor()函數執行環境對象出棧
	    //垃圾回收
        }
        
		//調用changeColor()函數代碼
		//創建changeColor()函數執行環境 
        changeColor();

		//全局代碼執行完畢,全局執行環境對象留在棧中,等待下一個JavaScript腳本的執行。
      	//在瀏覽器關閉時,全局執行環境對象出棧
    </script>


</head>
<body>
  
</body>
</html>

下面開始執行全局代碼

(2)創建全局執行環境(由引擎自動創建) Global EC

進程及其堆、線程及其執行環境棧、全局執行環境(包括棧楨ECO、執行作用域鏈 Scope Chain、全局變量對象 window對象/Global object對象)在瀏覽器啓動時建立。在ECMAScript程序執行之前宿主中就已經存在了。

執行環境棧(ECS Execution Context Stack) 存放的是執行環境對象,引擎在初始化後,將全局執行環境對象(Global Execution Context Object)壓棧,引擎會創建好全局執行環境對象的執行作用域鏈Scope Chain。

GEC 全局執行環境(Global Execution Context) 內容

1、棧楨內容:執行環境對象(ECO Execution Context Object)

成員: (scope chain), this

  • (scope chain):執行作用域(鏈)變量,引用執行作用域鏈
  • this:在GECO中,this變量(邏輯上可以看作隱式的全局形參變量),引用window對象(window Context Object)。

this是關鍵字,是隻讀的,不能賦值。

2、執行作用域鏈:

Scope Chain(Scope概念):執行作用域鏈/執行作用域是內部數據結構。可能是鏈表/列表結構。裏面每個成員都是Variable object,程序無法訪問,只有JS引擎可以訪問。

變量對象(Variable Object VO):作用域鏈引用的對象統稱爲變量對象。

3、全局變量對象:

在全局執行環境中,全局變量對象被叫Global Object對象 / window 對象(window的隱式屬性[[Prototype]] —> Object.prototype )。

ECMA規範規定其運行環境都必須存在一個唯一的全局對象Global Object, Global Object一定是一個宿主對象,由宿主實現,ECMA規範對它沒有額外要求。在Web中,爲window對象。

JS的全局變量和JS的全局函數(例如:Math、String、Date、parseInt等JavaScript中內置的全局對象和函數),宿主的全局變量和宿主的全局函數均存放於window對象中。

FEC 函數執行環境(Function Execution Context) 內容

1、棧楨內容ECO:執行環境對象(Execution Context Object)

  • (scope chain) :執行作用域(鏈)變量,引用執行作用域鏈
  • this:隱式的函數形參變量

1)方法調用:

this引用方法被調用的對象(Function Context Object,函數的環境對象)

2)函數調用

① 嚴格模式下的函數調用,this形參的值爲 undefined
② 非嚴格模式下的函數調用,this形參引用window對象

3)new 實例構造函數調用

this引用新創建的對象

this是關鍵字,是隻讀的。

編程建議:

  • 如果用於函數調用,函數體內部不要使用this隱式形參
  • 如果用於方法調用或new新對象時,函數體內部可以使用this隱式形參。

執行作用域鏈:

  • Scope Chain:是內部數據結構。可能是鏈表/列表。
  • 程序無法訪問,只有JS引擎可以訪問。

局部變量對象:

  • 在函數執行環境中,局部變量對象被叫做活動對象。
  • 是JS的內部數據結構。
  • 程序無法訪問,只有JS引擎可以訪問。

函數的局部變量和函數的形參以及arguments(存放實參)變量存放於活動對象中。Arguments引用實參對象([[Prototype]]:Object.protorype)。

在這裏插入圖片描述

(3)掃描全局代碼,提升函數聲明、變量聲明

1、確定JS代碼的執行模式
2、提升函數聲明、變量聲明,將它們放到源代碼樹的頂部,首先執行聲明語句。

  • 如果是兩個同名函數聲明,出現在後面的函數聲明可以覆蓋前面的,前面聲明的同名函數將成爲垃圾。
  • 如果是兩個同名變量聲明,出現在後面的變量聲明將被忽略。

函數聲明會首先被提升,然後纔是變量聲明。

上面例子的執行順序實際爲(解析後的僞碼):

<!DOCTYPE html>
<html>
<head>
    <title>Execution Context Example 2</title>
    <script type="text/javascript">
         
		
 		//創建全局執行環境(由引擎自動創建)

		//第一遍掃描後,將聲明放到源代碼樹的頂部
		//第二遍開始執行,先執行聲明語句,將它們添加到執行環境中。

		//掃描全局代碼,提升函數聲明、變量聲明
		//執行提升的函數聲明、變量聲明

		//函數聲明完成兩個步驟:
		//①聲明函數名稱變量,
			//	函數名稱changeColor僅僅是一個變量而已,增加到本地變量對象中
			//	var changeColor; //初始化值爲undefined,表示未知
		//②將新創建的函數對象賦予函數名稱變量changeColor
		function changeColor(){
	  
 	    	//掃描changeColor()函數體代碼,提升函數聲明、變量聲明
            //執行changeColor()函數體代碼1
            var anotherColor = "red";
        
 	    	function swapColors(name){
	    	 	//掃描swapColors()函數代碼,提升函數聲明、變量聲明
		     	//執行swapColors()函數體代碼
                var tempColor = anotherColor;
                anotherColor = color;
                color = tempColor;
				window.name = name;
                
                //color, anotherColor, and tempColor are all accessible here

           		//swapColors()函數執行完畢,swapColors()函數執行環境對象出棧
				//等待垃圾回收
            } 
        

            //color and anotherColor are accessible here, but not tempColor   
     
            //調用swapColors()函數代碼
	    	//創建swapColors()函數執行環境
            swapColors("紅色");

	    //changeColor()函數執行完畢,changeColor()函數執行環境對象出棧
	    //垃圾回收
        }

		//變量聲明時,
			//①如果本地變量對象中未存在color變量,則將color變量增加到本地變量對象中。
			//②如果本地變量對象中存在changeColor變量,則忽略該變量聲明語句。
		var color; 	//初始化爲undefined,表示未知
		var name;	//初始化爲undefined,表示未知
        

		//執行全局代碼1    
		color = "blue";
		name  = "藍色";
          

		//執行全局代碼2

		//調用changeColor()函數代碼
		//1、創建changeColor()函數執行環境 
		//2、掃描提升函數聲明、變量聲明
		//3、執行changeColor()函數的JS代碼
		//4、changeColor()函數執行完畢,函數執行環境對象退棧,
		//   changeColor()函數對象成爲垃圾,等待GC回收
        changeColor();

		//全局代碼執行完畢,全局執行環境對象留在棧中,
		//等待下一個JavaScript腳本的執行。
      	//在瀏覽器關閉時,全局執行環境對象出棧
    </script>
</head>
<body>
  
</body>
</html>

(4)執行提升的函數聲明和變量聲明

將當前執行作用域鏈拷入函數的靜態作用域鏈

在這裏插入圖片描述

(5)執行全局代碼1

在這裏插入圖片描述
函數聲明完成兩個步驟:

①聲明函數名稱變量

  • 函數名稱changeColor僅僅是一個變量而已,增加到本地變量對象中
  • var changeColor;初始化值爲undefined,表示未知

②將新創建的函數對象賦予函數名稱變量changeColor

(6)執行全局代碼2

調用changeColor()函數代碼:

創建changeColor()函數執行環境 changeColor EC

changeColor這裏主要涉及兩個對象,一個是環境對象ECO、另一個是活動對象AO

changeColor ECO涉及的內容有(scope chain)變量/Scope Chain 作用域鏈、this/function context object

changeColor AO([[Prototype]]:null)涉及的內容有 arguments變量/arguments對象([[Prototype]]:Object.protorype) (實參)、形參、局部變量。

創建changeColor()函數執行環境的流程:

① 創建一個arguments實參對象,使用實參列表進行設置,JS代碼可以訪問。

② 創建一個changeColor()函數的 Activation Object活動對象,爲一內部數據結構,JS代碼無法訪問。
a) 將arguments設置爲Activation Object的屬性,引用arguments實參對象。
b) 將形參設置爲Activation Object的屬性,使用實參列表進行初始化。
c) 運行過程中聲明的變量將成爲Activation Object的屬性。

③ 創建一個新的Scope Chain執行作用域鏈
a) 將changeColor()函數的靜態作用域鏈拷入該Scope Chain。
b) 在該Scope Chain增加一個新成員,引用changeColor()函數的Activation Object活動對象

④ 創建一個changeColor()函數的執行環境對象,然後壓棧。
a) (scope chain) 作用域(鏈)成員引用Scope Chain執行作用域鏈。
b) this成員:非嚴格模式下,引用window對象;嚴格模式下,其值爲undefined

在這裏插入圖片描述

掃描changeColor()函數代碼,提升函數聲明、變量聲明

changeColor()函數的執行順序實際爲(解析後的僞碼):

		  function changeColor(){
	    	//掃描changeColor()函數體代碼,提升函數聲明、變量聲明
      		function swapColors(name){
	    	 	//掃描swapColors()函數代碼,提升函數聲明、變量聲明
		     	//執行swapColors()函數體代碼
                var tempColor = anotherColor;
                anotherColor = color;
                color = tempColor;
				window.name = name;
                
                //color, anotherColor, and tempColor are all accessible here

           		//swapColors()函數執行完畢,swapColors()函數執行環境對象出棧
				//等待垃圾回收
            } 

 	
            var anotherColor;	//初始化爲undefined,表示未知


        	//執行changeColor()函數體代碼1
             anotherColor = "red";
                

			//執行changeColor()函數代碼2
        	//調用swapColors ()函數代碼
			//1、創建swapColors ()函數執行環境 
			//2、掃描提升函數聲明、變量聲明
			//3、執行swapColors ()函數的JS代碼
			//4、swapColors函數執行完畢,函數執行環境對象退棧,
			//   changeColor()函數對象成爲垃圾,等待GC回收
      		swapColors("紅色");
           //color and anotherColor are accessible here, but not tempColor   

	    	//changeColor()函數執行完畢,changeColor()函數執行環境對象出棧
	    	//垃圾回收
        }

執行changeColor()函數代碼1

將當前執行作用域鏈拷入函數的靜態作用域鏈
在這裏插入圖片描述

執行changeColor()函數代碼2

調用swapColors(“紅色”)函數代碼:

創建swapColors()函數執行環境 swapColors EC,與changeColor相似,涉及一個活動對象和一個環境對象。

swapColors ECO內容有、(scope chain)/Scope Chainthis/function context。

swapColors AO([[Prototype]]:null)內容有arguments/arguments對象(實參)、形參、局部變量。

創建過程:

① 創建一個arguments實參對象,使用實參列表進行設置,JS代碼可以訪問。

② 創建一個changeColor()函數的Activation Object活動對象,爲一內部數據結構,JS代碼無法訪問。
a) 將arguments設置爲Activation Object的屬性,引用arguments實參對象。
b) 將形參設置爲Activation Object的屬性,使用實參列表進行初始化。
c) 運行過程中聲明的變量將成爲Activation Object的屬性。

③ 創建一個新的Scope Chain執行作用域鏈
a) 將changeColor()函數的靜態作用域鏈拷入該Scope Chain。
b) 在該Scope Chain增加一個新成員,引用changeColor()函數的Activation Object活動對象

④ 創建一個changeColor()函數的執行環境對象,然後壓棧
a) (scope chain) 作用域(鏈)成員引用Scope Chain執行作用域鏈。
b) this成員:非嚴格模式下,引用window對象、嚴格模式下,其值爲undefined。

在這裏插入圖片描述

關鍵點
我們注意到swapColor的執行上下文的ScopeChain有0、1、2。其中0指向window,1指向其上一級執行環境,2纔是自己。
這個的意義是,如果用到上一級環境的信息,可以通過這個進行一個切換(即搜索),比如使用name變量,首先在1中找,然後再在0中找。

掃描swapColors()函數代碼,提升函數聲明、變量聲明

函數的執行順序實際爲(解析後的僞碼):

			function swapColors(name){
	    	 	//掃描swapColors()函數代碼,提升函數聲明、變量聲明
		     	//執行swapColors()函數體代碼
                var tempColor;  //初始化爲undefined,表示未知

				tempColor = anotherColor;
                anotherColor = color;
                color = tempColor;
				window.name = name;
                
                //color, anotherColor, and tempColor are all accessible here

           		//swapColors()函數執行完畢,swapColors()函數執行環境對象出棧
				//等待垃圾回收
            } 

執行swapColors()函數體代碼
在這裏插入圖片描述

swapColors()函數執行完畢,swapColors()函數執行環境對象出棧

產生垃圾,等待垃圾回收器回收
在這裏插入圖片描述

changeColor()函數執行完畢,changeColor()函數執行環境對象出棧

產生垃圾,等待垃圾回收器回收

(7)全局代碼執行完畢,全局執行環境對象留在棧中,等待下一個JavaScript腳本的執行。

(8)垃圾回收機制:

1) 當一個值失去引用之後就會被標記爲垃圾
2) GC啓動,回收垃圾並釋放空間,然後碎片整理

在這裏插入圖片描述

(9)在瀏覽器關閉時,全局執行環境對象出棧

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