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/