为啥扩展语法会将我的字符串转换为数组?
Posted
技术标签:
【中文标题】为啥扩展语法会将我的字符串转换为数组?【英文标题】:Why does spread syntax convert my string into an array?为什么扩展语法会将我的字符串转换为数组? 【发布时间】:2017-12-07 14:01:40 【问题描述】:为什么展开语法会将我的字符串转换为数组?
var v = 'hello';
var [, ...w] = v; // ["e", "l", "l", "o"]
为什么w
不是字符串?
【问题讨论】:
我猜这和JS中的字符串类似数组有关吧? 在这种情况下,“标点符号”一词仅指...
不是标识符或文字这一事实,它还描述了同一类别中的其他标记,例如=
和;
因此,当您不编写 javascript 解析器时,它并不是一个特别具有描述性或有用的术语。所以“标点符号”是一个没有语义意义的低级术语。正如我们在x + y
中不将+
称为标点符号一样,我们将其称为运算符,我们不会在... x
中将...
称为标点符号,我们将其称为扩展语法。该标准仅命名需要在其他地方引用的内容。
你能解释一下为什么你期望w
是一个字符串吗?
按照逻辑,第一个迭代器对象 Object value: 1, done: false
应该被丢弃,w
必须引用在第一个 yield
表达式之后暂停的生成器函数。
@ftor 我猜他们可以使用剩余的迭代器初始化其余变量,但是如果您想多次使用它,则必须将其显式转换为集合数据结构.可迭代协议不包含任何Type.fromIterable
逻辑。它被转换为数组是因为它使用数组语法,而且索引集合确实是最小公分母。
【参考方案1】:
Spread syntax(实际上是RobG 指出的标点符号)允许迭代散布到更小的位。由于字符串是可迭代的(它们在内部是字符数组,更具体地说是表示字符的有序整数序列),它们可以散布成单个字符。
接下来,对数组执行destructuring assignment 以解包和分组展开值。由于您用,
省略了字符数组的第一个元素并且没有分配引用,因此它丢失了,并且可迭代对象的 rest 被保存到w
中,分散到它的个体中部分,字符数组的单个字符。
此操作的具体语义由 ArrayAssignmentPattern 在ECMAScript 2015 Specification 中定义:[ Elisionopt AssignmentRestElement ] 产生式:
12.14.5.2 运行时语义:DestructuringAssignmentEvaluation
带参数值
[...]
ArrayAssignmentPattern : [省略opt AssignmentRestElement ]
让 iterator 为 GetIterator(value)。 ReturnIfAbrupt(迭代器)。 让 iteratorRecord 为 Record [[iterator]]: iterator, [[done]]: false。 如果存在省略,则 一种。令 status 为以 iteratorRecord 作为参数执行 Elision 的IteratorDestructuringAssignmentEvaluation 的结果。 湾。如果 status 是 abrupt completion,那么 一世。如果 iteratorRecord.[[done]] 为 false,则返回 IteratorClose(iterator, status)。 ii.返回Completion(状态)。 令 result 为以 iteratorRecord 作为参数执行 AssignmentRestElement 的IteratorDestructuringAssignmentEvaluation 的结果。 如果 iteratorRecord.[[done]] 为 false,则返回 IteratorClose(iterator, result )。 返回结果。
这里,Elision是指一个或多个逗号(,
)展开时的省略元素,顾名思义,相当于省略的音节,AssignmentRestElement指到将接收传播和解构值的目标,在这种情况下为w
。
这样做是首先从内部@@iterator
方法中获取对象的迭代器,然后逐步执行该迭代器,跳过@ 中的省略 产生式所指示的省略宽度的许多元素987654333@。完成后,它将逐步遍历 AssignmentRestElement 产生式的迭代器,并分配一个包含所有展开值的新数组——这就是w
的含义。它接收展开的单字符数组,解包以排除第一个字符。
从中获取迭代的@@iterator
方法是well-known Symbol,为一个对象更改它可以改变它的迭代方式,如Emissary's answer。具体来说,@@iterator
method for String的默认实现如下:
21.1.3.27 String.prototype [@@iterator]()
当@@iterator 方法被调用时,它会返回一个迭代器对象 (25.1.1.2),该对象迭代一个字符串值的代码点,将每个代码点作为一个字符串值返回。
因此,迭代器允许通过单个代码点或字符串中的字符进行迭代——因此扩展字符串将产生其字符的数组。
【讨论】:
展开操作符是否返回除单个元素或数组以外的任何内容? @BenAston 它没有。它总是将一个可迭代对象分成最小的部分,然后相应地展开它们。 @BenAston 实际上,它甚至不应该返回数组以外的任何内容。 IIRC 永远不会返回单个元素。 是的,我的错。谢谢。 我的问题中的示例可能更常被称为“其余语法”(不传播-我的错误)。这个词法位置的其余语法捕获了可迭代的其余部分,并且似乎执行了从源可迭代(例如,本例中的字符串)到Array
的类型转换(如果需要)。我猜这是因为创建源类型的目标可迭代对象实际上并不总是可能的(因为可迭代对象是 AFAIK 只是具有Symbol.Iterator
属性的对象),因此使用了共同的分母Array
。跨度>
【参考方案2】:
在 ES2015 中,扩展语法专门针对内部 String @@iterator
属性起作用 - 任何对象都可以通过将您自己的 iterator 或 generator / 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]);
您也可以更改字符串迭代器,但您必须显式创建一个String
对象。
【讨论】:
我认为字符串的可迭代性在 ES2015 中是新的? 最新规范中引入了价差运算符。但是您可以像以前的数组一样遍历字符串,即s = 'foo'; s[0] === 'f';
那不是迭代。那是通过索引 AFAIK 检索。
重点是展开运算符,...thing
没有“扩展运算符”。 ...
是一个 punctuator,用于扩展语法 (SpreadElement) 和其余参数 (FunctionRestParameter)。【参考方案3】:
Spread syntax 只能应用于可迭代对象。由于 String 是可迭代的,Spread 运算符可以正常工作并将您的 char
array(String) 拆分为 char
's。
您可以使用以下示例进行检查,该示例证明 String 默认情况下是可迭代的。
var s = 'test';
for (k in s)
console.log(k);
ECMAScript6 规范甚至提到了这个特定的字符串案例。
扩展运算符
将可迭代集合的元素(如数组
or even a string
)传播到文字元素和单个函数参数中。
http://es6-features.org/#SpreadOperator
var str = "foo";
var chars = [ ...str ]; // [ "f", "o", "o" ]
值得一提的是,这是一种特殊情况,仅当您使用带有扩展运算符的直接字符串时才会发生。当你在数组中给出单个字符串时,整个数组将被视为可迭代对象,而不是里面的字符串。
var str = [ "hello" ,2 ];
var other = [ ...str ]; // [ "hello" ,2 ]
我知道上面的例子没有多大意义,只是为了传达这样一个事实,即在这种情况下 String 将被区别对待。
【讨论】:
不是我的反对票,但没有“扩展运算符”,根据 OP,它是“扩展语法”(已编辑)。我猜原来的 MDN 文章也弄错了,所以文本已经更新,但 URL 没有更新。 @RobG 好吧,OP 最初确实有“操作员”,但我对其进行了编辑。从技术上讲,操作员不应该只评估一个单一的结果吗?由于展开语法不只是计算一个项目,它不会是一个运算符。 Axel Rauschmayer 识别扩展和休息运算符。 exploringjs.com/es6/ch_destructuring.html#sec_rest-operator。该规范仅提及SpreadElement
AFAICT。
@RobG 我会选择 ECMA-262。老实说,我更喜欢语法而不是运算符(只是因为我觉得它更正确)但如果规范说标点符号我会去。
@BenAston 只是一点点更新:我做了一些研究,发现它客观地不是运算符而是语法:see my q&a here。【参考方案4】:
这里发生了两件事。
首先,您使用的是数组解构。这允许您获取一个可迭代对象并将其值分配给各个变量。
其次,您正在使用休息参数。这会将可迭代的任何剩余输出转换为数组。
所以你的字符串'hello'
被迭代成它的单个字符,第一个被忽略(因为你省略了目标),然后剩下的字符被转换成一个数组并分配给w
。
【讨论】:
【参考方案5】:因为 javascript 中的字符串在某种程度上被视为字符数组。
例如,当你对一个字符串(比如hello
)执行 for each 循环时,使用 lodash:
_.forEach('hello', function(i)
console.log(i)
)
它输出:
h
e
l
l
o
slice()
等函数也适用于字符串和数组。
【讨论】:
为什么在这个例子中需要 lodash? 因为我不知道如何做一个常规的 javascript forEach 循环了。 JavaScript 中的字符串不是字符数组。 字符串不是“字符数组”。数组是一种特殊的对象,字符串是提供方便属性(如长度)的原语,其字符可以通过索引访问。所以它们符合"iterable" 的要求,但数组与参数、NodeList 和集合一样多(它们都不是数组)。 啊,感谢您对此的澄清。我一直认为它是,因为它可以迭代,具有length
属性,它们都可以使用slice()
等。以上是关于为啥扩展语法会将我的字符串转换为数组?的主要内容,如果未能解决你的问题,请参考以下文章
将rowdatapacket转换为数组,如何将mysql node.js api rowdatapacket转换为数组,将字符串转换为数组