164 votes

Est-il possible de mettre en œuvre dynamique des getters/setters en JavaScript?

Je suis conscient de la façon de créer des getters et setters pour les propriétés dont les noms que l'on sait déjà, 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, est-il possible de définir une sorte de fourre-tout des getters et setters comme celle-ci? I. e., créer des getters et setters pour tout nom de la propriété qui n'est pas déjà défini.

Le concept est possible en PHP à l'aide de l' __get() et __set() méthodes magiques (voir la documentation PHP pour plus d'informations sur ces), donc je suis vraiment se poser est-il un JavaScript équivalent à ceux-ci?

Inutile de dire que j'avais idéalement comme une solution qui est compatible avec tous les navigateurs.

256voto

T.J. Crowder Points 285826

Réponse à partir de 2011 (voir ci-dessous pour un 2013 mise à jour):

Non, JavaScript n'est pas un fourre-tout bien caractéristique. L'accesseur de la syntaxe que vous utilisez couverts dans l'Article 11.1.5 de la spécification, et n'offre pas un caractère générique ou quelque chose comme ça.

Vous pouvez, bien sûr, de mettre en œuvre une fonction pour le faire, mais je devine que vous ne voulez probablement pas à utiliser f = obj.prop("foo"); plutôt que d' f = obj.foo; et obj.prop("foo", value); plutôt que d' obj.foo = value; (ce qui serait nécessaire pour la fonction pour gérer les propriétés inconnues).

FWIW, la fonction get (je n'ai pas pris la peine avec le setter de la logique) ressemblerait à quelque chose comme ceci:

MyObject.prototype.prop = function(propName) {
    if (propName in this) {
        // This object or its prototype already has this property,
        // return the existing value.
        return this[propName];
    }

    // ...Catch-all, deal with undefined property here...
};

Mais encore une fois, je ne peux pas imaginer que vous auriez vraiment envie de faire cela, car de la façon dont il change la façon dont vous utilisez l'objet.


Réponse en 2013:

Cela va bientôt changer (et il a déjà de la mise à jour pour les utilisateurs de Firefox): ECMAScript 6e édition aura des procurations. Ils sont définis dans le projet de spécification, et aussi sur cette page (mais la spec brouillons ont priorité).

Les procurations vous permettent de créer des objets qui sont de véritables mandataires (façades) pour d'autres objets. Voici un exemple simple qui transforme toutes les valeurs de propriété qui sont des chaînes de caractères pour tous les bouchons sur la récupération:

var original = {"foo": "bar"};
var proxy = new Proxy(original, {
    get: function(target, name, receiver) {
        var rv = target[name];
        if (typeof rv === "string") {
            rv = rv.toUpperCase();
        }
        return rv;
    }
});

console.log("original.foo = " + original.foo); // "bar"
console.log("proxy.foo = " + proxy.foo);       // "BAR"

Exemple Vivant | Source

Les opérations que vous ne la supplante pas avoir leur comportement par défaut. Ci-dessus, tous nous remplacer est - get, mais il y a toute une liste d'opérations que vous pouvez brancher dans.

Dans l' get fonction de gestionnaire d'arguments de la liste:

  • target est l'objet proxy (original, dans notre cas).
  • name est (évidemment) le nom de la propriété en cours de récupération.
  • receiver est soit le proxy lui-même ou quelque chose qui en hérite. Dans notre cas, receiver est === proxy, mais si l' proxy ont été utilisés comme un prototype, receiver pourrait être un descendant de l'objet, d'où qu'il soit sur la signature de la fonction (mais à la fin, de sorte que vous pouvez facilement laisser de côté si, comme notre exemple ci-dessus, vous n'en utilisent pas).

Cela vous permet de créer un objet avec le fourre-tout getter et le setter de la fonction que vous voulez:

var obj = new Proxy({}, {
    get: function(target, name) {
        if (!(name in target)) {
            console.log("Getting non-existant property '" + name + "'");
            return undefined;
        }
        return target[name];
    },
    set: function(target, name, value) {
        if (!(name in target)) {
            console.log("Setting non-existant property '" + name + "', initial value: " + value);
        }
        target[name] = value;
    }
});

console.log("[before] obj.foo = " + obj.foo);
obj.foo = "bar";
console.log("[after] obj.foo = " + obj.foo);

Exemple vivant | Source (Notez que j'ai quitté receiver off les fonctions, car nous ne l'utilisons pas. receiver est une option du quatrième arg sur set.)

La sortie de la ci-dessus est:

L'obtention de la non-existant de la propriété 'foo'
[avant] obj.foo = undefined
Paramètre inexistant propriété 'foo', la valeur initiale: bar
[après] obj.foo = bar

Notez comment nous obtenons le "non-existant" message lorsque nous essayons de récupérer foo lorsqu'il n'existe pas encore, et encore, quand nous le créer, mais pas par la suite.

47voto

David Ellis Points 4278

Moderne Javascript (FF4+, IE9+, Chrome 5+, Safari 5.1+, Opera 11.60+), il y a Object.defineProperty. Cet exemple sur le MDN explique très bien comment defineProperty fonctionne et rend la dynamique des getters et setters possible.

Techniquement, cela ne fonctionne pas sur toute requête dynamique comme vous le cherchez, mais si la validité de votre getters et les setters sont définies par, disons, un appel AJAX vers un JSON-RPC server, par exemple, vous pourriez utiliser ceci de la façon suivante:

arrayOfNewProperties.forEach(function(property) {
    Object.defineProperty(myObject, property.name, {
        set: property.setter, get: property.getter
    });
});

5voto

clami219 Points 1133

La suite pourrait être une approche originale à ce problème:

var obj = {
  emptyValue: null,
  get: function(prop){
    if(typeof this[prop] == "undefined")
        return this.emptyValue;
    else
        return this[prop];
  },
  set: function(prop,value){
    this[prop] = value;
  }
}

0voto

Genia S. Points 12190

Pas à 100% ce que vous demandez, mais assez proche...

function Obj(){
   var innerProp = new Array; // note it's not "this" so it's private

   this.setInnerProp = function(key,val){
      innerProp[key] = val;
   }

   this.getInnerProp = function(key){
     return innerProp[key]
   }
 }

Vous pouvez évidemment adapter ce prototype sur n'importe quel objet que vous avez déjà.

-6voto

Bruno Points 1
var x={}
var propName = 'value' 
var get = Function("return this['" + propName + "']")
var set = Function("newValue", "this['" + propName + "'] = newValue")
var handler = { 'get': get, 'set': set, enumerable: true, configurable: true }
Object.defineProperty(x, propName, handler)

cela fonctionne pour moi

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X