Переносимость Array.prototype.* для массивов, таких как объекты или когда-либо родные/хост-объекты

ESMA 262 5.1 для многих функций Array.prototype указано, что они преднамеренно универсальны и описываются в терминах операций [[Get]], [[Put]] и т. д. над Object, но также требуют свойства length.

Таким образом, им разрешено работать со встроенными объектами, такими как:

obj = {"a":true, "length":10};
Array.prototype.push.call(obj, -1);
console.log(obj); // Object { 10: -1, a: true, length: 11 }

Стандарт для нативных объектов имеет примечание:

Возможность успешного применения функции push к хост-объекту зависит от реализации.

Является ли arguments хост-объектом? Кажется, что все DOM (как NodeList) являются хост-объектами. И они работают в современных браузерах.

документы MDN предупреждают о < IE9. А в других браузерах? А как насчет nodejs нативных объектов? Как насчет нативных объектов Rhino/Nashorn?

ОБНОВЛЕНИЕ @jfriend00 Хм, я не подумал об операции [[Put]]... В ECMA 5.1 я нашел специальные примечания о такой ситуации:

Host objects may implement these internal methods in any manner
unless specified otherwise; for example, one possibility is that
[[Get]] and [[Put]] for a particular host object indeed fetch and
store property values but [[HasProperty]] always generates false.
However, if any specified manipulation of a host object's
internal properties is not supported by an implementation, that
manipulation must throw a TypeError exception when attempted.

Так что в плохом случае вы получите TypeError!


person gavenkoa    schedule 04.09.2014    source источник
comment
Я не думаю, что вы можете предположить, что какие-либо функции массива работают с хост-объектами, если вы специально не протестируете их во всех браузерах и во всех версиях тех браузеров, которые вы хотите поддерживать. В частности, вы должны предположить, что они НЕ будут работать, если они изменяют объект, такой как .push() или .splice(). Скорее всего, они будут работать, если просто копируют некоторые элементы в новый массив, такой как .slice(). Что касается node.js, о каких нативных объектах вы спрашиваете, которые еще не являются фактическими массивами?   -  person jfriend00    schedule 05.09.2014
comment
Отвечаю на ваши правки. Вы получите TypeError тогда и только тогда, когда браузер действительно следует спецификации. Если бы все браузеры идеально следовали всем спецификациям, все было бы намного проще. Я поддерживаю свою рекомендацию, что вам нужно протестировать каждую версию/комбинацию браузера, которую вы хотите поддерживать. Лучшее решение — немедленно сделать копию любого не-массива в реальный массив. В общем, создание копии — довольно безопасная вещь, потому что ВСЕ подобные массиву элементы имеют длину и средства их итерации (обычно с [index]), и это все, что вам нужно для создания копии.   -  person jfriend00    schedule 05.09.2014
comment
К вашему сведению, первый пример кода в вашем вопросе немного бессмысленен. Почему .push() в объект JS? Вам нужен ключ и значение, чтобы что-то было свойством объекта, и ТОЛЬКО .push() предоставляет значение. Это кажется несоответствием с самого начала и чем-то, что вы никогда не должны делать или даже пытаться сделать.   -  person jfriend00    schedule 05.09.2014
comment
@gavenkoa: Arguments — это собственный объект, и мутирующие методы массива не будут работать как с собственными, так и с хост-объектами, если объект неизменяем.   -  person cookie monster    schedule 05.09.2014
comment
@jfriend00: Пример .push() не лишен смысла. Метод .push() автоматически предоставляет ключ (новый индекс) на основе анализа текущего .length, предоставленного его объектом. На самом деле это ничем не отличается от того же, например, для объекта jQuery.   -  person cookie monster    schedule 05.09.2014
comment
@cookiemonster - Хорошо, я понимаю твою точку зрения. Но вы бы не поймали меня на использовании .push() для не-массива. Я все еще думаю, что вся эта цепочка рассуждений об использовании широкого набора методов Array для объектов, не являющихся массивами, — это просто рискованное программирование. Если вы хотите выполнять операции с массивом над чем-то, что не является фактическим массивом, скопируйте его в фактический массив, где у вас есть полностью поддерживаемые методы без вопросов о том, что работает, а что нет, или напишите свой собственный код для выполнения операция способом, который, как вы знаете, гарантированно будет безопасным на основе известных возможностей типа объекта, который у вас есть.   -  person jfriend00    schedule 05.09.2014
comment
@jfriend00: Я в основном согласен с этим. Я думаю, что есть место для использования объектов, подобных массиву, хотя я думаю, что часто было бы полезно иметь реальный внутренний массив с методами объекта, которые обращаются к нему по мере необходимости.   -  person cookie monster    schedule 05.09.2014


Ответы (2)


Поскольку вы так и не получили полного ответа, я попытаюсь ответить на некоторые из вопросов, которые вы разместили.

Является ли аргумент хост-объектом?

arguments является частью языка Javascript, а не хост-объектом. Он имеет довольно четко определенное поведение, которое было изменено при работе в режиме strict. Поскольку arguments не сохраняется за пределами текущего вызова функции (даже в замыкании) и поскольку он не предназначен для изменения, обычный способ обработки объекта arguments состоит в том, чтобы немедленно сделать копию в реальный массив, где вы затем можете использовать все обычные методы массива на нем, и он может сохраняться в закрытии для доступа к локальной функции.

Документы MDN предупреждают о ‹ IE9. А в других браузерах?

Здесь не очень конкретно обобщать конкретный браузер. Вместо этого вам придется изучить конкретный объект, а затем определенные версии браузера. У старых версий IE была репутация хост-объектов, которые не взаимодействовали с Javsacript (таким образом), но вам действительно нужно было изучить конкретный объект, чтобы узнать, что вы можете, а что нет.

Как насчет нативных объектов nodejs?

node.js — это гораздо более чистая среда Javascript, чем браузер, потому что в нем нет DOM, нет объекта window и т. д. Были ли у вас какие-либо конкретные объекты node.js, о которых вы хотели спросить? В моем несколько ограниченном опыте работы с node.js я просто вижу настоящие объекты JS, хотя есть много мест, где node.js взаимодействует с ОС, поэтому, возможно, в этих интерфейсах есть некоторые не-JS-объекты (я не встречал никаких объектов). пока, но это возможно).

Так что в плохом случае вы получите TypeError!

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


Если вы ищете безопасный способ кодирования общего назначения, вы никогда не ошибетесь, скопировав похожий на массив хост-объект в реальный массив, а затем используя операции над массивом в фактическом массиве. Это гарантированно безопасно. Существует кросс-браузерный полифилл для Array.prototype.slice, который работает со всеми браузерами для копирования в фактический массив на страница MDN для .slice(). Если вы поддерживаете только IE 9 и выше, вам не нужен полифилл.

И никогда не следует предполагать, что любая операция, которая изменяет подобный массиву объект, в целом безопасна для хост-объекта (могут быть определенные исключения, но вам придется провести много тестов, чтобы быть уверенным). Я предпочитаю писать код, который, как я знаю, будет безопасным и не требует большого количества тестов, чтобы гарантировать это. Копирование в реальный массив дает мне это каждый раз.

person jfriend00    schedule 05.09.2014

Если вы хотите быть уверены, что это сработает, лучше сначала преобразовать его в массив.

Чтобы преобразовать массивоподобный объект в массив, вы можете использовать ES6 Array.from. В настоящее время его поддерживает только Firefox 32, но есть полифилл.

Кроме того, [].slice.call(arrayLike) будет работать в большинстве браузеров.

person Oriol    schedule 04.09.2014
comment
Чем это отличается? Вы используете саму концепцию, о которой он спрашивает, чтобы преобразовать ее в массив, поэтому ваше решение подлежит тому же запросу. - person cookie monster; 05.09.2014
comment
@cookiemonster Как jfriend00 сказал, это с большей вероятностью будет работать, если просто скопировать элементы в массив, например slice . Другие методы, которые изменяют массив, с большей вероятностью потерпят неудачу. - person Oriol; 05.09.2014
comment
Но то же самое и с нативными объектами. Любой неизменяемый объект потерпит неудачу, если к нему будет применен изменяющий метод Array. Он спрашивает, безопасно ли вообще использовать методы Array для хост-объектов. Конкретное использование (будь то собственное или хост) не имеет значения. В конечном итоге вы все еще предлагаете решение, которое вызывает первоначальный вопрос. - person cookie monster; 05.09.2014