为什么扩展语法将我的字符串转换为数组?

Ben*_*Ben 18 javascript ecmascript-6

为什么扩展语法将我的字符串转换为数组?

var v = 'hello';
var [, ...w] = v; // ["e", "l", "l", "o"]
Run Code Online (Sandbox Code Playgroud)

为什么w不是一个字符串?

Li3*_*357 18

扩展的语法(实际上是一个标点如所指出RobG)允许iterables被扩散成更小的比特.由于字符串是迭代的(它们是内部的字符数组,更具体地说是表示字符的有序整数序列),因此它们可以扩展为单个字符.

接下来,对阵列执行解构分配以对扩展值进行解包和分组.由于你省略了字符数组的第一个元素,并且没有分配引用,它会丢失,并且可迭代对象的其余部分被保存到其中w,扩展到它的各个部分,即字符数组的单个字符.


此操作的特定语义由ECMAcript 2015规范中ArrayAssignmentPattern:[Elision opt AssignmentRestElement]生产定义:

12.14.5.2运行时语义:DestructuringAssignmentEvaluation

带参数

[...]

ArrayAssignmentPattern:[Elision opt AssignmentRestElement]

  1. 迭代器成为GetIterator().
  2. ReturnIfAbrupt(迭代器).
  3. iteratorRecord为Record {[[iterator]]:iterator,[[done]]:false }.
  4. 如果有Elision,那么
    a.让状态是执行的结果IteratorDestructuringAssignmentEvaluation省音iteratorRecord作为参数.
    湾 如果状态突然完成,那么
        我.如果iteratorRecord.[[done]]为false,则返回IteratorClose(iterator,status).
        II.退货完成(状态).
  5. 我们的结果是执行的结果IteratorDestructuringAssignmentEvaluationAssignmentRestElementiteratorRecord作为参数.
  6. 如果iteratorRecord.[[done]]为false,则返回IteratorClose(迭代器,结果).
  7. 返回结果.

这里,Elision在使用一个或多个逗号(,)进行传播时引用省略的元素,与名称所暗示的省略音节相当,而AssignmentRestElement指的是将接收传播和解构值的目标,w在本例中.

这样做首先得到对象的迭代器,从内部@@iterator方法和迭代器的步骤,跳过由IteratorDestructuringAssignmentEvaluation中Elision生成的elision宽度指示的许多元素.完成后,它将逐步完成AssignmentRestElement生成的迭代器,并分配一个包含所有扩展值的新数组 - 就是这样.它接收展开的单字符数组,解压缩以排除第一个字符.w

@@iterator获取迭代的方法是一个众所周知的符号,并且为对象更改它可以改变它的迭代方式,如在Emissary的答案中那样.具体来说,@@iteratorString方法的默认实现如下:

21.1.3.27 String.prototype [@@ iterator]()

当调用@@ iterator方法时,它返回一个Iterator对象(25.1.1.2),该对象迭代String值的代码点,将每个代码点作为String值返回.

因此,迭代器允许迭代单个代码点或字符串的字符 - 因此传播字符串将导致其字符数组.


Sur*_*tta 6

扩展语法只能应用于可迭代对象.由于String是可迭代的,因此Spread运算符工作正常,并将char数组(String)拆分为char.

您可以使用下面的示例检查,该示例证明String默认是可迭代的.

var s = 'test';    
for (k in s) {
  console.log(k);
}
Run Code Online (Sandbox Code Playgroud)

ECMAScript6规范甚至提到了这个特定的String情况.

传播运营商

将可迭代集合(如数组or even a string)的元素传播到文字元素和单个函数参数中.

http://es6-features.org/#SpreadOperator

var str = "foo";
var chars = [ ...str ]; // [ "f", "o", "o" ]
Run Code Online (Sandbox Code Playgroud)

值得一提的是,这是一个特定的情况,只有在使用带扩展运算符的直接String时才会发生.当您在数组中提供单个String时,整个数组将被视为可迭代对象而不是内部的字符串.

var str = [ "hello" ,2 ];
var other = [  ...str ]; // [  "hello" ,2 ]
Run Code Online (Sandbox Code Playgroud)

我知道上面的例子没有多大意义,但只是为了表达在这种情况下String将被区别对待的事实.


Emi*_*ary 6

在ES2015传播语法是专门作用于内部字符串@@iterator属性-任何对象都可以以这种方式通过分配自己的迭代的迭代器发电机/function*obj[Symbol.iterator]财产.

例如,您可以更改新阵列的默认行为...

const a = [...'hello'];
a[Symbol.iterator] = function* (){
    for(let i=0; i<this.length; ++i)
        yield `${this[i]}!`;
};
console.log([...a]);
Run Code Online (Sandbox Code Playgroud)

也可以更改字符串迭代器,但您必须显式创建String对象.