1.1. 执行上下文和作用域
执行上下文(简称上下文)的概念在JS中比较重要.
执行上下文描述了 代码运行的环境,包括代码在哪儿执行、可以用哪些变量、函数、对象等。可以理解为“JS 代码执行时的工具箱”,里面有三样工具:
1. 你在代码里声明的 变量 和 函数。
2. 作用域链,可以让你访问到外面的变量。
3. this 的值,决定了当前代码的“主人”是谁。
执行上下文就像餐馆的工作台。
• 变量和函数是你工作台上的食材和工具。
• 作用域链是你厨房的储藏室,可以去拿到一些外部的资源。
• this 是“当下谁在干活”,比如是厨师自己还是服务员。
JS 运行代码时,会先创建执行上下文(工作台),然后再执行代码(利用工作台完成工作), 代码执行结束后,这个执行上下文销毁(工作台清空)。 全局执行上下文(整个餐馆)继续维持,因为代码还在全局环境下运行(餐厅其他工作继续正常进行)。
1.上下文与变量对象
每一个上下文都有一个关联的变量对象(每一个工具台都有一个专属的食材工具篮)(变量对象是一个抽象的概念,表示与某个上下文关联的数据环境,上下文和变量对象一一对应的),而这个上下文定义的所有变量和函数都存在于这个变量对象上(如果是函数上下文还包括该函数的形参). 虽然无法通过代码访问变量对象,但后台数据都会用到它.
2.全局上下文与函数上下文
最外层的上下文是 全局上下文.根据ECMAScript实现的宿主环境,表示全局上下文的对象可能不一样. 如果宿主环境是浏览器,那么全局上下文就是window对象. 所有通过var定义的全局变量和函数都会成为window对象的属性和方法.(let和const不会)
每个函数调用都会有自己的上下文(即函数上下文).当代码执行流 进入函数时,函数的上下文会被推到一个上下文栈上.在函数执行完之后, 上下文栈会弹出这个函数上下文.将控制权返回给之前的执行上下文. 上下文 在其所有代码都执行完毕后 会被销毁, 包括定义在它上面的所有变量和函数( 全局上下文在应用程序退出前 才会被销毁,比如关闭网页或退出浏览器)
ECMAScript程序的执行流,就是通过这个上下文栈进行控制的.
3.变量对象与作用域链
上下文中的代码在执行的时候,会创建变量对象的一个作用域链. 这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序.
- 代码正在执行的上下文的变量对象始终位于作用域链的最前端.
如果是函数上下文,那么其活动对象用作变量对象.活动对象最初只有一个定义变量:arguments.
- 作用域链中的下一个变量对象来自包含上下文,再下一个对象来自再下一个包含上下文,以此类推直至全局上下文.
- 全局上下文的变量对象 始终是作用域链的最后一个变量对象.
4. 标识符与作用域链
代码执行时的标识符解析,是通过沿作用域链逐级搜索标识符名称完成的.(如果没有找到标识符,那么通常会报错)(标识符 就是变量、函数、属性或函数参数的名称。)
如上图所示 代码涉及3个上下文.
全局上下文中 有一个变量color和一个函数changeColor();
changeColor()的局部上下文中 有一个变量 anotherColor 和 一个函数swapColors(); 在这里可以访问全局上下文中的变量color;
swapColors()的局部上下文中 有一个变量tempColor. 该变量只能在这个局部上下文中访问到.在这里可以访问到另外两个上下文中的变量,因为他们都是父级上下文.
每个上下文都可以到 上一级上下文中去搜索变量和函数. 例如swapColors()局部上下文的作用域链中有3个对象: swapColors()的变量对象、changeColor()的变量对象、全局变量对象。
5. 作用域链增强
执行上下文主要有全局上下文和函数上下文两种。但是 也有其他方式来增强作用域链。即某些语句会导致在作用域链前端临时添加一个上下文,这个上下文在代码执行后会被删除。
通常在两种情况下 会出现这个现象。
① try/catch语句的catch块 ② with语句
这两种情况都会在作用域链前端添加一个变量对象
对catch语句而言, 会在作用域链前端临时添加一个上下文和一个对应的变量对象, 这个变量对象会包含 要抛出的错误对象 的声明.这样,就只能在catch块内部才能访问到这个错误对象了.