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;
① 如果在作用域链中,存在该变量,则将值赋于该变量
② 如果在作用域链中,不存在该变量:
编程建议:变量应该先声明后使用
变量与属性的对比:
1) 属性访问表达式
obj.prop
① 如果在该对象中,存在该属性,则返回该属性的属性值。
② 如果在该对象中,不存在该属性,则返回undefined
2) 属性赋值语句
obj.prop = value;
① 如果在该对象中,存在该属性,则将值赋于该属性
② 如果在该对象中,不存在该属性,则在该对象中,增加该属性,并将值赋于该属性
与函数执行环境对象关联的变量对象称为活动对象(Activation Object),函数执行结束,函数执行环境对象将退栈。函数的活动对象:
函数执行过程:
1、创建函数执行环境
2、扫描函数代码,提升函数声明、变量声明
3、执行函数代码
4、函数执行完毕,函数的执行环境对象出栈
5、产生垃圾,等待垃圾回收器GC回收。当堆中的一个值失去引用之后,就会被标记为垃圾。
6、满足条件,GC启动,开始回收垃圾
① 回收垃圾并释放空间
② 然后进行碎片整理
这就是为什么没有GC的语言中,地址叫指针,指针是静态地址;而含有GC的语言中,由于GC将进行碎片整理,所以地址叫引用的原因所在,引用是动态地址。
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>
下面开始执行全局代码
进程及其堆、线程及其执行环境栈、全局执行环境(包括栈桢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
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)
1)方法调用:
this引用方法被调用的对象(Function Context Object,函数的环境对象)
2)函数调用
① 严格模式下的函数调用,this形参的值为 undefined
② 非严格模式下的函数调用,this形参引用window对象
3)new 实例构造函数调用
this引用新创建的对象
this是关键字,是只读的。
编程建议:
执行作用域链:
局部变量对象:
函数的局部变量和函数的形参以及arguments(存放实参)变量存放于活动对象中。Arguments引用实参对象([[Prototype]]:Object.protorype)。
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>
将当前执行作用域链拷入函数的静态作用域链
①声明函数名称变量
var changeColor;
初始化值为undefined,表示未知②将新创建的函数对象赋予函数名称变量changeColor
调用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()函数执行环境对象出栈
changeColor()函数执行完毕,changeColor()函数执行环境对象出栈
产生垃圾,等待垃圾回收器回收
1) 当一个值失去引用之后就会被标记为垃圾
2) GC启动,回收垃圾并释放空间,然后碎片整理