Почему синтаксис распространения преобразует мою строку в массив?

Почему синтаксис распространения преобразует мою строку в массив?

var v = 'hello';
var [, ...w] = v; // ["e", "l", "l", "o"]

Почему w не строка?

Ответы

Ответ 1

Синтаксис распространения (на самом деле пунктуатор, отмеченный RobG) позволяет использовать итерации для распространения на более мелкие биты. Поскольку строки являются итерабельными (они представляют собой массивы символов внутри, более конкретно упорядоченные последовательности целых чисел, представляющие символы), их можно разложить на отдельные символы.

Затем назначение деструкции выполняется в массиве для распаковки и группировки значений распространения. Поскольку вы опускаете первый элемент массива символов с помощью , и не назначаете ссылку, он теряется, а остальная часть итерируемого объекта сохраняется в w, распространяется на него отдельные части, одиночные символы символа массив.


Конкретная семантика этой операции определена в Спецификация ECMAScript 2015 с помощью параметра ArrayAssignmentPattern: [Elision opt AssignmentRestElement ] производство:

12.14.5.2 Семантика времени выполнения: DestructuringAssignmentEvaluation

со значением параметра

[...]

ArrayAssignmentPattern: [Elision opt AssignmentRestElement]

  • Пусть итератор GetIterator (значение).
  • ReturnIfAbrupt (итератор).
  • Пусть iteratorRecord будет Record {[[iterator]]: iterator, [[done]]: false}.
  • Если присутствует Elision, то а. Пусть статус будет результатом выполнения IteratorDestructuringAssignmentEvaluation Elision с iteratorRecord в качестве аргумента.
    б. Если статус - внезапное завершение, то     я. Если iteratorRecord. [[Done]] false, верните IteratorClose (iterator, status).
        II. Верните Completion (статус).
  • Пусть результат будет результатом выполнения IteratorDestructuringAssignmentEvaluation параметра AssignmentRestElement с iteratorRecord в качестве аргумента.
  • Если iteratorRecord. [[done]] false, верните IteratorClose (итератор, результат).
  • Результат возврата.

Здесь Elision относится к пропущенному элементу при распространении с одной или несколькими запятыми (,), сравнимой с пропущенными слогами, как следует из названия, и AssignmentRestElement ссылается на цель, которая получит разброс и деструктурированные значения, w в этом случае.

Что это значит, сначала получите итератор объекта, из внутреннего метода @@iterator и шаги через этот итератор, пропуская, однако, многие элементы, обозначенные шириной elision, производством Elision в IteratorDestructuringAssignmentEvaluation. Как только это будет сделано, он пройдет через итератор производства AssignmentRestElement и назначит новый массив со всеми значениями распространения - то, что w. Он получает распределенный односимвольный массив, распакованный для исключения первого символа.

Метод @@iterator, в котором получена итерация, является известным символом и его изменение для объекта может изменить его итерации, как в Ответ Эмиссара. В частности, стандартная реализация метода @@iterator для String выглядит следующим образом:

21.1.3.27 String.prototype [@@iterator]()

Когда вызывается метод @@iterator, он возвращает объект Iterator (25.1.1.2), который выполняет итерацию над кодовыми точками значения String, возвращая каждую кодовую точку как значение String.

Таким образом, итератор допускает итерацию через отдельные кодовые точки или символы строки - и, следовательно, распространение строки приведет к массиву ее символов.

Ответ 2

В ES2015 синтаксис распространения специфически действует против внутреннего свойства @@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]);

Ответ 3

Синтаксис распространения может применяться только к итерабельным объектам. Поскольку String iserable Оператор Spread работает отлично и разбивает ваш массив char (String) на char.

Вы можете проверить это с помощью примера ниже, который показывает, что String по умолчанию является итерируемой.

var s = 'test';    
for (k in s) {
  console.log(k);
}

И спецификация ECMAScript6 даже упоминала об этом конкретном случае String.

Оператор распространения

Распространение элементов итеративной коллекции (например, массив or even a string) в оба элементарных элемента и отдельные параметры функции.

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

var str = "foo";
var chars = [ ...str ]; // [ "f", "o", "o" ]

И стоит отметить, что это конкретный случай и происходит только при использовании прямого оператора String with spread. Когда вы даете одну строку внутри массива, весь массив будет обрабатываться как итерируемый объект, а не строка внутри.

var str = [ "hello" ,2 ];
var other = [  ...str ]; // [  "hello" ,2 ]

Я знаю, что приведенный выше пример не имеет большого смысла, а просто для того, чтобы передать тот факт, что в этом случае в String будут обрабатываться по-разному.

Ответ 4

Здесь есть две вещи.

Во-первых, вы используете деструктурирование массива. Это позволяет вам выполнять итерацию и присваивать значения из нее отдельным переменным.

Во-вторых, вы используете параметр rest. Это преобразует любой оставшийся вывод итерабельного в массив.

Итак, ваша строка 'hello' итерируется в ее отдельные символы, первая игнорируется (потому что вы пропустили пункт назначения), тогда остальные символы превращаются в массив и назначаются w.

Ответ 5

Поскольку строка в javascript рассматривается как массив символов.

Например, когда вы делаете a для каждого цикла над строкой (пусть hello), с lodash:

_.forEach('hello', function(i) {
    console.log(i)
})

выводится:

h
e
l
l
o

Функции типа slice() также работают как с строками, так и с массивами.