JavaScript: Какие опасности в распространении Array.prototype?
Руководство по стилю JavaScript в JavaScript советует не распространять Array.prototype
. Однако я использовал Array.prototype.filter = Array.prototype.filter || function(...) {...}
Array.prototype.filter = Array.prototype.filter || function(...) {...}
как способ иметь его (и подобные методы) в браузерах там, где их не существует. На самом деле MDN предоставляет аналогичный пример.
Я знаю о проблемах Object.prototype
, но Array
не является хеш-таблицей.
Какие проблемы могут возникнуть при распространении Array.prototype
которые заставили Google посоветовать против этого?
Ответы
Ответ 1
Большинство людей пропустили этот момент. На мой взгляд, хорошая функциональность, например, Array.prototype.filter
, так как она работает в старых браузерах, является хорошей идеей. Не слушайте ненавистников. Mozilla даже показывает вам, как это сделать на MDN. Обычно совет по нераспространению Array.prototype
или других натурных прототипов может свести к одному из них:
-
for..in
может работать неправильно
- Кто-то может также захотеть расширить Array с тем же именем функции
- Он может работать неправильно в каждом браузере, даже с помощью прокладки.
Вот мои ответы:
- Вам не нужно обычно использовать
for..in
в массиве. Если вы это сделаете, вы можете использовать hasOwnProperty
, чтобы убедиться, что он прав.
- Расширяйте только туземцев, когда вы знаете, что делаете это только один, или когда это стандартный материал, например
Array.prototype.filter
.
- Это раздражает и немного меня. У старого IE иногда возникают проблемы с добавлением такого рода функций. Вам просто нужно посмотреть, работает ли это в каждом конкретном случае. Для меня проблема, которую я имел, заключалась в добавлении
Object.keys
в IE7. Казалось, что он прекратил работать при определенных обстоятельствах. Ваш пробег может отличаться.
Проверьте эти ссылки:
Удачи!
Ответ 2
Я дам вам пункты с ключевыми предложениями из отличной статьи Николаса Закаса Поддерживаемый JavaScript: не изменяйте объекты, которые у вас нет:
- Надежность: "Простое объяснение состоит в том, что корпоративный программный продукт нуждается в совместимой и надежной среде исполнения, которая должна поддерживаться".
- Несовместимые реализации: "Еще одна опасность изменения объектов, которые у вас нет, - это возможность именования коллизий и несовместимых реализаций".
- Что делать, если все это сделали?: "Проще говоря: если бы все члены вашей команды изменили объекты, которые у них не были, вы быстро столкнетесь с именованием столкновений, несовместимыми реализациями и кошмарами обслуживания".
В принципе, не делайте этого. Даже если ваш проект никогда не будет использоваться кем-либо еще, и вы никогда не будете импортировать сторонний код, не делайте этого. Вы создадите ужасную привычку, которую трудно сломать, когда вы начнете пытаться играть с другими.
Ответ 3
Расширение Array.prototype
в вашем собственном коде приложения безопасно (если вы не используете for .. in
на массивах, и в этом случае вам нужно заплатить за это и получать удовольствие от рефакторинга их).
Расширение собственных объектов хоста в библиотеках, которые вы намерены использовать другим, не круто. Вы не имеете права коррумпировать окружающую среду других людей в своей собственной библиотеке.
Либо сделать это за дополнительным методом, например lib.extendNatives()
, либо иметь [].filter
как требование.
Расширение аборигенов и объектов хоста
Ответ 4
Как современное обновление для Джамунда Фергюсона:
Обычно совет о нераспространении Array.prototype или других собственных прототипов может свести к одному из следующих:
- for..in может не работать должным образом
- Кто-то может также захотеть расширить Array с тем же именем функции
- Он может работать неправильно в каждом браузере, даже с помощью прокладки.
Точки 1. и 2. теперь можно смягчить в ES6 с помощью Symbol, чтобы добавить свой метод.
Он создает немного более неуклюжую структуру вызовов, но добавляет свойство, которое не повторяется и не может быть легко дублировано.
// Any string works but a namespace may make library code easier to debug.
var myMethod = Symbol('MyNamespace::myMethod');
Array.prototype[ myMethod ] = function(){ /* ... */ };
var arr = [];
// slightly clumsier call syntax
arr[myMethod]();
// Also works for objects
Object.prototype[ myMethod ] = function(){ /* ... */ };
Плюсы:
- For..in работает как ожидалось, символы не повторяются.
- Никакое столкновение имен методов в качестве символов не является локальным для области видимости и требует усилий для извлечения.
Минусы:
Ответ 5
Некоторые люди используют циклы for ... in
для итерации массивов. Если вы добавите метод в прототип, цикл также попытается выполнить итерацию по этому ключу. Конечно, вы не должны использовать его для этого, но некоторые люди все равно.
Ответ 6
Prototype делает это. Это зло. Следующий фрагмент демонстрирует, как это может привести к неожиданным результатам:
<script language="javascript" src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script>
<script language="javascript">
a = ["not", "only", "four", "elements"];
for (var i in a)
document.writeln(a[i]);
</script>
Результат:
not only four elements function each(iterator, context) { var index = 0; . . .
и около 5000 символов.
Ответ 7
Вы можете легко создать нечто вроде песочницы с библиотекой poser
.
Посмотрите https://github.com/bevacqua/poser
var Array2 = require('poser').Array();
// <- Array
Array2.prototype.eat = function () {
var r = this[0];
delete this[0];
console.log('Y U NO .shift()?');
return r;
};
var a = new Array2(3, 5, 7);
console.log(Object.keys(Array2.prototype), Object.keys(Array.prototype))
Ответ 8
Расширение прототипа - это трюк, который работает только один раз. Вы делаете, и вы используете библиотеку, которая также делает это (несовместимым образом) и стрелой!
Ответ 9
Функция, которую вы переопределяете, может использоваться внутренними вызовами javascript, что может привести к неожиданным результатам. Это одна из причин руководства
Например, я перепробовал функцию indexOf массива и перепутал доступ к массиву с помощью [].
Ответ 10
Я считаю, что этот вопрос заслуживает обновления ES6.
ES5
Прежде всего, как уже многие люди заявили. Расширение собственных прототипов до подгонки или polyfill новых стандартов или исправлений ошибок является стандартной практикой и не является вредным. Например, если браузер не поддерживает метод .filter if (!Array.prototype.filter)
, вы можете добавить эту функцию самостоятельно. Фактически, язык предназначен для того, чтобы делать именно это, чтобы управлять обратной совместимостью.
Теперь вы можете позаботиться о том, что, поскольку объект JavaScript использует прототипное наследование, расширение собственного объекта типа Array.prototype
без вмешательства должно быть легким, но до ES6 это невозможно.
В отличие от объектов, например, вам пришлось полагаться и модифицировать Array.prototype
, чтобы добавить свои собственные методы. Как отмечали другие, это плохо, потому что он загрязняет глобальное пространство имен, может неожиданно вмешаться в другой код, имеет потенциальные проблемы с безопасностью, является кардинальным грехом и т.д.
В ES5 вы можете попробовать взломать это, но реализации на самом деле не очень полезны. Для более подробной информации я рекомендую вам ознакомиться с этим очень информативным сообщением: http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
Вы можете добавить метод в массив или даже конструктор массива, но вы столкнетесь с проблемами, которые пытаются работать с методами массива, которые полагаются на свойство length. Хуже всего, эти методы возвращают родной Array.prototype
, а не ваш новый новый массив подкласса, т.е. subClassArray.slice(0) instanceof subClassArray === false
.
ES6
Однако теперь с ES6 вы можете создавать подклассы с помощью class
в сочетании с extends Array
, который преодолевает все эти проблемы. Он оставляет объект Array.prototype
неповрежденным, создает новый подкласс и методы массива, которые он наследует, будут одного и того же подкласса! https://hacks.mozilla.org/2015/08/es6-in-depth-subclassing/
См. скрипку ниже для демонстрации:
https://jsfiddle.net/dmq8o0q4/1/