作用域和上下文的概念和区别说实话我一开始也不是很清楚,直到后面参加公司产品的开发,开始写原生 JS 的时候发现很多一旦逻辑写的稍微复杂一些就会涉及到上下文和作用域,如果不理解的话很容易产生一些不必要的问题,于是我找了很多资料,系统的对比了一下这两个知识点,并做了这篇笔记。
上下文(context)和作用域(scope)的关系可大致总结为:
上下文是一段程序运行所需要的最小数据集合;作用域是当前上下文中,按照具体规则能够访问到的标识符(变量)的范围。
一、上下文
上下文(context)是一段程序运行所需要的最小数据集合。我们可以从上下文交换(context switch)来理解上下文,在多进程或多线程环境中,任务切换时首先要中断当前的任务,将计算资源交给下一个任务。因为稍后还要恢复之前的任务,所以中断的时候要保存现场,即当前任务的上下文,也可以叫做环境。即上下文就是恢复现场所需的最小数据集合。容易把人弄晕的一点是,我们这里说的上下文、环境有时候也称作作用域(scope),即这两个概念有时候是混用的。不过,它们有不同的侧重点,我在下面对比的时候会着重说一下。
另外,JavaScript 中常见的情形是一个方法/函数的执行。从一段程序的角度看,这段程序运行所需的所有变量,就是它的上下文。在一定情况下可以理解为在某个作用域下的 This 值。
当你使用 new 关键字调用函数时,上下文的值会设置为被调用的函数实例:
1 |
|
二、作用域
作用域(scope)是标识符(变量)在程序中的可见性范围。作用域规则是按照具体规则维护标识符的可见性,以确定当前执行的代码对这些标识符的访问权限。作用域(scope)是在具体的作用域规则之下确定的。
作用域目前可分为以下三类:
- 全局作用域:全局作用域中的对象可以在代码的任何地方访问,一般来说,下面情况的对象会在全局作用域中:1>、最外层函数和在最外层函数外面定义的变量;2>、没有通过关键字”var”声明的变量;3>、浏览器中,window对象的属性。
- 局部作用域:局部作用域又被称为函数作用域(Function scope),所有的变量和函数只能在作用域内部使用。
- 块语句:只有 ES6 中支持在块语句中创建局部作用域。注意:在创建函数或者变量的时候,这个函数或变量的作用域与作用域链(函数的作用域链将会在运行时用到)就已经决定了,而是不是在调用的时候决定。
前面说过,有时候上下文、环境、作用域是同义词;不过,上下文(context)指代的是整体环境,作用域关注的是标识符(变量)的可访问性(可见性)。上下文确定了,根据具体编程语言的作用域规则,作用域也就确定了。这就是上下文与作用域的关系。
写 JavaScript 代码时,如果 Function 作为参数,可以指定它在具体对象上调用时,这个对象常常叫做 context。
代码段一:
1 |
|
为什么将这个参数叫做 context?因为它关系到调用环境,指定了它,就指定了函数的调用上下文。再加上具体的作用域规则,作用域也确定了。
- thisArg:在fun函数运行时指定的this值。需要注意的是,指定的this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
- arg1, arg2, …:指定的参数列表。
找了一个例子说明一下静态作用域和动态作用域的区别。
代码段二:
1 |
|
有一定 JavaScript 编程经验的人都能看出,这段程序会输出 2,但如果在动态作用域的规则下,应该输出 3,即 a 的引用不再是编译时确定,而是调用时确定的。
三、理解闭包
在上面的代码段二中,之所以输出 2,是因为 foo 是一个闭包函数。如果从本文中理解了上下文和作用域的概念,对于闭包是什么这一问题是不是感到豁然开朗?
前面说过,词法作用域也叫静态作用域,变量在词法阶段确定,也就是定义时确定。虽然在 bar 内调用,但由于 foo 是闭包函数,即使它在自己定义的词法作用域以外的地方执行,它也一直保持着自己的作用域。所谓闭包函数,即这个函数封闭了它自己的定义时的环境,形成了一个闭包,所以 foo 并不会从 bar 中寻找变量,这就是静态作用域的特点。
下面举个更加典型的例子。
代码段三:
1 |
|
sub 就是 func 这一返回值,func 定义在 fn 内部并且被传递出来了,所以 fn 执行之后垃圾回收器依然没有回收它的内部作用域,因为 func/sub 在使用。sub 依然持有 func 定义时的作用域的引用,而这个引用就叫作闭包。调用 sub 时,它可以访问 func 定义时的词法作用域,因此找到的 a 是 fn 内部的变量 a,它的值是 0。
参考文章:
- 噬己翼以驽吾心:Js 作用域与作用域链与执行上下文不得不说的故事
- 赵飞:JS小知识–作用域和上下文
- 本文作者: Alvin
- 本文链接: https://alvinyw.github.io/2018/05/1/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!