执行上下文
1. 作用域
1.1 作用域的概念
作用域是指程序源代码中定义变量的区域。
作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
JavaScript 采用词法作用域(lexical scoping),也就是静态作用域
1.2 静态作用域与动态作用域
静态作用域,即词法作用域,函数的作用域在函数定义的时候就决定了,JavaScript是静态作用域
动态作用域,函数的作用域是在函数调用的时候才决定的,bash是动态作用域
2. 执行上下文
2.1 可执行代码
JavaScript 的可执行代码(executable code)的类型:
全局代码、函数代码、eval代码
与之对应也就有了执行上下文的分类:
全局执行上下文、函数执行上下文、Eval函数执行上下文
2.2 执行上下文分类
当执行一个函数的时候,就会进行准备工作,而这个准备工作更专业的说法就是执行上下文
全局执行上下文
任何不在函数内部的代码都在全局上下文中,全局执行上下文在浏览器情况下,会创建一个全局的widow对象,并且设置this的值等于window,一个程序中只会有一个全局执行上下文
当JavaScript开始解释执行代码的时候,最先遇到的是全局代码,因此,初始化的时候会向执行上下文栈ECStack压入一个全局执行上下文 globalContex,只有当整个应用程序结束的时候,ECStack 才会被清空,所以程序结束之前, ECStack 最底部永远有个 globalContext
函数执行上下文
当执行一个函数的时候,就会创建一个执行上下文funcContex,并且压入执行上下文栈ECStack,当函数执行完毕的时候,就会将函数的执行上下文funcContex从栈中弹出
如果函数中调用了另外一个函数,则在外层函数执行上下文创建之后,会创建内层函数执行上下文,内层函数调用结束之后,内层函数执行上下文从栈中弹出,外层函数执行成功之后,外层函数执行上下文从栈中弹出
Eval函数执行上下文
执行在 eval 函数内部的代码也会有它属于自己的执行上下文
2.3 执行上下文的属性
变量对象
变量对象是与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明
全局上下文的变量对象
全局上下文中的对象就是全局对象,可以访问所有其他所有预定义的对象、函数和属性
函数执行上下文的变量对象
当创建函数上下文后,还未执行代码:
变量对象会包括:
- 函数的所有形参 (如果是函数上下文)
- 由名称和对应值组成的一个变量对象的属性被创建
- 没有实参,属性值设为 undefined
- 函数声明()
- 由名称和对应值(函数对象(function-object))组成一个变量对象的属性被创建
- 如果变量对象已经存在相同名称的属性,则完全替换这个属性
- 变量声明
- 由名称和对应值(undefined)组成一个变量对象的属性被创建;
- 如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性
代码执行:
顺序执行代码,会完成变量赋值(修改变量的值),函数引用,以及执行其他代码
总结
- 全局上下文的变量对象初始化是全局对象
- 函数上下文的变量对象初始化只包括 Arguments 对象
- 在进入执行上下文时会给变量对象添加形参、函数声明、变量声明等初始的属性值
- 在代码执行阶段,会再次修改变量对象的属性值
举例:
console.log(foo); // function foo
function foo(foo) {
console.log("foo");
}
var foo = 1;
console.log(foo); // 1
// -----------------------------
var foo;
console.log(foo); /// function foo
function foo(foo) {
console.log("foo");
};
作用域链
当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链
函数的作用域在函数定义的时候就决定了原因:
函数有一个内部属性 [[scope]],当函数创建的时候,就会保存所有父变量对象到其中,
this
全局执行上下文中,this指向全局对象
函数执行上下文中,this取决于函数是如何被调用的,如果是被一个对象调用,则this指向该对象,否则this是全局对象(严格模式下this是undefined)