Je sais comment créer des getters et des setters pour des propriétés dont on connaît déjà les noms, en faisant quelque chose comme ceci:
// A trivial example:
function MyObject(val){
this.count = 0;
this.value = val;
}
MyObject.prototype = {
get value(){
return this.count < 2 ? "Go away" : this._value;
},
set value(val){
this._value = val + (++this.count);
}
};
var a = new MyObject('foo');
alert(a.value); // --> "Go away"
a.value = 'bar';
alert(a.value); // --> "bar2"
Maintenant, ma question est la suivante: est-il possible de définir une sorte de getters et de setters fourre-tout comme ceux-ci? Par exemple, créez des getters et des setters pour tout nom de propriété qui n'est pas déjà défini.
Le concept est possible en PHP en utilisant les méthodes __get()
et __set()
magic (voir la documentation PHP pour plus d'informations à ce sujet), donc je me demande vraiment s'il existe un JavaScript équivalent à ceux-ci?
Inutile de dire que j'aimerais idéalement une solution compatible avec tous les navigateurs.
javascript
metaprogramming
getter-setter
marguerite
la source
la source
Réponses:
Mise à jour 2013 et 2015 (voir ci-dessous la réponse originale de 2011) :
Cela a changé depuis la spécification ES2015 (alias "ES6"): JavaScript a maintenant des proxies . Les proxies vous permettent de créer des objets qui sont de véritables proxies pour (façades sur) d'autres objets. Voici un exemple simple qui transforme toutes les valeurs de propriété qui sont des chaînes en majuscules lors de la récupération:
Les opérations que vous ne remplacez pas ont leur comportement par défaut. Dans ce qui précède, tout ce que nous remplaçons est
get
, mais il y a toute une liste d'opérations auxquelles vous pouvez vous connecter.Dans la
get
liste des arguments de la fonction de gestionnaire:target
est l'objet mandaté (original
dans notre cas).name
est (bien sûr) le nom de la propriété en cours de récupération, qui est généralement une chaîne mais peut également être un symbole.receiver
est l'objet qui doit être utilisé commethis
dans la fonction getter si la propriété est un accesseur plutôt qu'une propriété de données. Dans le cas normal, c'est le proxy ou quelque chose qui en hérite, mais cela peut être n'importe quoi puisque le trap peut être déclenché parReflect.get
.Cela vous permet de créer un objet avec la fonction catch-all getter et setter que vous voulez:
Le résultat de ce qui précède est:
Notez comment nous obtenons le message "inexistant" lorsque nous essayons de le récupérer
foo
alors qu'il n'existe pas encore, et à nouveau lorsque nous le créons, mais pas après cela.Réponse de 2011 (voir ci-dessus pour les mises à jour 2013 et 2015) :
Non, JavaScript n'a pas de fonction de propriété fourre-tout. La syntaxe d'accesseur que vous utilisez est traitée dans la section 11.1.5 de la spécification, et n'offre aucun caractère générique ou quelque chose comme ça.
Vous pouvez, bien sûr, implémenter une fonction pour le faire, mais je suppose que vous ne voulez probablement pas utiliser
f = obj.prop("foo");
plutôt quef = obj.foo;
etobj.prop("foo", value);
plutôt queobj.foo = value;
(ce qui serait nécessaire pour que la fonction gère des propriétés inconnues).FWIW, la fonction getter (je ne me suis pas soucié de la logique du setter) ressemblerait à ceci:
Mais encore une fois, je ne peux pas imaginer que vous souhaitiez vraiment faire cela, à cause de la façon dont cela change la façon dont vous utilisez l'objet.
la source
Proxy
:Object.defineProperty()
. J'ai mis les détails dans ma nouvelle réponse .Ce qui suit pourrait être une approche originale de ce problème:
Pour l'utiliser, les propriétés doivent être passées sous forme de chaînes. Voici donc un exemple de son fonctionnement:
Edit: Une approche améliorée, plus orientée objet basée sur ce que j'ai proposé est la suivante:
Vous pouvez le voir fonctionner ici .
la source
Préface:
La réponse de TJ Crowder mentionne a
Proxy
, qui sera nécessaire pour un getter / setter fourre-tout pour les propriétés qui n'existent pas, comme l'OP le demandait. En fonction du comportement réellement recherché avec les getters / setters dynamiques, aProxy
peut ne pas être nécessaire cependant; ou, potentiellement, vous voudrez peut-être utiliser une combinaison de aProxy
avec ce que je vais vous montrer ci-dessous.(PS J'ai
Proxy
récemment expérimenté à fond dans Firefox sur Linux et je l'ai trouvé très performant, mais aussi quelque peu déroutant / difficile à utiliser et à faire correctement. Plus important encore, je l'ai également trouvé assez lent (du moins en rapport à la façon dont JavaScript a tendance à être optimisé de nos jours) - je parle dans le domaine des déca-multiples plus lents.)Pour implémenter spécifiquement des getters et des setters créés dynamiquement, vous pouvez utiliser
Object.defineProperty()
ouObject.defineProperties()
. C'est également assez rapide.L'essentiel est que vous pouvez définir un getter et / ou un setter sur un objet comme ceci:
Plusieurs choses à noter ici:
value
propriété dans le descripteur de propriété ( non illustré ci-dessus) simultanément avecget
et / ouset
; à partir de la documentation:val
propriété en dehors duObject.defineProperty()
descripteur d'appel / de propriété. C'est un comportement standard.writable
surtrue
dans le descripteur de propriété si vous utilisezget
ouset
.configurable
etenumerable
, cependant, en fonction de ce que vous recherchez; à partir de la documentation:Sur cette note, ceux-ci peuvent également être intéressants:
Object.getOwnPropertyNames(obj)
: récupère toutes les propriétés d'un objet, même celles qui ne sont pas énumérables (AFAIK c'est la seule façon de le faire!).Object.getOwnPropertyDescriptor(obj, prop)
: obtient le descripteur de propriété d'un objet, l'objet qui a été passéObject.defineProperty()
ci-dessus.obj.propertyIsEnumerable(prop);
: pour une propriété individuelle sur une instance d'objet spécifique, appelez cette fonction sur l'instance d'objet pour déterminer si la propriété spécifique est énumérable ou non.la source
__get
et__set
.defineProperty
ne gère pas ce cas. De la question: "Ie, créez des getters et des setters pour tout nom de propriété qui n'est pas déjà défini." (leur emphase).defineProperty
définit les propriétés à l'avance. La seule façon de faire ce que l'OP a demandé est un proxy.obj.whateverProperty
sorte que la bibliothèque puisse intercepter cela avec un getter générique et recevoir le nom de propriété l'utilisateur a essayé d'accéder. D'où l'exigence de «getters et setters fourre-tout».cela fonctionne pour moi
la source
Function()
comme ça, c'est comme utilisereval
. Il suffit de mettre directement les fonctions comme paramètres dedefineProperty
. Ou, si pour une raison quelconque vous insistez pour créer dynamiquementget
etset
, alors utilisez une fonction d'ordre élevé qui crée la fonction et la renvoie, commevar get = (function(propName) { return function() { return this[propName]; };})('value');