大家好,我是晚天

从一段代码讲起

先从一段代码讲起。
以下代码分别通过 const 和 var 定义了 name1 和 name2,然后在 new Function() 中访问 name1 和 name2。

const name1 = 'jack';
var name2 = 'tom';

new Function(`console.log('name', name1, name2)`);

该段代码分别在 VSCode 终端和浏览器控制台中运行,结果出现不同。

VSCode 终端:
image.png
在 Vscode 终端中运行,直接出现报错 ReferenceError: name1 is not defined

浏览器控制台:
image.png
在浏览器控制台中运行,name1 和 name2 都可以正常被访问到。

为什么同一段代码在不同地方运行,会得到不同的结果呢。

追溯 new Function()

此时,我们需要追溯到 Function 构造函数的定义中:

the Function constructor creates functions that execute in the global scope only.

也就是说,通过 Function 构造函数创建的函数只会在全局作用域中执行。而上述代码出现不同的执行结果正是因为不同执行环境的全局作用域(global scope)不同导致的。

下面,我们来看下 Node.js 和浏览器环境的全局作用域有何不同。

Node.js 和浏览器环境的全局作用域

我们先来看 Node.js 文档中的一段描述:

In browsers, the top-level scope is the global scope. This means that within the browser var something will define a new global variable. In Node.js this is different. The top-level scope is not the global scope; var something inside a Node.js module will be local to that module.

也就是说,Node.js 和浏览器中的全局作用域有着明显的区别。

  • Node.js:最上层作用域不是全局作用域,在 Node.js 模块中使用 var 定义变量将在模块中定义一个本地变量;
  • 浏览器:最上层作用域就是全局作用域,在浏览器中使用 var 定义变量将定义一个新的全局变量。

Node.js

console.log(this) 在不同场景下的表现:

  • 在空 JS 文件(模块)中:将打印出一个指向该空模块的空对象 {};

image.png

  • 在函数中:将打印出 Node.js 全局作用域对象;

image.png

  • 在严格模式下的函数中:将打印 undefined,因为严格模式下,函数没有本地作用域对象。

image.png

从上述例子,我们可以看出,在空文件中 this 是一个空对象,在非严格模式的函数中 this 是 global 对象。

接下来,我们再看下这个例子:
image.png

从上面的例子,我们可以看出:

  • 模块文件中定义的变量是一个本地变量,无法通过模块文件的 this 访问到;
  • 模块文件中的 this 指向 module.exports 对象;
  • new Function 直接在全局作用域(即 global 对象)中执行;
  • 函数声明中的 this 指向全局作用域,即 global 对象;
  • 函数声明中的会沿着作用域链逐级寻找变量;
  • 作用域链:局部作用域 -> 模块作用域(module.exports) -> 全局作用域

浏览器

相比 Node.js ,浏览器的全局作用域就好理解的多,浏览器的全局作用域即为 window 对象。

看下具体代码执行结果:
image.png

从上述代码,我们可以看出在浏览器控制台中:

  • 虽然都是全局变量,即都存在在全局作用域中,通过 var 声明的变量会在 window 对象上创建属性,通过 const 声明的变量则不会;也就是说浏览器中全局作用域和 window 对象不能完全画等号,这一点有很强的迷惑性。

    var变量不同,const 声明的全局常量不会变为 window 对象的属性。
  • this 指向 window 对象,window 对象可通过 this、globalThis、window 访问到;
  • 在 new Function 中只能访问全局作用域,由于 name1 和 name2 均存在在全局作用域中,所以可以被直接访问到;
  • 函数声明中的 this 指向 window 对象;
  • 函数声明中的会沿着作用域链逐级寻找变量;
  • 作用域链:局部作用域 -> 全局作用域

    总结

    简要概括,Node.js 和 浏览器中全局作用域的区别为:

  • Node.js 中的全局作用域指向 global 对象,模块文件中的 this 指向 module.exports,模块文件中创建的变量会声明在局部作用域中;
  • 浏览器中的全局作用域指向 window 对象,全局声明的变量会声明在全局作用域中,var 声明的变量会增加相应的 window 属性,const/let 声明的变量则不会增加相应的 window 属性。

这也是开头的那段代码执行结果不一致的核心原因。

参考资料


晚天
8 声望0 粉丝