JavaScript: какие опасности в расширении Array.prototype? - PullRequest
55 голосов
/ 14 января 2012

Руководство по стилю Google JavaScript не советует расширять Array.prototype. Тем не менее, я использовал Array.prototype.filter = Array.prototype.filter || function(...) {...} как способ использовать его (и аналогичные методы) в браузерах, где они не существуют. MDN фактически предоставляет аналогичный пример .

Мне известно о Object.prototype проблемах, но Array не является хеш-таблицей.

Какие проблемы могут возникнуть при расширении Array.prototype, которое заставило Google посоветовать против него?

Ответы [ 10 ]

77 голосов
/ 14 января 2012

Большинство людей упустили из виду этот пункт.Полифаминг или отбрасывание стандартной функциональности, такой как Array.prototype.filter, чтобы она работала в старых браузерах, - это, на мой взгляд, хорошая идея.Не слушай ненавистников.Mozilla даже показывает, как это сделать на MDN.Обычно совет не расширять Array.prototype или другие нативные прототипы может сводиться к одному из следующих:

  1. for..in может работать некорректно
  2. Кто-то еще может захотеть расширитьМассив с тем же именем функции
  3. Он может работать некорректно во всех браузерах, даже с прокладкой.

Вот мои ответы:

  1. Выобычно не нужно использовать for..in на массивах.Если вы это сделаете, вы можете использовать hasOwnProperty, чтобы убедиться, что это законно.
  2. Расширяйте туземцев только тогда, когда вы знаете, что это единственный, кто делает это ИЛИ , когда это стандартные вещи, такие как Array.prototype.filter.
  3. Это раздражает и укусило меня.Старый IE иногда имеет проблемы с добавлением такого рода функциональности.Вам просто нужно посмотреть, работает ли он в каждом конкретном случае.Для меня проблемой было добавление Object.keys в IE7.Казалось, перестать работать при определенных обстоятельствах.Ваш пробег может отличаться.

Проверьте эти ссылки:

Удачи!

9 голосов
/ 14 января 2012

Я дам вам ключевые пункты с ключевыми предложениями из превосходной статьи Николаса Закаса Поддерживаемый JavaScript: не изменяйте объекты, которые вам не принадлежат :

  • Надежность : «Простое объяснение состоит в том, что корпоративному программному продукту требуется согласованная и надежная среда выполнения, чтобы его можно было обслуживать».
  • Несовместимые реализации : «Еще одна опасностьизменение объектов, которыми вы не владеете, - это возможность именования коллизий и несовместимых реализаций. "
  • Что, если все это сделали? :" Проще говоря: если все в вашей команде изменили объекты, которыеони не принадлежат, вы быстро столкнетесь с коллизиями имен, несовместимыми реализациями и кошмарами обслуживания. "

По сути, не делайте этого.Даже если ваш проект никогда не будет использоваться кем-то другим, и вы никогда не собираетесь импортировать сторонний код, не делайте этого.Вы создадите ужасную привычку, которую трудно будет сломать, когда вы начнете пытаться хорошо играть с другими.

4 голосов
/ 10 декабря 2015

Как современное обновление ответа Джамунда Фергюсона:

Обычно совет не расширять Array.prototype или другие нативные прототипы может сводиться к одному из следующих:

  1. for..in может работать неправильно
  2. Кто-то может также захотеть расширить массив с тем же именем функции
  3. Может работать некорректно в любом браузере, даже с прокладкой.

Точки 1. и 2. теперь можно уменьшить в ES6 , используя Символ для добавления вашего метода.

Это создает немного более неуклюжую структуру вызовов, но добавляет свойство, которое не повторяется и не может быть легко скопировано.

// 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(){ /* ... */ };

Плюсы:

  • Поскольку ... работает как положено, символы не повторяются.
  • Нет конфликта имен методов, так как символы являются локальными для области и требуют усилий для извлечения.

Минусы:

3 голосов
/ 14 января 2012

Расширение Array.prototype в вашем собственном коде приложения безопасно (если вы не используете for .. in для массивов, в этом случае вам нужно заплатить за это и весело провести рефакторинг их).

Расширение нативных хост-объектов в библиотеках, которые вы намерены использовать другими, не круто. Вы не имеете права портить окружение других людей в вашей собственной библиотеке.

Либо сделайте это за необязательным методом, например lib.extendNatives(), либо укажите [].filter в качестве требования.

Расширение нативных и хост-объектов

2 голосов
/ 31 мая 2012

Прототип делает это. Это зло Следующий фрагмент демонстрирует, как это может привести к неожиданным результатам:

<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 символов.

2 голосов
/ 14 января 2012

Некоторые люди используют циклы for ... in для перебора массивов.Если вы добавите метод к прототипу, цикл также попытается перебрать ключ с ключом .Конечно, вы не должны использовать это для этого, но некоторые люди все равно используют.

1 голос
/ 01 сентября 2014

Вы можете легко создать своего рода песочницу с библиотекой 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))
0 голосов
/ 17 октября 2016

Я считаю, что этот вопрос заслуживает обновленного ES6 ответа.

ES5

Прежде всего, как уже говорили многие люди. Расширение нативных прототипов для добавления новых стандартов или исправления ошибок или исправления ошибок является стандартной практикой и не наносит вреда. Например, если браузер не поддерживает метод .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/

0 голосов
/ 14 января 2012

Перезаписываемая функция может использоваться внутренними вызовами JavaScript, что может привести к неожиданным результатам.Это одна из причин для указания

Например, я переопределил функцию indexOf массива, и он испортил доступ к массиву, используя [].

0 голосов
/ 14 января 2012

Расширение прототипа - это трюк, который работает только один раз.Вы делаете и , вы используете библиотеку, которая также делает это (несовместимым образом), и boom !

...