图解javascript作用域

本文原文地址

此文章是回答知乎问题总结而来

作用域访问规则

let count = 0;

function func() {
  console.log(count);
}

func();

下面我们来看看变量count是如何被打印出来:

1. `func`函数调用,`console.log`打印`count`变量,查找当前函数作用域,是否存在变量`count`;
2. 不存在继续向上查找,查找模块作用域,发现`count`变量存在,并且打印出变量的值。

如果模块作用域依然不存在count变量?

会继续向上查找,查找全局作用域是否存在count变量,如果依然不存在,提示undefined

如下图所示:

通过上面的示例代码,我们可以知道一个规则,作用域访问顺序:函数作用域 ——> 模块作用域 ——> 全局作用域

提示:作用域访问的方向是不可逆只有由里向外访问,先函数,再模块,最后全局;

作用域创建规则

JavaScript中的作用域是词法作用域(相对于动态作用域)。

什么叫词法作用域?顾名思义,词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪来决定的。

无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定,这就是JavaScript的词法作用域。(除非你使用 with 或者 eval 欺骗它)
我们来看下面的例子:

let count = 0;

function func(name) {
  console.log(count, name);
}

func('func');

代码执行,作用域创建顺序:

1. 优先创建全局作用域,如全局window对象,或者挂载在全局的函数或属性;
2. 之后模块作用域被创建,`count`变量会挂载在模块作用域,`func`函数也是挂载在模块作用域;
3. 最后才是函数作用域,此时全局作用域和模块作用域已创建生成,如果它需要使用外部的存在的变量或方法,只需向上查找即可;

如下图所示:

上图中作用域创建是至上而下,我省略了表示递进关系的线条。我们来归纳一下,作用域创建顺序是:全局作用域 ——> 模块作用域 ——> 函数作用域

在调用完成之后,作用域也会相应释放。如果是存在闭包,作用域链就会被保存,可以看看:闭包装逼失败(闭包使用) - 掘金

总结

  • 作用域访问顺序:函数作用域 ——> 模块作用域 ——> 全局作用域
  • 作用域访问的方向是不可逆只有由里向外访问
  • 作用域创建顺序是:全局作用域 ——> 模块作用域 ——> 函数作用域

以上内容希望可以帮助到你,你的点赞、收藏是我更新的动力!!

参考

MDN文档

《你不知道的Javascript上卷》

冴羽 · JavaScript深入之执行上下文


HerryLo
238 声望10 粉丝

喜爱JavaScript和NodeJs;