本文解析 JavaScript 中的作用域机制。ECMAScript 默认所有属性和方法均为公有,缺乏真正的私有作用域。开发者常通过下划线前缀约定私有成员,但这仅具提示作用。此外,文章探讨了静态作用域的概念,说明如何在构造函数上添加静态方法,并澄清此类方法与实例方法的本质区别。
JavaScript 作用域类型概述
公用、受保护和私有作用域
在传统面向对象编程中,重点通常在于区分公用和私有作用域。公用作用域内的对象属性允许从外部访问,这意味着一旦开发者创建了对象实例,即可直接调用其公用属性。相反,私有作用域中的属性仅限于在对象内部访问,对于外部代码而言,这些属性仿佛不存在。因此,若类定义了私有属性和方法,其子类也无法访问这些成员。
近年来,受保护作用域逐渐流行。尽管不同编程语言中受保护作用域的具体规则各异,但其核心用途通常是定义私有的属性和方法,同时允许这些成员被子类访问。
在 ECMAScript 中讨论这些作用域的意义有限,因为 ECMAScript 仅存在一种作用域,即公用作用域。ECMAScript 中所有对象的所有属性和方法默认都是公有的。因此,在定义自定义类和对象时需格外谨慎,务必牢记所有属性和方法默认情况下均为公用。
针对 ECMAScript 缺乏私有作用域的问题,许多开发者在网上提出了有效的属性作用域模式。由于缺少真正的私有作用域,社区制定了一种规约,用于标识哪些属性和方法应被视为私有。该规约规定在属性名前后添加下划线,例如:
![]()
在上述代码中,属性 color 被视为私有。需要注意的是,这些下划线并未改变属性本身是公有的事实,它仅作为一种约定,提示其他开发者将该属性视为私有。
部分开发者倾向于使用单下划线来标识私有成员,例如 obj._color。
静态作用域与 ECMAScript 的实现
静态作用域并非静态的
静态作用域定义的属性和方法可以在任何时候从同一位置访问。以 Java 为例,类可以拥有静态属性和方法,无需实例化该类即可直接访问,例如 java.net.URLEncoder 类的 encode() 函数即为静态方法。
严格来说,ECMAScript 并不具备静态作用域的概念。不过,它可以为构造函数添加属性和方法。正如前文所述,构造函数本质上只是函数,而函数也是对象,因此对象可以拥有属性和方法。例如:
![]()
![]()

在此示例中,方法 alternate() 实际上是函数 sayHi 的方法。你可以像调用常规函数一样调用 sayHi() 以输出 "hi",也可以调用 sayHi.alternate() 以输出 "hola"。尽管使用方式相似,但 alternate() 仍属于 sayHi() 公用作用域中的方法,而非严格意义上的静态方法。