"Field提案"是什么东东?

本文是系列文章,包括:

  • "Field提案"是什么东东(本文)
  • 关于废止proposal-class-fields提案的建议 - 在这里
  • 私有属性的实现 - 在这里
  • No prefix! operator is Ok! - 在这里
  • (未完待续)

本文是对一份tc39提案的讨论。原有提案:https://github.com/tc39/proposal-class-fields

上次掺和JavaScript的事,是说JavaScript语言有值的问题;再往前一年,就是在“红绿灯大战”中讨论Promise了。这细数起来,都是两三年前的旧事了。

但我还是跟进JavaScript的。最新近的事,是持续地讨论这个所谓“TC39提案”的事情。本以为真关心的人并不多,但前两天连老宋都转了篇贴子过来,“这帮人要把js折腾成啥样子啊”,老宋说。

“啥样子?”

1. 导致太下大乱的“Field提案”

“Field提案”全称是“proposal-class-fields”(在这里),是试图在类声明中添加“Fields”的一项语言设计。提案基本的想法是这样:

// 在类中声明字段(Field)
class Counter {
  x = 0;
  foo() {
    console.log(this.x);  // 0
  } 
}

这看起来并没有什么大不了,毕竟之前在类中只能声明方法,新方案可以直接为this.x赋个初值,大家都很happy。

但“字段(Field)”是什么?没人鸟这个问题。

后来这个方案扩展了,提供了公开字段(Public fields)、私有字段(Private fields)等特性,并整合了种种有关于“类”的提案的思想,还提出了向前兼容类装饰器(decorators)等愿景,摇身一变,成了一个巨无霸方案。同时,也不可避免地在社区掀起了轩然大波:多达120余条issues,其中#100高达300余条讨论,Git issues折了好几回才能加载完。

话说,我就是被这个issues入坑的。

那么,现在这个巨无霸的方案又在讲什么呢?它说起来就是提供了两个语法:

// 在类中声明私有字段(private field)
class Counter {
  #x = 100;
  foo() {
    console.log(this.#x);  // 100
  }
}

然而这样一来,即便连最初的提案者都跳出来反对了,包括BE大神也反对。即便如此,都挡不住这辆战车轰隆隆开进到了Stage 3(阶段3是提案即将正式写入规范前的最后一个阶段)。而且一并拖上的还有另外两个方案,称为“静态成员与方法(Static class fields and private static methods)”,以及“私有方法与存取器(Private instance methods and accessors)”。这两个提案推动的语法是这样:

// 对象字段
class Counter {
  // 声明私有字段(private fields)
  #xValue = 100;

  // 声明和使用存取器(accessors for private name '#x')
  get #x() { return #xValue; }
  set #x(value) {
    this.#xValue = value; 
  }

  // 私有方法
  #foo() {
    this.#x++;
  }
}

// 类(静态)字段
class Counter2 {
  // 声明类私有字段(class private fields)
  static #x = 100;

  // 访问类私有字段
  static foo2() {
    console.log(Counter2.#x);
  }

  // 类私有方法
  static #foo2() {
    Counter2.foo2();
  }
}

等等,这还不够,又还有一份与此相关的提案进入了Stage 2。是这种所谓“未来的装饰器”:

class Counter {
  @observed #x = 0;

  @bound
  #clicked() {
    this.#x++;
  }
}

还有……

如果你想深入了解一点细节,在这里:

2. 但是,问题是?

难道没有人注意到么,到现在为止,谁也没有(或许也是不敢)说清楚:

Fields是什么?

如果一个字段不是属性(property),那么所谓“公开字段”在语义上跟属性又有什么区别?如果它是属性,那么它为什么不在属性表中,而是一个“私有名(private names in scope over the same body of code)”呢?

所以,打蛇就得打七寸。没人说得清这个问题,还没有人敢说,所以你看,我跑去git里面提issues了(在这里)。往深底里,人家能跟你分析规范的每一步,但往这概念上一问就没人说话了。

所以呢,以后跟人吵架,就不要动刀动枪,开口就问:你从哪里来?要往哪里去?人活着是为了什么?等等这些,就很好了。

回到正题。为什么没人讨论Fields是什么呢?不是的,并不是没有。而是只要一讨论,这个问题的答案就明显会是“No Fields”,这个答案有人专门写过,在这里:

这是本“Fields提案”提及到的几个相关/类似提案之一。而它的观点之一就是“没有字段”,对“Fields”提出了概念层面的质问。关于这个问题的进一步分析,我在上面的提及的issues里面也讲了,或者也可以看看中文版(在这里)。

3. 更多的声音

终于有人无法忍受巨大冗长的#100了,于是@mbrowne在@littledan的倡议下开了#150这个新主题。对大家的观点做了提炼,从@hax的主要观点开始:

  1. TC39极大地低估了该标准导致社区分裂(community break)的风险,这对所有人来说都是有害的。
  2. TC39现在的议事流程完全无法阻止灾难。
  3. 一些TC39的成员和支持者基于循环论证和双重标准来驱逐别人的意见和竞争性提案。

随后总结的观点包括(#100里的讨论者太多了,作者一时半会儿大概也数不过来):

  • 明显的语法混淆与歧义 @hax @rdking
  • 向TC39表达不满,对提案长期目标或效果置疑 @rdking @bdistin
  • 否定"Fields"这一概念,建议在"Property"的概念基础上实现相关特性 @hax @rdking @mbrowne @shannon @aimingoo
  • 否定“不可探测(Undetectability)”这一需求,认为它并不能提供足够的保护性 @rdking
  • 新的语法或语言特性与既有体验之间的冲突,例如动态语言特性;或对语法的长期影响置疑 @rdking @bdistin @mbrowne @shannon
  • 语法不友好,在概念表达上存在多重指向;推荐启用private关键字 @hax @rdking @mbrowne @aimingoo
  • 推荐Symbol.private或者其它 @Igmat @shannon

喜大普奔的是,几乎所有批评者都表达了对@hax前三条主要观点的赞同,TC39被严重打脸。