С момента своего появления в ECMA-262, 3-е издание, возвращаемое значение метода Array.prototype.push - Number:

15.4.4.7 Array.prototype.push ([item1 [, item2 [,…]]])

Аргументы добавляются в конец массива в порядке их появления. Новая длина массива возвращается в результате вызова.

Каковы были конструктивные решения для возврата новой длины массива, в отличие от возврата чего-то потенциально более полезного, например:

  • Ссылка на недавно добавленный элемент / ы
  • Сам мутированный массив

Почему это было сделано так, и есть ли исторический отчет о том, как эти решения принимались?

19
Bryce 14 Дек 2015 в 06:00

4 ответа

Лучший ответ

Я разместил это в центре связи TC39 и смог узнать немного больше об истории, стоящей за этим:

push, pop, shift, unshift были первоначально добавлены в JS1.2 (Netscape 4) в 1997 году.

Там были смоделированы по аналогии с именами функций в Perl.

JS1.2 push следовал соглашению Perl 4 о возвращении последнего отправленного элемента. В JS1.3 (Netscape 4.06, лето 1998) изменился push, чтобы следовать соглашениям Perl 5 о возвращении новой длины массива.

см. https://dxr.mozilla.org/classic/source/ JS / SRC / jsarray.c # 804

/*
 * If JS1.2, follow Perl4 by returning the last thing pushed.  Otherwise,
 * return the new array length.
 */
7
romellem 3 Июн 2019 в 14:24

Мне было любопытно, так как вы спросили. Я сделал массив образцов и проверил его в Chrome.

var arr = [];
arr.push(1);
arr.push(2);
arr.push(3);
console.log(arr);

enter image description here

Поскольку у меня уже есть ссылка на массив, а также на каждый объект, который я в него помещаю, есть только одно другое свойство, которое может быть полезным ... длина. Возвращая это одно дополнительное значение структуры данных Array, я теперь имею доступ ко всей соответствующей информации. Кажется, лучший выбор дизайна. Это, или вообще ничего не возвращать, если вы хотите поспорить ради сохранения одной машинной инструкции.

Почему это было сделано так, и есть ли исторический отчет о том, как эти решения принимались?

Понятия не имею - я не уверен, что существует обоснование в этом направлении. Это будет зависеть от разработчика и, вероятно, будет прокомментировано в любой конкретной кодовой базе, реализующей стандарты сценариев ECMA.

2
ThisClark 14 Дек 2015 в 03:31

Я понимаю ожидание для array.push() возврата измененного массива вместо его новой длины. И желание использовать этот синтаксис по причинам цепочки.
Однако для этого есть встроенный способ: array.concat(). Обратите внимание, что concat ожидает получить массив, а не элемент. Поэтому не забудьте обернуть элементы, которые вы хотите добавить в [], если их еще нет в массиве.

newArray = oldArray.concat([newItem]);

Цепочка массивов может быть выполнена с помощью {{X0 }}, так как он возвращает массив,
но не .push(), так как возвращает целое число (новая длина массива).

Вот общий шаблон, используемый в React для изменения переменной state, основанный на ее предыдущем значении:

// the property value we are changing
selectedBook.shelf = newShelf;

this.setState((prevState) => (
  {books: prevState.books
           .filter((book) => (book.id !== selectedBook.id))
           .concat(selectedBook)
  }
));

state объект имеет свойство books, которое содержит массив book.
book - это объект со свойствами id и shelf (среди прочих).
setState() принимает объект, который содержит новое значение, которое будет присвоено state

selectedBook уже находится в массиве books, но его свойство shelf необходимо изменить.
Однако мы можем предоставить только setState объект верхнего уровня.
Мы не можем сказать ему, чтобы найти книгу, найти свойство в этой книге и придать ей новое значение.

Поэтому мы берем массив books как есть.
filter чтобы удалить старую копию selectedBook.
Затем concat добавить selectedBook обратно после обновления его свойства shelf.

Отличный вариант использования для цепочки push.
Тем не менее, правильный способ сделать это на самом деле с concat.

Резюме:
array.push() вернет число (новая длина мутированного массива).
array.concat[] вернет новый "мутированный массив.
Технически, он возвращает массив new с измененным элементом, добавленным в конец, и оставляет исходные массивы без изменений. Возвращение нового экземпляра массива, в отличие от повторного использования существующего экземпляра массива, является важным отличием, которое делает очень полезным для объектов состояния в приложениях React получение измененных данных для повторной визуализации.

1
SherylHohman 6 Фев 2020 в 21:49

Я не могу объяснить, почему они решили вернуть новую длину, но в ответ на ваши предложения:

  • Возврат недавно добавленного элемента:

Учитывая, что JavaScript использует присваивание в стиле C, которое генерирует присвоенное значение (в отличие от присваивания в стиле Basic, которое не имеет), вы все равно можете иметь такое поведение:

var addedItem;
myArray.push( addedItem = someExpression() );

(хотя я признаю, что это означает, что вы не можете использовать его как часть r-значения в комбинации объявления + присваивания)

  • Возврат самого мутированного массива:

Это было бы в стиле «беглых» API, которые значительно приобрели популярность после завершения ECMAScript 3, и это не будет соответствовать стилю других библиотечных функций в ECMAScript, и опять же, это не так уж и много лишней работы для включения Сценарии, которые вы ищете, создав собственный метод push:

Array.prototype.push2 = function(x) {
    this.push(x);
    return this;
};

myArray.push2( foo ).push2( bar ).push2( baz );

Или же:

Array.prototype.push3 = function(x) {
    this.push(x);
    return x;
};

var foo = myArray.push3( computeFoo() );
2
Dai 14 Дек 2015 в 03:15