js没那么简单(2)--作用域链

651 阅读4分钟

前言

作用域链是js的一个概念,和执行上下文相关。关于执行上下文的描述,在上一篇文章《js没那么简单(1)-- 执行上下文》已经说了。

讨论作用域链的意义在于:

  1. js的作用域关系和作用域链息息相关
  2. 作用域链是闭包的基础
  3. 作用域链对后面理解垃圾回收也有一定关系

静态vs动态

讨论作用域链之前,不妨先来搞几个头脑风暴。关于静态和动态,我们经常会听到,js是一门动态语言,我们也经常会听到,js是一门静态作用域语言,我们还经常听到,CMD是一种动态模块规范。于是乎,我们发现,静态和动态,似乎,在不同语义下,对同个语言描述也是不同的。

  1. 我们之所以说js是动态编程语言,是基于js的变量定义描述的,意思是,js可以在运行时变量类型是允许改变的。噢,很好理解,因为js的var确实支持运行时不同类型的改变。如一下代码:
var a = 1
a = 'string'

这是可以的。

  1. 我们之所以说js是静态作用域,是因为我们发现js的作用域好像不是在运行时定义。仿佛是在我们写好代码就定义好了。如以下代码:
var a = 2;
function back() {
	console.log(a);
}

function next() {
	var a = 3;
    back(); // 2 but not 3
}

next()

确实是:有点闭包那味,但是却和闭包不同。

  1. 我们之所以说CMD是动态模块规范,是因为require导入的模块,竟然能运行时改变。如以下代码:
let moduleAorB = null;

if () {
	moduleAorB = require('a')
} else {
	moduleAorB = require('b')
}

相反,举一反三,我们知道import必须在一开始导入,且不给更改,所以import是静态模块。

这时候。我们渐渐懂了静态和动态的含义了,他讲的是在一开始就定义,或者在运行时才确定的一个边界。那这个和我们这次要聊的作用域链有关系吗?是有关系的,因为js是静态作用域。

词法作用域

词法作用域的意思是:指在词法阶段确定的作用域,叫词法作用域。通过前面文章描述。我们知道词法阶段是编译原理一开始解析代码的阶段,所以词法解析意味着代码写完就确定了作用域,所以我们说词法作用域就是静态作用域。

所以我们前面的例子:

var a = 2;
function back() {
    console.log(a);
}

function next() {
    var a = 3;
    back(); // 2 but not 3
}

next()

结果之所以是2,而不是3 的原因。是因为back函数,在声明时就确定了其所在上级作用域是全局作用域。所以在执行时候,即使执行作用域是在next里面,依然先访问到自己作用域的上级作用域。这是因为他在词法阶段就确定了其上级作用域是全局作用域。

这是词法作用域的基本特性。

作用域链

再一次提到作用域链,那这一次。我们直接认为,作用域链是执行上下文里面的一个变量[[scope]]。而我们前面提到,执行上下文是在词法阶段确定的结构。所以作用域链也是在词法阶段确定的关系。

在v8源码中,scope链变量在词法解析被描述为如链表一般的结构,可以通过out_scope拿到该环境下的上一层作用域链。

在简单情况下。我们可以将其抽象的看成是执行上下文中的一个变量[[scope]],scope可以通过outer拿到上一层的作用域链。

那么假如一个声明在全局环境下的函数,其上下文环境应该如图所示:

作用域链最大的意义在于,定义环境所能访问的上级环境。进而会有后续闭包的说法。

var a = 2;
function back() {
    console.log(a);
}

function next() {
    var a = 3;
    back(); // 2 but not 3
}

next()

对于上面这段代码执行时的上下文顺序跟作用域顺序是不一样的,在next运行后,back也进入执行上下文,他们的上下文关系是这样的:

但是他们的作用域链关系是这样的:

也就是说,两个函数的直接上级作用域都是全局上下文,原因是因为他们都是声明在全局环境中。假如你希望next函数访问到back函数的上下文,你应该在back函数中声明next函数。那这个时候next称做back的闭包。关于闭包的细节,不仅仅在于我们这里简单去在函数中声明另一个函数。更在于在垃圾回收中,如何让闭包不被回收,是更值得探讨的问题。

另外,在浏览器控制台,可以通过打断点,看到当前所处点作用域链集合:

关于作用链,我的认识就是这样。the end