adicionando funções personalizadas ao Array.prototype

Eu estava trabalhando em um aplicativo asp.net habilitado para AJAX. Acabei de adicionar alguns methods para Array.prototype como

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

Esta solução funcionou para mim, sendo possível reutilizar código de uma maneira ‘bonita’.

Mas quando eu testei isso trabalhando com a página inteira, eu tive problemas .. Nós tínhamos alguns extensores ajax customizados, e eles começaram a se comportar como o inesperado: alguns controles mostravam ‘indefinidos’ em torno de seu conteúdo ou valor.

Qual poderia ser a causa disso? Estou faltando alguma coisa sobre como modificar o protótipo de objects standart?

Nota: Tenho certeza de que o erro começa quando modifico o protótipo do Array. Deve ser compatível apenas com o IE.

Modificar os protótipos de objects internos pode ser uma má ideia em geral, porque sempre tem o potencial de entrar em conflito com outro código na mesma página.

No caso do protótipo do object Array, é uma idéia especialmente ruim, porque ele tem o potencial de interferir com qualquer parte do código que se repete sobre os membros de qualquer array, por exemplo com for .. in .

Para ilustrar usando um exemplo (emprestado daqui ):

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

Seria melhor para você criar seu próprio tipo de construtor de objects completo com a function doSomething, em vez de estender o Array interno.


Edit: para repetir o que eu coloquei em um comentário:

O inverso é verdadeiro – você deve evitar por … no caso de algum n00b ter modificado o protótipo do Array, e você deve evitar modificar o protótipo do Array caso algum n00b tenha usado for..in em um array. 😉

Além disso, existe agora Object.defineProperty como uma maneira geral de estender protótipos de objects sem que as novas propriedades sejam enumeráveis, embora eu ainda não usasse isso como justificativa para estender os tipos for..in , porque mesmo além de for … ainda o potencial para outros conflitos com outros scripts. Considere alguém que bifurque seu código e, em seguida, coloque as duas versões na mesma página – seu aprimoramento personalizado para o object Array ainda funcionará conforme o esperado?

Embora o potencial para colidir com outros bits de codificar a substituição de uma function em um protótipo ainda seja um risco, se você quiser fazer isso com versões modernas de JavaScript, você pode usar o método Object.defineProperty, desativando o bit enumerável, por exemplo

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

Existe uma precaução! Talvez você tenha feito isso: violino demo

Vamos dizer uma matriz e um método foo que retornam o primeiro elemento:

 var myArray = ["apple","ball","cat"]; foo(myArray) // <- 'apple' function foo(array){ return array[0] } 

O acima exposto é aceitável porque as funções são elevadas para o topo durante o tempo de interpretação.

Mas, isso não funciona: (porque o protótipo não está definido)

 myArray.foo() // <- 'undefined function foo' Array.prototype.foo = function(){ return this[0] } 

Para isso funcionar, basta definir protótipos no topo:

 Array.prototype.foo = function(){ return this[0] } myArray.foo() // <- 'apple' 

E sim! Você pode replace protótipos !!! Isso é permitido. Você pode até definir seu próprio método add para Arrays.

Em geral, bagunçar os principais objects javascript é uma má ideia. Você nunca sabe o que as bibliotecas de terceiros podem esperar e alterando os objects principais no javascript altera-os para tudo.

Se você usa o Prototype, ele é especialmente ruim porque o protótipo também mexe com o escopo global e é difícil dizer se você vai colidir ou não. Na verdade, modificar partes centrais de qualquer idioma geralmente é uma má idéia, mesmo em javascript.

(lisp pode ser a pequena exceção lá)

Você aumentou tipos genéricos, por assim dizer. Você provavelmente substituiu a funcionalidade de alguma outra biblioteca e é por isso que ela parou de funcionar.

Suponha que alguma lib que você está usando estenda o Array com a function Array.remove (). Após o carregamento da lib, você também inclui o remove () no protótipo da Array, mas com sua própria funcionalidade. Quando o lib chamar sua function, provavelmente funcionará de uma maneira diferente da esperada e quebrará sua execução … Isso é o que está acontecendo aqui.