JavaScript 全局变量那些事

2022-03-09JavaScript

JavaScript 的设计导致其全局变量的定义方式与其他语言有显著的不同。本文简单介绍一些关于 JavaScript 全局变量的一些小细节。

我们都知道,JavaScript 定义全局变量有以下几种方式:

var a = 1;            // 1
globalThis.a = 1;    // 2
a = 1;                // 3

let a = 1;            // 4
const a = 1;        // 5

1、2 和 3 都是将全局变量 a 挂载在 globalThis 对象上,而 4 和 5 不会。letconst 没什么可说的,和其他语言都很类似。这里主要对 1、2 和 3 做一些探究。主要问题是:通过这三种方式定义的 globalThis.a,有区别吗?

有区别。通过 3 定义的 globalThis.a 和另外两种方式定义的是不一样的。1 和 2 定义的属性的 [[configurable]] 描述符是 false,而 3 定义的为 true

var a = 1;

// {value: 1, writable: true, enumerable: true, configurable: false}
console.log(Object.getOwnPropertyDescriptor(globalThis, 'a'));
globalThis.a = 1;

// {value: 1, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(globalThis, 'a'));
a = 1;

// {value: 1, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(globalThis, 'a'));

[[configurable]] 属性是 false 意味着,我们不能通过 delete 删除这个属性,也不能对其他描述符进行配置:

'use strict'

var a = 1;
delete globalThis.a;    // Uncaught TypeError: Cannot delete property 'a' of #<Window>

// Uncaught TypeError: Cannot redefine property: a
Object.defineProperty(globalThis, 'a', {
    enumerable: false
});

以上的示例都是在浏览器中运行得到。如果你用 Node.js 运行以上的示例,会发现奇怪的现象:

var a = 1;

console.log(globalThis.a);    // 在 Node.js 中是 undefined,在浏览器中是 1

乍一看非常奇怪。我们明明在全局作用域声明了一个 var 变量,怎么在 globalThis 上找不到呢?

答案很简单:我们在 Node.js 里运行的代码从来都不会运行在全局作用域里。在执行之前,Node.js 对我们的代码进行了封装(不然,modulerequire 是怎么来的呢?)。以上的代码实际运行时被改为:

(function (exports, require, module, __filename, __dirname) {
    var a = 1;
    console.log(globalThis.a);
})();

a 实际被定义在了这个函数作用域里,自然不能在 globalThis 上访问到。编写代码时需要注意浏览器和 Node.js 环境的不同。当然,最好永远也不要用全局变量。