1. 首页

【译】JS新语法:私有属性

类私有字段已经处于JavaScript规范流程的Stage 2,它还没完成但是JS规范协会期望这个功能得到实现并归入语言规范(虽然可能还有变数)。

该语法(目前)如下:

class Point {
  #x;
  #y;
  //JS中文网 – 前端进阶资源分享 www.javascriptc.com

  constructor(x, y) {
    this.#x = x;
    this.#y = y;
  }

  equals(point) {
    return this.#x === point.#x && this.#y === point.#y;
  }
}

这个语法有两个关键部分:

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

定义私有字段

定义私有字段跟定义公有字段区别不大:


class Foo { publicFieldName = 1; #privateFieldName = 2; }

欲引用一个私有字段,必须先定义它,所以如果你不想在定义的时候给初始值,可以这样:

class Foo {
  #privateFieldName;
}

引用私有字段

引用一个私有字段跟引用其他字段类似,只是有一个特殊的语法。

class Foo {
  publicFieldName = 1;
  #privateFieldName = 2;
  add() {
    return this.publicFieldName + this.#privateFieldName;
  }
}

this.#还有个简写方法:

method() {
  #privateFieldName;
}

其实跟这个效果相同:

method() {
  this.#privateFieldName;
}

引用实例的私有字段

引用私有字段并不局限于this,你还可以访问同类其他实例里的私有字段:

class Foo {
  #privateValue = 42;
  static getPrivateValue(foo) {
    return foo.#privateValue;
  }
}

Foo.getPrivateValue(new Foo()); // >> 42

在这里,foo是Foo的实例,所以可以在Foo类的定义里面访问foo.#privateValue

私有方法(即将到来?)

私有字段提案只是关注于添加类字段,该提案没有对类方法有任何改动,所以类私有方法即将出现在一个跟进提案当中,并且很可能长这样:

class Foo {
  constructor() {
    this.#method();
  }
  #method() {
    // ...
  }
}

在这之前,你可以给私有字段复制函数值:

class Foo {
  constructor() {
    this.#method();
  }

  #method = () => {
    // ... JS中文网 – 前端进阶资源分享 www.javascriptc.com
  };
}

封装性

如果你在使用一个类的实例,你不能引用该类的私有字段。你只能在该类的定义里引用它们。

class Foo {
  #bar;
  method() {
    this.#bar; // Works
  }
}
let foo = new Foo();
foo.#bar; // Invalid!

为了真正地体现私有,你不应该有办法检测一个私有字段是否存在。

为了保证你无法检测到一个私有字段,我们需要私有字段和公有自动可以重名。

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

不然如果不允许同名,你就可以用如下方法检测到私有字段是否存在:

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

或者静默的版本:

foo.bar = 1;//JS中文网 – 前端进阶资源分享 www.javascriptc.com
foo.bar; // `undefined` (boom... detected again)

这种封装性对于子类也同样试用。一个子类应当可以定义同名字段而不用担忧会影响父类型。

class Foo {
  #fieldName = 1;
}

class Bar extends Foo {
  fieldName = 2; // Works!
}

那为什么是井号?

许多人会想:“为啥不按照其他语言的约定,用一个private关键字呢?”

这有一个这种语法的样例:

class Foo {
  private value;

  equals(foo) {
      //JS中文网 – 前端进阶资源分享 www.javascriptc.com
    return this.value === foo.value;
  }
}

我们来分别看看这种语法的两个部分。

为什么定义的时候不用private关键字?

在很多语言里都会使用private来定义私有字段。

这种语言的语法如:

class EnterpriseFoo {
  public bar;
  private baz;
  method() {
    this.bar;
    this.baz;
  }
}

这些语言当中,公有和私有字段的访问方式是一样的。所以这么定义可以理解。

然而在JS当中,因为我们不能用this.field访问私有属性(我一会再讲),我们就需要一种方法进行语法层面的关联。通过在两处都使用#,到底引用了什么就很明显了。

为什么引用的时候需要用 #井号 ?

我们必须用this.#field而不是this.field有以下几个原因:

  • 为了封装性(见上面封装性章节),我们需要公有和私有字段可以同时拥有相同的名字。所以访问一个私有字段不能是个普通的查询。
  • JS 里公有属性可以通过this.field或者this[‘field’]访问。然而私有属性不支持第二个语法(因为它必须是静态的),这会可能导致混淆。
  • 你 需要付出不少性能带价来做类型检查:
    来看看一个代码例子:
class Point {
  #x;
  #y;

  constructor(x, y) {
    this.#x = x;
    this.#y = y;
    //JS中文网 – 前端进阶资源分享 www.javascriptc.com
  }
  equals(other) {
    return this.#x === other.#x && this.#y === other.#y;
  }
}

注意我们是如何引用other.#x以及other.#y。使用私有字段,我们就在假定该实例是我们Point类的一个实例。

因为我们使用了 # 语法,所以也就告知了JS编译器我们是在当前类里的私有属性。

如果我们没用 # 会怎样?

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

我们有个问题:我们如何知道otherPoint是什么?

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

这就产生问题了:

  • 我们的函数根据传入参数的不同会有不同的表现:有时会访问一个私有属性,又有时访问共有属性。
  • 我们每次都需要检查otherPoint的类型:
if (
  otherPoint instanceof Point &&
  isNotSubClass(otherPoint, Point)
) {
  return getPrivate(otherPoint, 'foo');
    //JS中文网 – 前端进阶资源分享 www.javascriptc.com
} else {
  return otherPoint.foo;
}

更糟糕的是,我们每一个属性访问都需要检查一下是不是在引用私有属性。

属性访问已经挺慢的,所以我们真的不想再给它增加负担了。

上面太长;不看:我们需要用 # 来使用私有属性是因为如果不用,会产生不可预料的表现并且造成巨大的性能影响。

结语

私有属性是对语言的一个帅气加强。感谢所有为TC39辛勤工作的人们让这成为现实。

JS中文网 – 前端进阶资源分享 www.javascriptc.com

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:【译】JS新语法:私有属性

链接:https://www.javascriptc.com/405.html

« 几分钟内提升技能的8个 JavaScript 方法
Array 原型方法源码实现大解密»
Flutter 中文教程资源

相关推荐

QR code