JavaScript 的黑暗时刻来了?
近日,横竖都是二的「#」字符在前端圈子点了一把熊熊烈火,燃烧起的浓烟让很多使用 JavaScript 的开发者懊恼其黑暗时刻已来临。
这其中真实的原因是 ECMA TC39 委员会(Technical Committee 39,简称TC39)在 GitHub 上通过了一条 ECMAScript 语法特性的草案,即被称之为弱类型语言的 JavaScript 中,类的私有属性修饰符将以「#」字符来表示。
示例如下:
class Counter extends HTMLElement { #x = 0; clicked() { this.#x++; window.requestAnimationFrame(this.render.bind(this)); } constructor() { super(); this.onclick = this.clicked.bind(this); } connectedCallback() { this.render(); } render() { this.textContent = this.#x.toString(); } } window.customElements.define('num-counter', Counter);
事实上,我们都知道在 JavaScript 中并没有像 Java 和 C++ 中的 private 关键字来访问私有成员变量,通常开发者更多的是采取以“下划线_”为开头将变量约定为私有成员:
function Person(name){ this._name = name; } var person = new Person(‘Joe’);
或者利用 JavaScript 闭包的特性,来模拟私有变量:
function Person(name){ var _name = name; this.getName = function(){ return _name; } } var person = new Person(‘Joe’);
由此,我们不禁产生疑问,为何不用和其他语言相同的“private”来表示JavaScript 类私有字段,而是“#”字符?况且“#”在 Python、MySQL、R、Perl 等编程语言中都是代表的注释,这样是否会造成混乱?
JavaScript 中为何不用“private”替代“#”?
对此,开发者 Jamie 在《JavaScript's new #private class fields》(https://jamie.build/javascripts-new-private-class-fields.html)一文中为我们做了解释,也正如上文所述,在 Java、C++ 等编程语言中,都会使用 private 来定义私有字段,用法如下:
class EnterpriseFoo { public bar; private baz; method() { this.bar; this.baz; } }
在这些语言中,公有和私有字段的访问方式相同。因此,它们采用这种定义方式也是合理的。
但是在 JavaScript 中,因为我们不能使用 this.field 的方式去访问私有属性,那么我们就需要一种在语法上传达关联的方法。通过在这两个地方都使用符号 #,到底在引用哪个属性就很明显了。
为什么引用需要“#”字符?
我们需要使用 this.#field 而不是 this.field 主要有几个原因:
- 为了封装性,我们需要允许公有和私有字段可以同时具有相同的名称。因此,访问一个私有字段不仅仅是普通的查找。
- JavaScript 中的公有字段可以通过 this.field 或 this ['field'] 引用。而私有字段将无法支持第二种语法方式(因为它是静态的),这可能会导致混淆。
- 需要昂贵的检查“代价”。
来看一个代码示例:
class Point { #x; #y; constructor(x, y) { this.#x = x; this.#y = y; } equals(other) { return this.#x === other.#x && this.#y === other.#y; } }
注意我们是如何引用 other.#x 和 other.#y。通过访问私有字段,我们假设 other 是我们的 Point 类的实例。
因为我们已经使用了 # 符号语法,所以我们告诉 JavaScript 编译器我们正在从当前类中查找私有属性。
但是如果我们不使用 # 符号会发生什么?
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'); } else { return otherPoint.foo; }
更糟糕的是,我们必须为类中的每个属性访问执行此操作,以检查我们是否引用了私有属性。
本来属性的访问已经非常慢了,所以我们绝对不想再增加它的权重。
因此,我们需要对私有属性使用 # 字符,因为使用其他方式会造成不可预料的行为和后果,升值可能带来巨大的性能问题。
开发者的态度
但即使如此,草案刚刚通过,GitHub 上的不少开发者还是坐不住了,纷纷站出来表示(https://github.com/tc39/proposal-class-fields/pull/140):
- 这个字符对我而言,看起来真的容易混乱。在社区中的开发者频频说不的同时,TC39 竟对这个草案达成共识?!对此,我真的很失望。我不想说这是自 ES4 发展历史以来最黑暗的一天,因为历史会证明一切。
- TC39 委员会内部可能达成共识(尽管我知晓至少有一位委员会成员不同意这一点),但 TC39 和社区之间肯定没有达成共识,我不明白他们为何会忽略这一点。
- 我更宁愿“private”而不是“#”作为 JavaScript 私有属性的修饰符。作为 @ 装饰器的语法,也许“#”未来会被用于另一种特殊的语法。
甚至有开发者针对提案做了调研,结果显示反对声很强烈:
同时国内的开发者也开启了吐槽模式:
基于此,TC39 委员会成员@littledan 回应表示,我真的认为 TC39 之外的社区观点非常重要,并希望在我们的决策中尽可能地考虑它。我真的想通过 GitHub repos 作为通信媒介,以获得针对我们的规范更多的反馈意见。针对这个提案,我很抱歉没有更详细地关注和审核。尤其令我感到遗憾的是,当讨论有负面态度时,我没有强烈的干预。因为我想让开发者都知道你的意见对我和 TC39 中的许多人都非常重要。
除了在 GitHub 的讨论之外,我也一直在与各种库、框架的作者以及在更传统的面向对象编程语言方面有更多经验的开发者交谈。对于这些人中的许多人来说,缺乏易于使用的封装是 JavaScript 中的一个大漏洞,虽然通常开发者需要一些时间来习惯语法,但这种方法仍被视为一种实用的选择。我觉得那些对这个提案持肯定态度的人不太愿意在 GitHub 问题上发表评论,而这些问题更多的是表达不同意见。
鉴于世界上数以百万计的 JavaScript 开发者,开放流程无法在整个社区中达成绝对的共识,但这并不意味着我们会忽略这些意见。但有时候,我们必须做出艰难的选择,尽可能多地考虑社区开发者的意见,并做出这些选择是 TC39 有权做的事情。
你如何看待这项新特性?欢迎下方留言,分享你的看法。
“征稿啦”
CSDN 公众号秉持着「与千万技术人共成长」理念,不仅以「极客头条」、「畅言」栏目在第一时间以技术人的独特视角描述技术人关心的行业焦点事件,更有「技术头条」专栏,深度解读行业内的热门技术与场景应用,让所有的开发者紧跟技术潮流,保持警醒的技术嗅觉,对行业趋势、技术有更为全面的认知。
如果你有优质的文章,或是行业热点事件、技术趋势的真知灼见,或是深度的应用实践、场景方案等的新见解,欢迎联系 CSDN 投稿,联系方式:微信(guorui_1118,请备注投稿+姓名+公司职位),邮箱([email protected])。
2018 AI开发者大会
◆AI工程师必备大会◆
2018 AI开发者大会是一场由中美人工智能技术高手联袂打造的AI技术与产业的年度盛会!我们只讲技术,拒绝空谈!
这里有10场技术专题论坛:计算机视觉、数据分析、机器学习、知识图谱、智慧金融、智能驾驶、语音技术、智慧医疗、机器学习工具、自然语言处理。
还有15+硅谷实力讲师团、80+AI领军企业技术核心人物、100+技术&大众实力媒体、1500+AI专业开发者
识别下方二维码,快速获取大会更多信息,并获得最低折扣票!
点击“阅读原文”,也可立即报名。