什么是执行上下文?
了解前端作用域的第一步是先知道什么是执行上下文
执行上下文是js引擎在解析到一些可执行代码的时候所做的一些执行前准备,这个准备工作就被称执行上下文
作用:为代码执行前做必要的准备。例如变量对象的定义、作用域链的扩展、提供调用者对象的引用等
什么是作用域[[scope]]?
我们知道,每一个js函数是一个对象,对象是可以有属性的,对象中的有些属性我们都可以访问。但是有些隐藏属性,是我们外部永远不能访问的,这些属性只供js引擎存取,[[scope]]就是其中一个。[[scope]]就是作用域,其中储存了执行上下文的集合。
作用:作用域规定了如何查找变量,也就是确定了当前执行代码·对于变量的访问权限
什么是作用域链?
[[scope]]中储存的执行上下文集合是一个呈链式排布的链接,这个链式排布的链接称为作用域链
说明:由于直接讲理论可能会比较晦涩难懂,所以我这里用一个例子来给大家说明作用域和作用域链
知识小补充
- 执行期上下文AO:当函数执行时,会创建一个执行期上下文,放在作用域链的顶端;当函数执行完毕时,执行期上下文会被销毁。所以函数每次执行时产生的执行期上下文都是独一无二的,所以多次调用一个函数会创建多个执行期上下文。
- 全局上下文GO:这是默认的或基本的执行上下文,任何不在函数内部的代码都位于全局上下文中。
例子
我们根据就以下函数来一步步说明作用域以及作用域链的创建过程(以下按js引擎解析顺序来说明)
function a(){
function b(){
var b=234
}
var a=123
b()
}
var glob=100
a()
- a函数被定义时,作用域链只存在一个全局作用域GO——a的作用域
- 接着调用a,a函数执行会产生一个a的执行期上下文,并且将a的执行期上下文放在作用域链的顶端——a的作用域
- a函数执行时会创建一个b函数,此时b创建时所处的环境是在a执行时的作用域中,所以b创建时的作用域直接引用了a的执行作用域(也就是上图)——b的作用域
- b创建完成过后,接着会调用b()来调用b函数,此时b的执行会创建一个b的执行期上下文,排在链表顶端——b的作用域
- 此时b执行完毕,销毁自己的执行期上下文bAO——b的作用域
- b的调用完毕代表着a也调用完毕,所以a也会销毁自己的执行期上下文aAO——a的作用域
好啦!这就是我们这个例子里面各函数的作用域的变化过程。欢迎各位大佬指正!
补充知识:上述例子中a执行期的作用域链和b执行(或创建时)的作用域链中都包括了a函数的执行期上下文aAO,他们访问的是同一个aAO。也就是说,如果在b的执行期去改变了a函数中参数a=123的值,在a的执行期参数会被同步更改。
可能有点绕,简单一句话就是,在一次上述例子代码的执行中,js在解析和运行代码时,每个函数只会生成一个AO