【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)在浏览器关闭时,全局执行环境对象出栈

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