standard - javascript coding style guide
JavaScript: ¿Qué peligros hay en extender Array.prototype? (10)
Algunas personas usan for ... in
bucles para iterar a través de matrices. Si agrega un método al prototipo, el ciclo también intentará iterar sobre esa tecla. Por supuesto, no debes usarlo para esto, pero algunas personas lo hacen de todos modos.
La Guía de estilo de JavaScript de Google recomienda no extender el Array.prototype
. Sin embargo, utilicé Array.prototype.filter = Array.prototype.filter || function(...) {...}
Array.prototype.filter = Array.prototype.filter || function(...) {...}
como una forma de tenerlo (y métodos similares) en navegadores donde no existen. MDN realmente proporciona un ejemplo similar .
Soy consciente de los problemas de Object.prototype
, pero Array
no es una tabla hash.
¿Qué problemas pueden surgir al extender Array.prototype
que hizo que Google desaconseje?
Como una actualización moderna de la respuesta de Jamund Ferguson:
Por lo general, los consejos para no extender Array.prototype u otros prototipos nativos pueden reducirse a uno de estos:
- para ... podría no funcionar correctamente
- Alguien más también podría querer extender Array con el mismo nombre de función
- Es posible que no funcione correctamente en todos los navegadores, incluso con la cuña.
Los puntos 1. y 2. ahora pueden mitigarse en ES6 utilizando un Symbol para agregar su método.
Hace una estructura de llamada un poco más torpe, pero agrega una propiedad que no se itera y no se puede duplicar fácilmente.
// 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(){ /* ... */ };
Pros:
- Para ... funciona como se espera, los símbolos no se repiten.
- No hay conflicto de nombres de métodos, ya que los símbolos son locales para el alcance y requieren un esfuerzo para recuperarlos.
Contras:
- Solo funciona en ambientes modernos
- Sintaxis un poco torpe
Creo que esta pregunta merece una respuesta ES6 actualizada.
ES5
En primer lugar, como mucha gente ya ha declarado. Extender los prototipos nativos para ajustar o rellenar nuevos estándares o corregir errores es una práctica estándar y no dañina. Por ejemplo, si un navegador no es compatible con el método .filter, if (!Array.prototype.filter)
puede agregar esta funcionalidad usted mismo. De hecho, el lenguaje está diseñado para hacer exactamente esto para administrar la compatibilidad con versiones anteriores.
Ahora, estarías perdiendo la esperanza de pensar que, dado que el objeto JavaScript usa herencia prototípica, extender un objeto nativo como Array.prototype
sin interferir debería ser fácil, pero hasta ES6 no ha sido posible.
A diferencia de los objetos, por ejemplo, debe confiar y modificar el Array.prototype
para agregar sus propios métodos personalizados. Como otros han señalado, esto es malo porque contamina el espacio de nombres global, puede interferir con otro código de una manera inesperada, tiene posibles problemas de seguridad, es un pecado capital, etc.
En ES5 puedes intentar hackear esto, pero las implementaciones no son realmente útiles en la práctica. Para obtener información más detallada, le recomiendo que consulte esta publicación muy informativa: http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
Puede agregar un método a una matriz, o incluso un constructor de matriz, pero se encuentra con problemas al tratar de trabajar con los métodos de matriz nativa que se basan en la propiedad de longitud. Lo peor de todo es que estos métodos devolverán un Array.prototype
nativo y no su nueva matriz de subclase brillante, es decir: subClassArray.slice(0) instanceof subClassArray === false
.
ES6
Sin embargo, ahora con ES6 puede subclasificar los builtins usando la class
combinada con extends Array
que soluciona todos estos problemas. Deja intacto el Array.prototype
, crea una nueva subclase y los métodos de matriz que hereda serán de la misma subclase. https://hacks.mozilla.org/2015/08/es6-in-depth-subclassing/
Vea el violín a continuación para una demostración: https://jsfiddle.net/dmq8o0q4/1/
Extender Array.prototype
en su propio código de aplicación es seguro (a menos que lo use for .. in
matrices, en cuyo caso debe pagar por eso y divertirse refactándolas).
No es genial extender los objetos host nativos en las bibliotecas que pretendas que otros usen. No tiene derecho a corromper el entorno de otras personas en su propia biblioteca.
Haga esto detrás de un método opcional como lib.extendNatives()
o tenga [].filter
como requisito.
Extender el prototipo es un truco que solo funciona una vez. Lo haces y usas una biblioteca que también lo hace (de una manera incompatible) y ¡ boom !
La función que está anulando podría ser utilizada por las llamadas internas de JavaScript y eso podría generar resultados inesperados. Esa es una de las razones de la directriz
Por ejemplo, superé la función indexOf de la matriz y dañó el acceso a la matriz usando [].
La mayoría de las personas se perdió el punto en este caso. Rellenar o ajustar la funcionalidad estándar como Array.prototype.filter
para que funcione en navegadores antiguos es una buena idea, en mi opinión. No escuches a los enemigos. Mozilla incluso te muestra cómo hacer esto en el MDN. Por lo general, los consejos para no extender Array.prototype
u otros prototipos nativos pueden reducirse a uno de estos:
-
for..in
podría no funcionar correctamente - Alguien más también podría querer extender Array con el mismo nombre de función
- Es posible que no funcione correctamente en todos los navegadores, incluso con la cuña.
Aquí están mis respuestas:
- No es necesario usar
for..in
en Array''s general. Si lo haces, puedes usarhasOwnProperty
para asegurarte de que sea legítimo. - Extiende solo nativos cuando sepas que eres el único que lo está haciendo O cuando se trata de material estándar como
Array.prototype.filter
. - Esto es molesto y me ha mordido. El viejo IE a veces tiene problemas para agregar este tipo de funcionalidad. Deberá ver si funciona caso por caso. Para mí, el problema que tenía era agregar
Object.keys
a IE7. Parecía dejar de funcionar en ciertas circunstancias. Su experiencia puede ser diferente.
Mira estas referencias:
- http://perfectionkills.com/extending-native-builtins/
- http://blip.tv/jsconf/jsconf2011-andrew-dupont-everything-is-permitted-extending-built-ins-5211542
- https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter
- https://github.com/kriskowal/es5-shim
¡Buena suerte!
Le daré las viñetas, con oraciones clave, del excelente artículo de Nicholas Zakas. JavaScript conservable: no modifique objetos que no le pertenecen:
- Confiabilidad : "La explicación simple es que un producto de software empresarial necesita un entorno de ejecución coherente y confiable para mantenerlo".
- Implementaciones incompatibles : "Otro peligro de modificar objetos que no posee es la posibilidad de nombrar colisiones e implementaciones incompatibles".
- ¿Qué pasa si todos lo hicieran? : "En pocas palabras: si todos en su equipo modificaran objetos que no poseían, rápidamente encontraríamos colisiones de nombres, implementaciones incompatibles y pesadillas de mantenimiento".
Básicamente, no lo hagas. Incluso si su proyecto nunca va a ser utilizado por otra persona, y nunca va a importar código de terceros, no lo haga. Establecerá un hábito horrible que podría ser difícil de romper cuando empiece a tratar de jugar bien con los demás.
Puedes crear fácilmente una especie de sandbox con la biblioteca poser
.
Eche un vistazo en 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))
Prototype hace esto. Es malvado El siguiente fragmento muestra cómo hacerlo puede producir resultados inesperados:
<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>
El resultado:
not only four elements function each(iterator, context) { var index = 0; . . .
y alrededor de 5000 caracteres más.