Добавление пользовательских функций в 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