2017-10-19 7 views
1

J'ai un cas simple: un ES6 Map, et j'ai besoin d'ajouter get() et set() personnalisé.Comment étendre correctement ES6 Carte

Mais Map est un objet intégré, donc je ne suis pas sûr s'il y aurait des avertissements à le faire. J'ai essayé de chercher s'il est correct de sous-classer un Map, et j'ai obtenu des résultats incohérents: on ne sait pas si cela est autorisé par spécification, quelles versions de browser/node.js le supportent, et quels effets secondaires sont possibles (et quoi couvrir avec des tests).

Si je comprends bien, il y a trois approches pour étendre les fonctionnalités Map:

1) sous-classer. Ce que j'ai fait, et il semble comme si cela fonctionne.

class CustomMap extends Map{ 
    get(key){ 
     super.get(key); 
    } 
    set(key, value){ 
     super.set(key, value); 
    } 
} 

Problème avec cela: de nombreux articles sur Internet affirment que vous pouvez rencontrer des problèmes avec l'extension d'objets intégrés. La plupart sont début 2016, et sont maintenant fin 2017, testant dans Chrome 61. Peut-être que maintenant c'est une façon sûre et supportée de le faire?

2) Faire un objet wrapper

const Wrapper = function(){ 
    this._map = new Map(); 
    this.get = (key) => {return this._map.get(key);} 
    this.set = (key, value) => {this._map.set(key, value);} 
    ... everything else 
} 

La solution la moins élégante, comme je l'ai besoin de mettre en œuvre non seulement get et set, mais toutes de fonctionnalités de la carte. En outre, Wrapper n'est pas une instance de Map.

3) Utilisez ES6 Proxy

const ProxyMap = function(){ 
    return new Proxy(new Map(), { 
     get(target, key){ 
      return target.get(key) 
     } 
     set(target, key, value){ 
      target.set(key, value); 
     } 
    } 
} 

Comme l'extension d'une classe, il est déconseillé d'appliquer Proxy à certains types intégrés. Mais encore une fois, beaucoup de temps s'est écoulé depuis l'introduction des spécifications Proxy; peut-être maintenant Map pourrait par proxy dans les navigateurs modernes? Donc, la question est: quelle est la façon d'étendre un Map est une manière correcte et robuste en 2017?

+0

* "Beaucoup d'articles sur Internet affirment que vous pouvez rencontrer des problèmes avec l'extension d'objets intégrés" * Vous faites peut-être allusion au fait que l'extension des objets intégrés n'est pas bien supportée dans Babel car ES5 doesn ne le supporte pas. –

+0

* quelqu'un sur internet * est aussi utile que * quelqu'un m'a dit hier de ne pas manger de chocolat *, cela ne veut pas dire que le chocolat n'est pas délicieux –

+0

Les avertissements concernant * l'extension des objets intégrés * concernent l'ajout de choses à 'Array. prototype', 'Object.prototype' etc. pour d'autres objets intégrés. – pawel

Répondre

5

On ne sait pas si elle est autorisée par la spécification

Il est. Depuis ES6, tous les types intégrés sont extensibles en utilisant la syntaxe class

On ne sait pas quel navigateur/nœud.js versions le supportent

Ils doivent prendre en charge les classes ES6 et Map nativement. L'utilisation d'un transpiler va généralement le casser.

1) Sous-classe. C'est ce que j'ai fait, et cela semble fonctionner.

Oui, c'est la bonne approche.

De nombreux articles sur Internet indiquent que vous pouvez rencontrer des problèmes avec l'extension d'objets intégrés. La plupart sont au début 2016, et est maintenant fin 2017, les tests dans Chrome 61.

Je ne sais pas, la principale référence est http://perfectionkills.com/extending-native-builtins/ à partir de 2011. Et ces articles signifiait quelque chose de différent par « étendant builtins »: la modification de leurs objets prototypes avec des objets personnalisés, p.ex. Map.prototype.getWithDefault = function(…) { … };. Ils ne se réfèrent pas à class … extends ….

Faire un objet wrapper

Cela devrait être bien aussi. Je ne pense pas que vous ayez nécessairement besoin que vos instances soient instanceof Map, si vous le faites, vous devrez suivre le Liskov substitution principle. Toutes les "extensions" d'une collection de valeurs-clés ne correspondent pas à cela.

3) Utiliser le proxy ES6 - il est déconseillé d'appliquer le proxy à certains types prédéfinis.

En effet, this doesn't work ou est au moins encombrant.

+0

_Pas toutes les "extensions" d'une collection de valeurs-clés seraient compatibles._ Que voulez-vous dire par "extensions"? Et les collections k/v sont-elles conçues comme des dictionnaires (par exemple de type 'Map {Number: Boolean}')? – ftor

+0

@ftor Oui, les dictionnaires/maps/hashes/accociative-array/whatever-you-wanna-call-them. Je pourrais imaginer des implémentations de personnalisations qui ne suivent pas le LSP. Par exemple. une multi-carte ne devrait pas être une sous-classe de la carte. – Bergi

0

Qu'en est-il de remplacer les méthodes?

m = new Map() 
m.set('x', 2) 
m.get('x') 
=> 2 
Map.prototype.get = (x) => 'lalala' 
m.get('x') 
=> lalala 
+0

* d'une manière correcte et robuste * ... Non ce n'est pas un –

2

Le premier est le chemin à parcourir. la syntaxe de classe est supportée par ES6, ainsi que Maps et l'extension de Maps fait également partie de cette définition initiale. Ainsi, chaque système qui prend en charge carte prend en charge la première méthode, et la deuxième et la troisième sont tout simplement laid (en ce qui concerne etc performance)