добавление пользовательских функций в Array.prototype - PullRequest
45 голосов
/ 04 июня 2009

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

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

Это решение сработало для меня, так как возможно повторное использование кода «довольно».

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

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

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

Ответы [ 6 ]

44 голосов
/ 04 июня 2009

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

В случае прототипа объекта 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. Определите свои собственные типы с вашими собственными конструкторами.

36 голосов
/ 20 февраля 2016

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

// functional sort
Object.defineProperty(Array.prototype, 'sortf', {
    value: function(compare) { return [].concat(this).sort(compare); }
});
10 голосов
/ 16 апреля 2015

Осторожно! Может быть, вы сделали это: fiddle demo

Скажем, массив и метод 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 для массивов.

1 голос
/ 04 июня 2009

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

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

1 голос
/ 04 июня 2009

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

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

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

0 голосов
/ 12 мая 2019

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

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)
})

Производит б с d е е г

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

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

Производит б с

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...