《JavaScript权威指南》
MDN web docs
Github:smyhvae/web
作为一个前端小白,入门跟着这几个来源学习,感谢作者的分享,在其基础上,通过自己的理解,梳理出的知识点,或许有遗漏,或许有些理解是错误的,如有发现,欢迎指点下。
PS:梳理的内容以《JavaScript权威指南》这本书中的内容为主,因此接下去跟 JavaScript 语法相关的系列文章基本只介绍 ES5 标准规范的内容、ES6 等这系列梳理完再单独来讲讲。
正文-执行上下文EC和变量对象VOEC:Execution Context,中文翻译执行上下文,也有翻译成执行环境的。
VO:Variable object,中文翻译变量对象。
这两个概念很重要,涉及到作用域以及作用域链的原理。
执行上下文 EC先说说Android中的上下文:
Android
Android 中也有上下文:Context,四大组件都是上下文,还有一个全局的 Application上下文。在 Android 中基本是以四大组件为界限,每创建一个四大组件,都会产生一个上下文,比如每个 Activity 都是独立的上下文。
在 Android 中,上下文 Context 的作用大体上用于标识各种资源的所属,要加载一张图片、创建一个 View、弹一个 Dialog 等等,你需要告诉系统,这些是谁发出的指令,要挂载到哪个上下文,这些资源依赖于上下文的生命周期。
所以才会出现,有时弹 Dialog 或者更新某个 View 时抛异常说 Context 已销毁,因为它需要挂载的上下文已经销毁了,那么就没有上下文来统筹管理这些资源了,自然会抛异常。
JavaScript
在 JavaScript 中,上下文是指执行上下文,通俗点理解,代码执行的上下文,所以也有翻译成执行环境,可以通俗的把它理解成一个对象,对象名 EC,表示代码的执行上下文。
既然理解成一个对象,那么就有它创建的时机,在 JavaScript 中,每当要执行不同类型的代码时,就会创建一个执行上下文 EC。
而代码的类型分三种:
全局代码
函数代码
eval()执行的代码
最后一种不讨论,全局代码就是指写在函数外的代码,在前端里,当 HTML 加载一个 js 文件时,全局代码就会被执行,那么在全局代码执行前就会先创建一个全局的执行上下文 EC,之后每调用一次函数,要执行函数内的代码时,会再创建一个函数执行上下文 EC。
也就是说,不讨论 eval 的话,那么在 JavaScript 有两种执行上下文,一种是全局执行上下文,一种是函数执行上下文。
而每次创建一个执行上下文时,都会将其放入一个栈结构,这个栈就称为执行上下文栈(ECS),也有翻译成执行环境栈。
所以执行 js 文件代码期间,这个栈底一直是全局执行上下文,直到 js 文件代码执行结束。全局代码执行过程中,每调用一次函数,新创建一个函数执行上下文,就放入栈内。
因此,栈顶就表示当前执行的代码,如果栈顶是全局执行上下文,表示正在执行全局代码;如果栈顶是函数执行上下文,表示正在执行函数内的代码。当函数执行结束时,这个函数执行上下文就从栈中移出。
那么执行上下文(EC)有什么用呢?
用途可多了,跟 Android 不一样,Android 里由于是各种资源的组合使用,但在 JavaScript 中更多的是嵌套函数的变量使用。所以,用途之一就是保存各个变量。
将 EC 理解成一个对象的话,它有两个属性,一个是变量对象(VO),另一个是作用域链(Scope Chain)。对于全局执行上下文,当 HTML 加载一个 js 文件时,就会创建一个全局 EC,此时会创建它的两个属性:变量对象和作用域链。之后,每调用一次函数,创建这次函数执行的上下文,函数内部的变量的使用就依赖于这个函数执行上下文中的变量对象和作用域链。
也就是说,内部函数之所以可以使用外部函数内的变量,之所以可以使用全局变量,都是依赖于当前这个内部函数的执行上下文。
而且,变量之所以会提前声明也是因为执行上下文的因素。这些当讲解了执行上下文 EC 的创建过程就清楚了。
变量对象 VO变量对象只是一个抽象的概念,可以通俗的理解成保存当前上下文所有变量的对象。
在不同的执行上下文中,它有不同的具体表现。
在全局执行上下文中,变量对象 VO 的具体表现就是全局对象,因为所有的全局变量其实都是全局对象的属性,而变量对象 VO 的作用是要保存当前上下文中的所有变量,所以此时的变量对象 VO 实际上是指向的全局对象。
尤其在前端中,全局对象就是 window,所以全局执行上下文的变量对象 VO = window。
在函数执行上下文中,因为变量对象 VO 是要保存当前上下文中所有的变量,一个函数内的变量包括:形参变量、局部变量、自身函数对象引用变量、arguments、this。