Добавление пользовательских функций в Array.prototype

Я работал над приложением asp.net с поддержкой AJAX. Я просто добавил некоторые методы в Array.prototype, например

Array.prototype.doSomething = function(){
   ...
}

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

Но когда я протестировал его, работая со всей страницей, у меня были проблемы. У нас были некоторые пользовательские расширения ajax, и они начали вести себя как неожиданное: некоторые элементы управления отображали "undefined" вокруг своего содержимого или значения.

Что может быть причиной этого? Я что-то пропустил для модификации прототипа стандартных объектов?

Примечание. Я уверен, что ошибка начинается, когда я изменяю прототип для массива. Он должен быть совместим только с IE.

Ответ 1

Модификация встроенных прототипов объектов в целом является плохой идеей, поскольку она всегда может конфликтовать с другим кодом на той же странице.

В случае прототипа объекта Array это особенно плохая идея, поскольку он может создавать помехи любому фрагменту кода, который перебирает элементы любого массива, например, для for.. in.

Для иллюстрации на примере (заимствовано здесь):

Array.prototype.foo = 1;

// somewhere deep in other javascript code...
var a = [1,2,3,4,5];
for (x in a){
    // Now foo is a part of EVERY array and 
    // will show up here as a value of 'x'
}

Обратное верно: вы должны избегать for..in, если какой-то n00b изменил прототип Array, и вам следует избегать модификации прототипа Array, если какой-то n00b использовал for..in в массиве.

Было бы лучше, если бы вы создавали свой собственный тип конструктора объекта с функцией doSomething, а не расширяли встроенный массив.

Как насчет Object.defineProperty?

В настоящее время существует Object.defineProperty как общий способ расширения прототипов объектов без перечисления новых свойств, хотя я все еще не буду использовать это в качестве оправдания для расширения встроенных типов, потому что даже за for..in еще есть потенциал для других конфликтов с другими сценариями. Представьте себе кого-то, использующего две платформы Javascript, которые пытаются аналогичным образом расширить массив и выбрать одно и то же имя метода. Или подумайте, кто-то разветвляет ваш код, а затем помещает как оригинальную, так и разветвленную версии на одну и ту же страницу. Будут ли работать пользовательские улучшения объекта Array?

Это реальность с Javascript, и почему вы должны избегать модификации прототипов встроенных типов, даже с Object.defineProperty. Определите свои собственные типы с вашими собственными конструкторами.

Ответ 2

Хотя вероятность столкновения с другими битами кода, переопределение функции в прототипе все еще является риском, если вы хотите сделать это с современными версиями JavaScript, вы можете использовать метод Object.defineProperty, отключив перечисляемый бит, например

// functional sort
Object.defineProperty(Array.prototype, 'sortf', {
    value: function(compare) { return [].concat(this).sort(compare); }
});

Ответ 3

Существует осторожность! Возможно, вы сделали это: скрипка демонстрации

Скажем, массив и метод foo, которые возвращают первый элемент:

var myArray = ["apple","ball","cat"];

foo(myArray) // <- 'apple'

function foo(array){
    return array[0]
}

Вышеприведенное хорошо, потому что функции во время интерпретации поднимаются вверх.

Но это НЕ работает: (Поскольку прототип не определен)

myArray.foo() // <- 'undefined function foo'

Array.prototype.foo = function(){
    return this[0]
}

Для этого просто определите прототипы вверху:

Array.prototype.foo = function(){
    return this[0]
}

myArray.foo() // <- 'apple'

И ДА! Вы можете переопределить прототипы!!! Это ДОПУСКАЕТСЯ. Вы даже можете определить свой собственный метод add для массивов.

Ответ 4

В общем, беспорядок с основными объектами javascript - плохая идея. Вы никогда не знаете, что могут ожидать некоторые сторонние библиотеки, и изменение основных объектов в javascript изменяет их для всего.

Если вы используете Prototype, это особенно плохо, потому что прототип беспорядок с глобальной областью, а также трудно определить, собираетесь ли вы столкнуться или нет. Фактически изменение основных частей любого языка обычно является плохим даже в javascript.

(lisp может быть небольшое исключение)

Ответ 5

Вы увеличили общие типы, так сказать. Вероятно, вы перезаписали некоторые другие функции lib и почему они перестали работать.

Предположим, что какой-либо lib, который вы используете, расширяет массив с помощью функции Array.remove(). После загрузки библиотеки вы также добавите remove() в прототип Array, но со своей собственной функциональностью. Когда lib вызовет вашу функцию, он, вероятно, будет работать по-другому, как ожидалось, и нарушить его выполнение... Что происходит здесь.

Ответ 6

Использование рекурсии

function forEachWithBreak(someArray, fn){
   let breakFlag = false
   function breakFn(){
       breakFlag = true
   }
   function loop(indexIntoSomeArray){

       if(!breakFlag && indexIntoSomeArray<someArray.length){
           fn(someArray[indexIntoSomeArray],breakFn)
           loop(indexIntoSomeArray+1)   
       }
   }
   loop(0)
}

Тест 1... перерыв не называется

forEachWithBreak(["a","b","c","d","e","f","g"], function(element, breakFn){
    console.log(element)
})

Производит b c d e f g

Тест 2... break вызывается после элемента c

forEachWithBreak(["a","b","c","d","e","f","g"], function(element, breakFn){
    console.log(element)
    if(element =="c"){breakFn()}
})

Производит b c