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
的语法来存取它。不过这并非唯一的方案。 -
关于类似于”采用词法上下文“来实现私有成员的问题,我另写文章来讨论吧。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Build Your Own Web Site the Right Way Using HTML & CSS
Ian Lloyd / SitePoint / 2006-05-02 / USD 29.95
Build Your Own Website The Right Way Using HTML & CSS teaches web development from scratch, without assuming any previous knowledge of HTML, CSS or web development techniques. This book introduces you......一起来看看 《Build Your Own Web Site the Right Way Using HTML & CSS》 这本书的介绍吧!