作用域、作用域链、执行上下文、执行栈
前言
因为笔者在准备前端面试的过程中,对于作用域、作用域链、执行上下文、执行栈的概念时长感到力不从心、模糊不清,因此记录了这篇文章,深入探究以下。
作用域
说起作用域,其实分为两种:静态作用域、动态作用域,js是前者。
所谓静态作用域,无非就是在编译阶段就产生的。学过编译原理的朋友都知道,在此法分析解阶段,编译器会将你的代码解析成一个个token关键字,形成AST抽象词法树,静态作用域在此时就可创建完成,而动态作用域只有在函数执行时才确认,js中的this很像动态作用域的概念,玩家可以通过bind、call等方法动态改变this的指向(箭头函数除外,箭头函数本身没有this,其中的this经过babel翻译之后只不过是一个被赋值了外层this值的一个变量而已,在定义时就已经确定),这也变相弥补了js是静态作用域的缺陷。
也就是说作用域定义了一套代码执行时变量的一个访问规则。
js的作用域分为:
- 全局作用域
- 局部作用域(es6 let/const)
作用域链
为什么会有作用域链呢?js引入作用域链并非是多此一举(这里不要与js的原型链弄混),主要是为了提高js语言的容错率,在本层作用域未访问到的方法,会向上查找外层的作用域。知道找到全局作用域
举一个栗子:
1 | const a = 1 |
执行A函数,A函数中找不到关于a的声明,那么A就会沿着作用域链向上查找,知道找到变量a。
执行上下文
执行上下文与作用域不同,执行上下文是在代码运行的过程中创建的一个变量对象,这个对象我们无法直接访问,但是可以访问其中的变量、this对象等。
作用域是在函数声明的时候就确定的一套变量访问规则,而执行上下文时函数执行时才产生的一系列变量的环境,也就是说作用域定义了执行上下文中的变量访问规则(这句话很重要),执行上下文在这个作用域规则的前提下进行变量查找,函数引用等具体操作。
执行上下文分为两个阶段:
- 创建阶段
为了方便理解,可以将作用域和this指向当作执行上下文的组成部分。 - 执行阶段
执行栈
执行栈,在其他编程语言中也被称为“调用栈”,是一个具有后进先出结构的栈,它用于存储代码执行期间创建的所有执行上下文。
执行栈是存放执行上下文的一个栈,当新的函数执行时,那么该函数的执行上下文便会入栈,执行结束之后便会出栈。