web-dev-qa-db-fra.com

JavaScript: Quels sont les dangers liés à l'extension de Array.prototype?

Google JavaScript Style Guide déconseille l'extension du Array.prototype . Cependant, j'ai utilisé Array.prototype.filter = Array.prototype.filter || function(...) {...} comme moyen de l'avoir (et des méthodes similaires) dans les navigateurs où ils n'existent pas. MDN fournit en fait exemple similaire .

Je suis au courant des problèmes de Object.prototype, Mais Array n'est pas une table de hachage.

Quels problèmes peuvent survenir lors de l'extension de Array.prototype Qui ont déconseillé Google de le faire?

56
Andrey Shchekin

La plupart des gens ont raté le point sur celui-ci. Fonctionnalité standard de remplissage ou de calage comme Array.prototype.filter pour que cela fonctionne dans les anciens navigateurs est une bonne idée à mon avis. N'écoutez pas les ennemis. Mozilla vous montre même comment faire cela sur le MDN. Habituellement, les conseils pour ne pas étendre Array.prototype ou d'autres prototypes natifs peuvent se résumer à l'un d'eux:

  1. for..in peut ne pas fonctionner correctement
  2. Quelqu'un d'autre peut également vouloir étendre Array avec le même nom de fonction
  3. Cela pourrait ne pas fonctionner correctement dans tous les navigateurs, même avec la cale.

Voici mes réponses:

  1. Vous n'avez pas besoin d'utiliser for..in sur Array est généralement. Si vous le faites, vous pouvez utiliser hasOwnProperty pour vous assurer qu'il est légitime.
  2. N'étendez les natifs que lorsque vous savez que vous êtes le seul à le faire [~ # ~] ou [~ # ~] lorsque ce sont des choses standard comme Array.prototype.filter.
  3. C'est ennuyeux et m'a mordu. Old IE a parfois des problèmes avec l'ajout de ce type de fonctionnalité. Vous n'aurez qu'à voir si cela fonctionne au cas par cas. Pour moi, le problème que j'ai eu était d'ajouter Object.keys à IE7. Il semblait cesser de fonctionner dans certaines circonstances. Votre kilométrage peut varier.

Découvrez ces références:

Bonne chance!

78
Jamund Ferguson

Je vais vous donner les puces, avec des phrases clés, de l'excellent article de Nicholas Zakas JavaScript maintenable: ne modifiez pas les objets que vous ne possédez pas :

  • Fiabilité: "L'explication simple est qu'un produit logiciel d'entreprise a besoin d'un environnement d'exécution cohérent et fiable pour être maintenable."
  • Implémentations incompatibles: "Un autre risque de modifier des objets que vous ne possédez pas est la possibilité de nommer les collisions et les implémentations incompatibles."
  • Et si tout le monde le faisait?: "Si tout le monde dans votre équipe modifiait des objets qui ne lui appartenaient pas, vous vous heurteriez rapidement à des collisions de noms, à des implémentations incompatibles et à des cauchemars de maintenance. "

Fondamentalement, ne le faites pas. Même si votre projet ne sera jamais utilisé par quelqu'un d'autre et que vous n'importerez jamais de code tiers, ne le faites pas. Vous établirez une horrible habitude qui pourrait être difficile à briser lorsque vous commencez à jouer à Nice avec les autres.

9
James Sumners

En tant que mise à jour moderne de la réponse de Jamund Ferguson:

Habituellement, les conseils pour ne pas étendre Array.prototype ou d'autres prototypes natifs peuvent se résumer à l'un de ces éléments:

  1. for..in pourrait ne pas fonctionner correctement
  2. Quelqu'un d'autre peut également vouloir étendre Array avec le même nom de fonction
  3. Cela pourrait ne pas fonctionner correctement dans tous les navigateurs, même avec la cale.

Les points 1. et 2. peuvent maintenant être atténués dans ES6 en utilisant un Symbol pour ajouter votre méthode.

Cela crée une structure d'appel légèrement plus maladroite, mais ajoute une propriété qui n'est pas répétée et qui ne peut pas être facilement dupliquée.

// 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(){ /* ... */ };

Avantages:

  • For..in fonctionne comme prévu, les symboles ne sont pas répétés.
  • Aucun conflit de noms de méthode car les symboles sont locaux à la portée et nécessitent des efforts pour les récupérer.

Les inconvénients:

5
thelastshadow

Étendre Array.prototype Dans votre propre code d'application est sûr (sauf si vous utilisez for .. in Sur des tableaux, auquel cas vous devez payer pour cela et vous amuser à les refactoriser).

Il n'est pas cool d'étendre des objets Host natifs dans des bibliothèques que vous avez l'intention d'utiliser par d'autres. Vous n'avez pas le droit de corrompre l'environnement d'autres personnes dans votre propre bibliothèque.

Faites cela derrière une méthode facultative comme lib.extendNatives() ou ayez [].filter Comme exigence.

Extension des objets natifs et hôtes

3
Raynos

Prototype fait cela. C'est mal. L'extrait de code suivant montre comment cela peut produire des résultats inattendus:

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

Le résultat:

not only four elements function each(iterator, context) { var index = 0; . . .

et environ 5000 caractères de plus.

2
wadim

Certaines personnes utilisent for ... in boucles pour parcourir les tableaux. Si vous ajoutez une méthode au prototype, la boucle tentera également d'itérer sur la clé that. Bien sûr, vous ne devriez pas l'utiliser pour cela, mais certaines personnes le font quand même.

2
Tikhon Jelvis

Vous pouvez facilement créer une sorte de sandbox avec la bibliothèque poser.

Jetez un oeil sur 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))
1
test30

L'extension du prototype est une astuce qui ne fonctionne qu'une seule fois. Vous faites et vous utilisez une bibliothèque qui le fait aussi (de manière incompatible) et boom!

0
Malvolio

La fonction que vous remplacez pourrait être utilisée par les appels javascript internes et cela pourrait conduire à des résultats inattendus. C'est l'une des raisons de la directive

Par exemple, j'ai outrepassé la fonction indexOf du tableau et il a gâché l'accès au tableau à l'aide de [].

0
Schu

Je crois que cette question mérite une réponse mise à jour ES6 .

ES5

Tout d'abord, comme beaucoup de personnes l'ont déjà dit. L'extension des prototypes natifs pour caler ou remplir de nouvelles normes ou corriger des bogues est une pratique standard et n'est pas nuisible. Par exemple, si un navigateur ne prend pas en charge la méthode .filter if (!Array.prototype.filter), vous êtes libre d'ajouter cette fonctionnalité par vous-même. En fait, le langage est conçu pour faire exactement cela pour gérer la compatibilité descendante.

Maintenant, vous pardonneriez de penser que puisque l'objet JavaScript utilise l'héritage prototypique, étendre un objet natif comme Array.prototype Sans interférer devrait être facile, mais jusqu'à ES6 ce n'était pas faisable.

Contrairement aux objets par exemple, vous avez dû compter et modifier le Array.prototype Pour ajouter vos propres méthodes personnalisées. Comme d'autres l'ont souligné, c'est mauvais car il pollue l'espace de noms Global, peut interférer avec d'autres codes de manière inattendue, a des problèmes de sécurité potentiels, est un péché cardinal, etc.

Dans ES5, vous pouvez essayer de pirater cela, mais les implémentations ne sont pas vraiment utiles. Pour des informations plus détaillées, je vous recommande de consulter ce post très informatif: http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/

Vous pouvez ajouter une méthode à un tableau, ou même un constructeur de tableau, mais vous rencontrez des problèmes en essayant de travailler avec les méthodes de tableau natives qui reposent sur la propriété length. Pire encore, ces méthodes vont retourner un Array.prototype Natif et non votre nouveau tableau de sous-classes brillant, c'est-à-dire: subClassArray.slice(0) instanceof subClassArray === false.

ES6

Cependant, maintenant avec ES6, vous pouvez sous-classer les buildins en utilisant class combiné avec extends Array Qui surmonte tous ces problèmes. Il laisse le Array.prototype Intact, crée une nouvelle sous-classe et les méthodes de tableau dont il hérite seront de la même sous-classe! https://hacks.mozilla.org/2015/08/es6-in-depth-subclassing/

Voir le violon ci-dessous pour une démonstration: https://jsfiddle.net/dmq8o0q4/1/

0
CervEd