为什么 JavaScript 的私有属性使用 # 符号
发布于 5 年前 作者 falost 4146 次浏览 来自 分享

这几天 JavaScript 的私有属性又成为了前端社区热议的话题。原因很简单,这家伙长这样:

image.png 惊不惊喜!意不意外!

而且 TC39 委员会以及对此达成了一致意见,并且该提案已经进入了 stage 3。在 es 规范阶段 stage 3 是候选提案,又很大的可能会进入到下一个标准。到目前为止,已经可以确定会进入 es2019(es10) 标准的有 optional-catch-binding 和 proposal-json-superset。而这个 private 大概也只能进入到 es2020 后的标准了。

TC39 委员会解释道,他们也是做了深思熟虑最终选择了 # 符号,而没有使用 private 关键字。其中还讨论了把 private 和 # 符号一起使用的方案。并且还打算预留了一个 @ 关键字作为 protected 属性😄,不过这个 @ 已经被 decorator 使用了。

我们在此就不讨论这个语法到底丑不丑了,这不显而易见的吗,还用讨论吗。我们讨论一下为什么要使用 # 符号。

有人说,“如果这个进入了规范,我就再也不用 JavaScript了,以后项目都用 TypeScript 写”。

这就有点由不得你了,我们看 TypeScript 的官网介绍:

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.

TypeScript 是 JavaScript 的超集,言外之意就是 js 有的 ts 也全都有。对于一个已经进入 stage 3 的提案,ts 当然不会坐视不理的。ts 也正在抓紧支持这个语法:All Bloomberg Changes for Private Fields and Methods。

也有人疑问:“为什么 ts 的私有属性就可以使用 private 关键字,而 js 就不行呢?”

因为 ts 的 private 和这个 # 语法完全是不同的东西。TypeScript 是一种静态类型语言而不是强类型语言(有争议)。private 只是在编译时做检查,最终生成的 js 代码中被 private 修饰的属性依然是公开的。

那为什么不使用下划线( _)呢,毕竟这个已经是私有属性的非正式的最佳实践了?

之所以不选择下划线是出于兼容性考虑,另一个由于兼容性而妥协的提案是 JavaScript 社区由一个库引发的“smoosh门”事件到底怎么回事? 由于下划线是合法的 js 变量标识符所以很多的代码都使用了下划线,如果新的 js 规范把下划线开头的变量作为私有属性,那么会导致很多就代码无法运行。比如知名的 lodash 和 underscore 库就是使用的下划线,这两个库每周在 npm 的下载量是 2 千万。

“为什么不使用 private?”

这里有一个经典的例子:

class Foo {
  private;
  value;
  equals(foo) {
    return;
    this.value === foo.value;
  }
}

在很多面向对象语言中都有这种写法,判断类的两个实例是否相等。如果在 js 中也这么写,会有一些问题。

在静态类型语言中,我们可以明确的知道传入的参数是 Foo 类型,因此在 Fooequals 方法中我们可以访问foo 的私有属性value。但是 js 是动态类型的,我们无法知道参数 foo 的类型,如果我们传入了 {value:'123'} 会,则函数的行为不符合我们的预期。这也导致了该函数有时访问的是私有属性,有时访问的是公有属性。

另一方面,私有属性只在类的内部可以访问,外部无法访问,甚至不知道此变量的存在。因此在 JavaScript 中同名的私有属性和公有属性可以同时存在,两者使用 # 做区分。

如果使用 private 进行修饰,则我们可以探测类的私有属性:

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

或者:

foo.bar = 1;
foo.bar; // 'undefined' (boom... detected again)

PS:虽然社区的整体意见是使用 private 代替 #,但是 TC39 的意见很坚决。

最后,这种引入私有属性的方式,其实隐式的为 js 引入了静态类型。

来自公众号:justjavac 地址:https://mp.weixin.qq.com/s/yo6jKu5FC6ekY28Rmuieug

回到顶部