С момента своего появления в ECMA-262, 3-е издание, возвращаемое значение метода Array.prototype.push
- Number
:
15.4.4.7 Array.prototype.push ([item1 [, item2 [,…]]])
Аргументы добавляются в конец массива в порядке их появления. Новая длина массива возвращается в результате вызова.
Каковы были конструктивные решения для возврата новой длины массива, в отличие от возврата чего-то потенциально более полезного, например:
- Ссылка на недавно добавленный элемент / ы
- Сам мутированный массив
Почему это было сделано так, и есть ли исторический отчет о том, как эти решения принимались?
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. */
Мне было любопытно, так как вы спросили. Я сделал массив образцов и проверил его в Chrome.
var arr = [];
arr.push(1);
arr.push(2);
arr.push(3);
console.log(arr);
Поскольку у меня уже есть ссылка на массив, а также на каждый объект, который я в него помещаю, есть только одно другое свойство, которое может быть полезным ... длина. Возвращая это одно дополнительное значение структуры данных Array, я теперь имею доступ ко всей соответствующей информации. Кажется, лучший выбор дизайна. Это, или вообще ничего не возвращать, если вы хотите поспорить ради сохранения одной машинной инструкции.
Почему это было сделано так, и есть ли исторический отчет о том, как эти решения принимались?
Понятия не имею - я не уверен, что существует обоснование в этом направлении. Это будет зависеть от разработчика и, вероятно, будет прокомментировано в любой конкретной кодовой базе, реализующей стандарты сценариев ECMA.
Я понимаю ожидание для 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 получение измененных данных для повторной визуализации.
Я не могу объяснить, почему они решили вернуть новую длину, но в ответ на ваши предложения:
- Возврат недавно добавленного элемента:
Учитывая, что 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() );
Похожие вопросы
Связанные вопросы
Новые вопросы
javascript
По вопросам программирования на ECMAScript (JavaScript / JS) и его различных диалектах / реализациях (кроме ActionScript). Включите все соответствующие теги в свой вопрос; например, [node.js], [jquery], [json] и т. д.