JavaScript 的新特性:类的 #private 字段

这是什么,如何使用,为什么需要?

一边听“Noise Pollution” —— Portugal. The Man,一边阅读本文简直就是享受

JavaScript 标准的第二阶段(Stage 2)加入了类私有字段。它还没有最终确定,但 JavaScript 标准委员会认为这个特性会被开发出来并最终纳入标准(虽然它可能还会改变)

它的语法(当前)看起来像这样:

 1 class Point {
 2   **#x;**
 3   **#y;**
 4
 5   constructor(x, y) {
 6     **this.#x** = x;
 7     **this.#y** = y;
 8   }
 9
10   equals(point) {
11     return **this.#x** === **point.#x** && **this.#y** === **point.#y**;
12   }
13 }

  

这个语法有两个主要部分:

  • 定义私有字段
  • 引用私有字段

定义私有字段

定义私有字段与定义公共字段基本相同:

1 class Foo {
2   **publicFieldName =** 1;
3   **#privateFieldName =** 2;
4 }

为了访问私有字段,你需要定义它。如果你不想在定义的时候给它赋值,可以这样:

1 class Foo {
2   **#privateFieldName;**
3 }

引用私有字段

引用私有字段与访问其它属性类似,只是它有一点特殊的语法。

1 class Foo {
2   publicFieldName = 1;
3   #privateFieldName = 2;
4
5   add() {
6     return this.publicFieldName + **this.#privateFieldName**;
7   }
8 }

this.# 也可以简写:

1 method() {
2   **#privateFieldName;**
3 }

它与下面这段代码等价:

1 method() {
2   **this.#privateFieldName;**
3 }

引用实例(对象)的私有字段

引用私有字段并不局限于 this。也可以访问类实例(对象)的私有字段:

1 class Foo {
2   #privateValue = 42;
3
4   static getPrivateValue(**foo**) {
5     return **foo.#privateValue**;
6   }
7 }
8
9 Foo.getPrivateValue(**new Foo()**); // >> 42

这里,foo 是 Foo 的实例,所以我们允许在类定义中去寻找 #privateValue。

私有方法(很快有会有?)

私有字段这个提案只关注了为类添加私有字段这个问题。提案并没有提到会对类的方法做什么改变,所以私有方法可能会在后续建议中提到,像这样:

1 class Foo {
2   constructor() {
3     **this.#method();**
4   }
5
6   **#method() {**
7     // ...
8   **}**
9 }

这之前,可以将函数赋值给私有字段:

1 class Foo {
2   constructor() {
3     **this.#method();**
4   }
5
6   **#method = () => {**
7     // ...
8   **};**
9 }

封装

使用某个类的实例的时候,不能引用这个类的私有字段。只能在定义这些私有字段的类中引用它们。

 1 class Foo {
 2   **#bar;**
 3
 4   method() {
 5     **this.#bar;** // Works
 6   }
 7 }
 8
 9 let foo = new Foo();
10
11 **foo.#bar;** // Invalid!

进一步说,作为真正的私有属性,你应该不能检测到私有字段的存在。

为了确保你不能探测到私有字段,我们需要允许拥有同样名称的公共字段。

1 class Foo {
2   bar = 1; // public bar
3   #bar = 2; // private bar
4 }

如果私有字段不允许公共字段有同样的名称,你就可以通过尝试写同名属性来探测到私有字段的存在:

1 foo.bar = 1; // Error: bar is private! (boom... detected)

或者不报错的版本:

1 foo.bar = 1;
2 console.log(foo.bar); // undefined (boom... detected again)

这种封装同样对子类有效。子类应该可以拥有相同名称的私有字段,而不必担心父类是否已经存某个私有字段是这个名称。

1 class Foo {
2   **#fieldName** = 1;
3 }
4
5 class Bar extends Foo {
6   **fieldName** = 2; // Works!
7 }

注意:关于封装或“强私有”背后的动机,阅读 FAQ 中这个部分


那么, 为什么用 # 号[译者注:URL 的 Hash标记]?

很多人想知道“为什么不像其它语言那样使用 private 关键字”?

如果使用这样的语法,这里有个示例:

1 class Foo {
2   **private value;**
3
4   equals(foo) {
5     return **this.value** === **foo.value**;
6   }
7 }

让我们单独看看语法的两个部分。

为什么不使用 private 关键字来声明?

private 关键字在很多不同的语言中用于声明私有字段。

来看看使用这种语法的语言:

1 class EnterpriseFoo {
2   public bar;
3   private baz;
4
5   method() {
6     this.bar;
7     this.baz;
8   }
9 }

在这些语言中,以同样的方式访问私有字段和公共字段。所以它们才会这样定义。

但是在 JavaScript 中,我们不能使用 this.field 来引用私有属性(稍后深入),我们需要一种基于语法的方法来连接它们的关系。这两个地方使用 # 更能清楚的表明引用的是什么。

什么引用需要 # (Hash 标记)?

因为如下原因,我们需要使用 this.#field 而不是 this.field:

  1. 因为 #封装 (参阅上面“封装”一节),我们要能同时访问公共和私有的同名字段。因此访问一个私有字段不是按常规的方式查找。
  2. JavaScript 中的公共字段可以通过 this.field 或 this[‘field‘] 来访问。私有字段不能支持第二种语法(因为它需要是静态的),这样会导致混淆。
  3. 你需要费劲去检查:

来看一段示例代码。

 1 class Point {
 2   **#x;**
 3   **#y;**
 4
 5   constructor(x, y) {
 6     **this.#x** = x;
 7     **this.#y** = y;
 8   }
 9
10   equals(other) {
11     return **this.#x** === **other.#x** && **this.#y** === **other.#y**;
12   }
13 }

注意我们是如何引用 other.#x 和 other.#y 的。为了访问私有字段,我们假设 other 是 Point 类的实例。

因为我们使用了 #Hash标签 语法,它告诉 JavaScript 编译器我们正在从当前类中寻找私有属性。

那么,如果不用 #Hash标签,会发生什么呢?

1 equals(otherPoint) {
2   return this.x === otherPoint.x && this.y === otherPoint.y;
3 }

现在我们有个问题:我们怎么知道 otherPoint 是什么?

JavaScript 没有静态类型系统,所以 otherPoint 什么都可能是。

有两个原因导致这个问题:

  1. 我们的函数行为依赖于传入的值类型,不同的值类型导致不同的行为:有时候是在访问私有属性,有时候又是在查找公共属性。
  2. 我们每次检查 otherPoint 的类型:

    if (otherPoint instanceof Point && isNotSubClass(otherPoint, Point)) { return getPrivate(otherPoint, ‘foo‘); } else { return otherPoint.foo; }

更糟糕的是,如果我们引用一个私有属性的话,就不得不在类中对每次属性访问进行这样的检查。

访问属性已经非常慢了,我们绝不想让它变得更慢。



TL;DR: 我们需要对私有属性使用 #Hash标记,因为不正常的使用标准属性访问机制会导致意外行为,并造成巨大的性能问题。



将私有属性添加到语言中是件非常好的事情。感谢努力工作在 TC39 勤劳的人们,他们正在让这美好的事情发生!

版权声明

本译文仅用于学习、研究和交流目的,欢迎非商业转载。转载请注明出处、译者和众成翻译的完整链接。要获取包含以上信息的本文Markdown源文本,请点击这里

关注微信更多学习资料分享!

时间: 2024-10-26 13:06:15

JavaScript 的新特性:类的 #private 字段的相关文章

javascript ES6 新特性之 扩展运算符 三个点 ...

对于 ES6 新特性中的 ... 可以简单的理解为下面一句话就可以了: 对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中. 作用类似于 Object.assign() 方法,我们先来看一下 Object.assign() 方法: Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象.它将返回目标对象.如下: const target = { a: 1, b: 2 }; const source = { b: 4, c: 5

JavaScript ECAMScript5 新特性——get/set访问器

之前对get/set的理解一直有误,觉得get set 是对象属性方法.看了别人的博客也有很多疑问,今天系统的做了很多测试终于弄明白了.(自己通过看书和写demo测试的,如有不对欢迎大家批评指正) get/set访问器不是对象的属性,而是属性的特性.大家一定要分清楚.特性只有内部才用,因此在javaScript中不能直接访问他们.为了表示特性是内部值用两队中括号括起来表示如[[Value]].  1.先简单介绍一下属性的这些特性(这里是简单的背书)          (1)数据属性——包含一个数

oracle 12c 新特性之不可见字段

在Oracle 11g R1中,Oracle以不可见索引和虚拟字段的形式引入了一些不错的增强特性.继承前者并发扬光大,Oracle 12c 中引入了不可见字段思想.在之前的版本中,为了隐藏重要的数据字段以避免在通用查询中显示,我们往往会创建一个视图来隐藏所需信息或应用某些安全条件. 在12c中,你可以在表中创建不可见字段.当一个字段定义为不可见时,这一字段就默认不会出现在通用查询中,除非在SQL语句或条件中有显式的提及这一字段,或是在表定义中有DESCRIBED.要添加或是修改一个不可见字段是非

javascript ES6 新特性之 let

let的作用是声明变量,和var差不多. let是ES6提出的,在了解let之前,最好先熟悉var的原理. JavaScript有一个机制叫“预解析”,也可以叫“提升(Hoisting)机制”.很多刚接触JavaScript的人都会被这个机制弄混.比如: // var 的情况 console.log(a); // 输出undefined var a = 2; 在预编译阶段,JavaScript引擎会将上面的a函数修改成下面的写法: var a; //声明且初始化为undefined consol

JavaScript数组新特性

1 <!DOCTYPE html> 2 2 <html lang="en"> 3 3 <head> 4 4 <meta charset="UTF-8"> 5 5 <title>test1</title> 6 6 <script> 7 7 8 94 9 95 /* 10 96 11 97 every,some 前者是遍历数组中的每一个元素,并执行函数运行,如果有一个返回false则返回

javascript ES6 新特性之 解构

解构的作用是可以快速取得数组或对象当中的元素或属性,而无需使用arr[x]或者obj[key]等传统方式进行赋值 var arr = [1, 2, 3]; //传统方式 var a = arr[0], b = arr[1], c = arr[2]; console.log(a, b, c); // 1 2 3 //解构方式 var [a, b, c] = arr; console.log(a, b, c); // 1 2 3 从上面的例子我们可以看出,要想获取一个数组中的值,我们可以利用传统的索

JDK1.5的新特性:javabean、注解类、类加载器

关于Java基础的文章,我觉得写得还可以,以前发在了我其它的博客了,肯定是原创,现在再分享给大家出来. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

黑马程序员----java基础--JDK新特性和集合其他类

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.JDK1.5新特性 JDK升级的三大原因: (1).提高代码的复用性 (2).提高代码的安全性 (3).简化书写 1.泛型机制 JDK1.5版本以后出现新特性.用于解决安全问题,是一个类型安全机制. (1).泛型概念 泛型是根据数组的思想设计出来的,因为数组一旦建立成功就已经明确了数据类型,所以可根据数组思想给集合指定类型. 如:数组:int[] arr=new int[4]; 而集合的泛

ArcGIS API for JavaScript 4.2学习笔记[0] AJS4.2概述、新特性、未来产品线计划与AJS笔记目录

放着好好的成熟的AJS 3.19不学,为什么要去碰乳臭未干的AJS 4.2? 诸君,我喜欢嫩的--呸呸呸 诸君,我喜欢3D咋了?新事物会替代旧事物不是~ ArcGIS API for JavaScript 4.2概述 AJS 4.2,即ArcGIS API for JavaScript 4.2,是美国ESRI公司针对WebGIS市场推出的.利用JavaScript和Dojo开发的一款产品,它在2016年12月发布.而AJS 4.0 beta则在一年前就发布了. 关于AJS3和AJS4选择的问题,