Li3*_*357 30 javascript language-lawyer ecmascript-6 spread-syntax
我听说...两者都称为"传播语法 "和"传播运算符 ",后者更受欢迎.相关MDN文档的URL 表明它最初被称为扩展运算符,但后来改为扩展语法,MDN的运算符列表没有提到它.
Google似乎认为术语运营商更受欢迎并被接受,其中包括Microsoft文档和es6-features.org等网站.
哪个术语在ECMAScript的上下文中最正确,如果有的话,为什么?数组解构赋值怎么样?
Li3*_*357 56
在这个词的所有意义上,它不是一个.这是一个巨大的误解,因为它被引入,尽管流行的观点 - 它不是一个,并有一些客观要点:
应该提到的是,扩展语法有不同的"风格",在不同的上下文中使用,并且在使用相同的标点符号时通常由不同的名称引用.传播语法基本上是...标点符号应用的总称,并且请参阅Felix Kling的详细回答,详细说明所有用法和名称.补充答案中给出了关于这些个人用途的更多解释.
从语义上讲,在ECMAScript的上下文中,运算符只是内置函数,它接受参数并计算单个值 - 用前缀,中缀或后缀表示法编写,通常使用符号名称,如+或/.来自维基百科:
简单地说,以某种方式评估涉及运算符的表达式,并且结果值可以只是值(r值),或者可以是允许赋值的对象(l值).
例如,+运算符产生诸如2的值,这是右手侧表达式,并且.运算符导致允许赋值的对象,例如foo.bar左手侧表达式.
从表面上看,...标点符号1看起来是前缀一元运算符:
const baz = [foo, ...bar];
Run Code Online (Sandbox Code Playgroud)
但是这个论点的问题是...bar不能评估为奇异值; 它bar逐个传播迭代的元素.传播参数也是如此:
foo(...bar);
Run Code Online (Sandbox Code Playgroud)
在这里,从iterable foo接收单独的参数bar.它们是传递给的单独值foo,而不仅仅是一个值.它不符合运算符的定义,因此它不是一个.
另一个要点是运营商应该是独立的并且返回单个值.例如:
const bar = [...foo];
Run Code Online (Sandbox Code Playgroud)
如前所述,这很有效.当您尝试执行此操作时会出现问题:
const bar = ...foo;
Run Code Online (Sandbox Code Playgroud)
如果扩展语法是一个运算符,后者将正常工作,因为运算符将表达式计算为单个值,但传播不会因此失败.扩展语法和扩展参数仅在数组和函数调用的上下文中起作用,因为这些结构接收通过扩展数组元素或参数提供的多个值.评估多个值超出了操作员能够做的范围.
完整的运算符列表在ECMAScript 2015语言规范中的条款§12.5至§12.15中列出,其中...引入的规范未提及....还可以推断出它不是运营商.本回答中提到的两个主要情况,其中扩展语法在生产中,对于函数调用(扩展参数)或数组文字(扩展语法),如下所述:
ArrayLiteral : [ Elisionopt ] [ ElementList ] [ ElementList , Elisionopt ] ElementList : Elisionopt AssignmentExpression Elisionopt SpreadElement ElementList , Elisionopt AssignmentExpression ElementList , Elisionopt SpreadElement Elision : , Elision , SpreadElement : ... AssignmentExpression
对于函数调用:
CallExpression : MemberExpression Arguments Arguments : ( ) ( ArgumentList ) ArgumentList : AssignmentExpression ... AssignmentExpression ArgumentList , AssignmentExpression ArgumentList , ... AssignmentExpression
在这些作品中,可以得出结论:传播'运算符'不存在.如前所述,运营商应该是独立的,如同const bar = ...foo并评估一个单一的价值.语言的语法阻止了这一点,这意味着扩展语法从来就不是独立的.它是数组初始值设定项和函数调用的扩展,是语法的扩展.
语法,由维基百科定义:
在计算机科学中,计算机语言的语法是一组规则,它们定义被认为是该语言中正确结构化的文档或片段的符号组合.
语法基本上是语言的"形式",规则来管理代码应该如何看待是合法的,以及代码应该如何编写.在这种情况下,ECMAScript的语法专门定义了...标点符号,它只出现在函数调用和数组文字中作为扩展名 - 这是一个定义符号(...foo)组合的规则,它们被认为是合法的,因此它的语法类似于箭头函数(=>)不是运算符,而是语法2.
呼叫...运营商是用词不当.运算符是一个内置函数,它接受参数(操作数)并采用前缀,中缀或后缀表示法的形式,并且只计算一个值....在满足前两个条件的同时,不满足最后一个条件....相反,它是语法,因为它是在语言的语法中专门和明确定义的.因此,"扩展运算符"客观上更准确地称为"扩展语法".
1术语"标点符号"是指ECMAScript 2015及后续规范中的标点符号.这些符号包括语法组件和运算符,并且是语言的标点符号....是一个标点符号本身,但术语"传播语法"指的是标点符号的整个应用.
2 =>本身就是一个标点符号,正如...我所指的那样具体是箭头函数语法,=>punctuator((…) => { … })的应用,正如扩展语法指的是...标点符号的应用.
主要答案中未涵盖传播/休息语法的其他多种用途。它们包括:
扩展语法的使用,通常称为休息语法,用于函数参数中可变数量的参数。这与传播参数不同,传播参数用于将参数传递给基于可迭代元素的函数调用。例如:
function add(...addends) {
…
}
Run Code Online (Sandbox Code Playgroud)
在这里,函数使用了 rest 语法add来接收标识符中的其余参数addends。这似乎addends与传递参数的数组一样评估为奇异值,但是如果我们尝试:
function foo(...[bar, baz]) {
…
}
Run Code Online (Sandbox Code Playgroud)
在这里,bar和baz都将被分配一个与传递的第一个和第二个参数相对应的值——因此这并不总是评估为一个值。潜在的问题是,...addends在第一个示例和...[bar, baz]第二个示例中,实际上根本不计算值 - 它只是在将参数数组分配给标识符的操作期间使用。因此,它的语法允许函数的参数数量可变,而不是运算符。
Spread 语法也可以在数组解构赋值期间使用,并且实际上在语言规范中被称为剩余元素(因为在解构中使用时,它获得了解构后的可迭代对象的其余部分)。可以提出令人信服的论点,因为这看起来像一个运算符:
const [...bar] = [1, 2, 3];
Run Code Online (Sandbox Code Playgroud)
它像前缀一元运算符一样使用。这里,bar计算为[1, 2, 3]?—? 这是一个单一的值。但这并不总是发生,例如:
const [first, ...[second, third]] = [1, 2, 3];
Run Code Online (Sandbox Code Playgroud)
这里,first、second和 分别third求值为 1、2 和 3。但是...[second, third]分配给两个标识符,而不是一个,并且不会评估为单数,而是两个。就像 rest 语法一样,潜在的问题是,...bar在第一个示例和...[second, third]第二个示例中,实际上根本没有计算出一个值——它只是在赋值操作期间使用。因此,它根本不是运算符2,只是帮助解压缩值的新语法。
扩展语法的最后一个用途是对象字面量,通常称为“对象扩展属性”,其中目标对象自己的可枚举属性扩展到另一个对象,例如:
const foo = { ...bar };
Run Code Online (Sandbox Code Playgroud)
这不是运算符,就像数组展开语法不是运算符一样。概念是相同的,而不是数组中的索引和元素,bar的可枚举键和值分布到foo。这里,收集的bar的属性是传播-不只是一个单一的值,因此它不适合运营商的定义。
1 Object rest/spread 属性目前在 ECMAScript 的第 3 阶段提案中,并且很可能在不久的将来添加
2解构赋值作为运算符的另一个问题是,除了语义之外,语言规范将其定义为补充语法—— ? 不是补充运算符,这是理所当然的。它不是独立的,因为这不起作用:
const ...bar = [1, 2, 3, 4];
Run Code Online (Sandbox Code Playgroud)
它是上下文的,只允许在语言的语法、对象字面量和作为左侧表达式的数组字面量中。它也是改进左侧表达式解释的语法。同样,这是为语言添加新语法的扩展,是对现有语法的改进。这重申了规范的论点。