No prefix! operator is Ok!
栏目: JavaScript · 发布时间: 6年前
内容简介:所有在我对原有提案的修改中,核心不是“不用原有提案:修改的提议:
所有在我对原有提案的修改中,核心不是“不用 #
字符”,而是将它从一个前缀字符,变成了一个操作符。这一方面是使“声明语法”与“表达式运算”分开,另一方面也让这些修改与ECMAScript规范保持在语法上的一致性。
原有提案: https://github.com/tc39/proposal-class-fields
修改的提议: #issuecomment-429533532
1. 为什么是":“而不是”="?
在所有类、对象等声明性质的语法中,":"是表明"key/value pair"的,既然这里的私有字段仍然是“key/value pair”,那么仍然建议使用该符号。而原提案建议使用 =
并且与TypeScript保持一致,却忽略了TypeScript中完整的语法 x: number = 100
中, :
是指示类型的,而不是用于指示值。——这与ECMAScript的一般规则并不一致。
ECMAScript规范事实上是沿用了“旧式的对象字面量”的属性声明与初值的语法,亦即是:
obj = { x: 100, y: 100 }
注意在这个语法中”有或没有 ,
号都是接受的,但如果有 ,
号则称为一个List,且整个声明是以一个“没有 ,
号的单个属性声明”结束的。——这与传统的对象字面量声明语法一致。
在ECMAScript中,类声明一定程度上沿用和扩展了这一语法。一方面,把“方法声明”给到了对象字面量声明;另一方面,从对象字面量那里把“get/setter声明”拿了过来。而与TypeScript类似的 = xxxx
语法,尽管也是一个称为 Initializer
的语法组件,也的确出现在了对象初始化语法中,但是作为错误语法来识别的(CoverInitializedName):
PropertyDefinition: CoverInitializedName Always throw a Syntax Error if code matches this production.
所以,回到最前面的说明,推荐的语法设计是:
// Because obj = { data: 100, foo() { .... } } // YES class f { data: 100, foo() { ... } } // NO!!! class f { data = 100; ... }
这样使用 f()
类构造出来的对象实例,与用相似语法声明的对象字面量是类似的。不存在语法设计上的“例外(unexpected)”。
2. 为什么是"private x"而不是"#"?
在现有类、对象等声明语法中是使用限定词来指示成员性质的,例如 static
、 get
等。除了生成器函数之外,并不使用限定符号或“前缀(prefix)”,因此我建议用限定词 private
来扩展类声明语法,而反对原提案中使用 #
作为限定符来声明类私有字段(private field of class)。例如:
// YES class f { private data: 100 } // NO! class f { #data = 100; }
3.为什么是",“而不是”;"?
在所有的列表(List)中,ECMAScript采用的分隔符都是 ,
号,例如参数列表、对象/数组成员列表,以及导入导出的名字列表,还有变量 var
声明的名字列表等等。而 ;
在语法中惯例是用作语句结束符或分隔符,包括你所见的各种(任意的)语句,以及 for(;;)
中的子句等等。我们既然是在声明类或对象的“成员列表”,那么显然应该是按“列表(List)”的规则处理成 ,
为好,怎么会用到 ;
号了呢?
TypeScript中在这个位置是这样声明的:
class f { data: string = 'in typescript'; ... }
留意这里的语法特点:符号 :
是用于指示类型的,因此初值声明使用了 =
。这个声明与 var
语句声明类似,TypeScript将这里处理成了 ;
是可以理解的。但ECMAScript何必要绕开现成的 ,
号不用,去使用在这里毫无意义的 ;
号呢?
4.最后一个","号的问题
ECMAScript既然已经接受了对象字面量声明的末尾逗号(object literal trailing commas),那么下面两种声明都可以是合法的了:
// 推荐 class f { data: 100, foo() { ... } } // (可接受) class f { ..., // more, a list with ',' data: 100 foo() { ... } }
5.其它的声明示例
包括:
var data = 'outer'; class f { // reference data, // outer reference, no computed // public data: 100, // for object, equ: f.prototype.data = 100 static data: 100, // for class, equ: f.data = 100 ["da"+"ta"]: 100, // computed static ["da"+"ta"]: 100, // computed // private private data: 100, // normal private object properties private ["da"+"ta"]: 100, // computed private object properties, and symbol keys private static data: 100, // for class static field private static ["da" + "ta"]: 100, // computed // get&setter, etc private get data() { ... } ... }
6.私有属性的存取
私有属性的存取是一个大问题,也是 #
语法争议的焦点。首先必须确定的是:私有属性存取的语义是什么?
在原提案中,私有属性存取是将 #
作为一个前缀(prefix),而存取运算仍然是 .
或 []
运算符。因此,本质上来说,存取运算的操作没变,但是需要在存取中判断属性名的前缀是否是 #
字符。如下例:
// 原提案 class f { #data = 100; foo() { console.log(this.#data); // "#"是前缀,而"."是存取运算符 } }
根本原因出在 .
运算检测 data
是否是私有成员的成本过高。例如:
// 示例:一个不太可行的方案 class f{ data = 100; // 假设这里是私有成员 foo() { console.log(this.data); // 假设这里的this.data指向私有成员 } } x = new f; x.data = '200'; f(); // 应该显示200还是100?
那么在上面这个例子中, x.data
添加了一个公开的属性时,foo()方法是无法识别用户代码的意图的。
所以在旧的提案中需要使用 #
前缀。但是,仔细思考这个问题:
- 私有字段列表与自有成员列表必须是同一个吗?
当然不需要。那么为什么不为私有字段列表安排一个专门的运算符呢?只要“像使用super一样”限定它使用的上下文就好。因此,新的语法设计如下:
class f{ data: 100, foo() { console.log(this#data); } }
也就是说 #
现在是当作一个运算符(operator )在用,而不是一个前缀(prefix)。
但为什么是 #
?
答案是:老实说,我也想不到更好的了。如果你能找一个大家都满意的,我接受。
NOTE:
一个备选的运算符可以是 ->
,但老实说我认为比 #
更差劲。
7.其它
-
在本方案中,是默认用“对象或类”的自有成员列表来实现的,这意味着总是需要用类似
this#xxx
的语法来存取它。不过这并非唯一的方案。 -
关于类似于”采用词法上下文“来实现私有成员的问题,我另写文章来讨论吧。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
大数据大创新:阿里巴巴云上数据中台之道
邓中华 / 电子工业出版社 / 2018-11 / 99
阿里巴巴云上数据中台正服务着阿里生态中的数十个业务板块、百余家公司、千万级客户,在帮助决策层看清甚至决定业态走向的同时,在上万个业务场景中应用并催生创新。 《大数据大创新:阿里巴巴云上数据中台之道》基于作者在阿里巴巴的十年大数据从业经历,精彩演绎云上数据中台之道。《大数据大创新:阿里巴巴云上数据中台之道》基于大数据探索的大趋势,讲述阿里巴巴云上数据中台顶层设计,再以实际案例详述阿里巴巴云上数......一起来看看 《大数据大创新:阿里巴巴云上数据中台之道》 这本书的介绍吧!