Getter / setter dans le constructeur

j'ai récemment lu qu'il existe une possibilité de définir les getters/setters en JavaScript. Il semble extrêmement utile - le setter est une sorte de "helper" qui peut analyser la valeur à régler d'abord, avant de l'établissement.

Par exemple, j'ai actuellement ce code:

var obj = function(value) {
    var test = !!value; // 'test' has to be a boolean
    return {
        get test() { return test },
        set test(value) { test = !!value }
    };
};

var instance = new obj(true);

Ce code convertit toujours value booléen. Donc, si vous le code instance.test = 0, puis instance.test === false.

Toutefois, pour que cela fonctionne, vous devez réellement de retour objet, ce qui signifie que la nouvelle instance n'est pas de type obj mais c'est tout simplement un objet. Cela signifie que changer le prototype de obj n'a aucun effet sur les instances. Par exemple, ce n' travail instance.func n'est pas défini:

obj.prototype.func = function() { console.log(this.value); };

parce que instance n'est pas de type obj. Pour que les fonctions prototypes fonctionnent, je suppose que je ne devrais pas retourner un objet simple, mais plutôt ne rien retourner pour que instance serait simplement de type obj, comme un constructeur de travaux.

le problème est alors de savoir comment implémenter les getters / setters? Je ne peux que trouver des articles décrivant comment les ajouter à un objet, et non comme faisant partie d'un constructeur de type personnalisé.

alors comment implémenter getters / setters dans le constructeur pour pouvoir à la fois utiliser getters/setters et étendre le prototype?

24
demandé sur pimvdb 2011-03-07 19:29:45

6 réponses

Vous ne pouvez pas le faire.

Vous pouvez définir setter/getters pour les propriétés des objets. Je vous conseille D'utiliser ES5 Object.defineProperties si. bien sûr, cela ne fonctionne que dans les navigateurs modernes.

var obj = function() {
    ...
    Object.defineProperties(this, {
        "test": {
             "get": function() { ... },
             "set": function() { ... }
        }
    });
}

obj.prototype.func = function() { ... }

var o = new obj;
o.test;
o.func();
42
répondu Raynos 2011-03-07 16:37:44

Habituellement, vous voulez classe méthodes. La réponse de @Raynos le 7 mai 2011 fait le travail, mais elle définit un exemple méthode, pas une méthode de classe.

ce qui suit illustre une définition de classe avec a le getter et le setter faisant partie de la classe. Cette définition ressemble beaucoup à la réponse de @Raynos, mais avec deux différences dans le code: (1) l'action "defineProperties()" a été retirée du constructeur. (2) L'argument de "defineProperties()"a été modifié à partir de l'instance de l'objet "ce", le prototype du constructeur de l'objet.

function TheConstructor(side) {
  this.side = side;
}

Object.defineProperties(TheConstructor.prototype, {
        area: {
             get: function()    { return this.side * this.side; }
            ,set: function(val) { this.side = Math.sqrt(val);   }
        }
});

// Test code:

var anInstance = new TheConstructor(2);
console.log("initial  Area:"+anInstance.area);
anInstance.area = 9;
console.log("modified Area:"+anInstance.area);

Qui produit ces résultats:

initial  Area:4
modified Area:9

bien que généralement la distinction entre Classe versus instance définition est juste une question de style, Il ya un but à bon style, et il ya un cas où la distinction questions: memoized getter. Le but d'un getter memoized est décrit ici: /Intelligent auto-écrasement/paresseux getters

définir le getter au niveau de la classe lorsque la valeur memoized est se rapportent à l'ensemble de la classe. Par exemple, un fichier de configuration devrait être lu qu'une seule fois; les valeurs obtenues doivent ensuite appliquer pour la durée du programme. Le code d'échantillon suivant définit un getter memoized au niveau de la classe.

function configureMe() {
  return 42;
}

Object.defineProperties(TheConstructor.prototype, {
    memoizedConfigParam: {
        get: function() {
            delete TheConstructor.prototype.memoizedConfigParam;
            return TheConstructor.prototype.memoizedConfigParam = configureMe();
        }
        ,configurable:  true
    }
});

// Test code:

console.log("memoizedConfigParam:"+anInstance.memoizedConfigParam);

Produit:

memoizedConfigParam:42

Comme on peut le voir dans l'exemple, memoized getters ont le caractéristique de la fonction get supprime lui-même, puis se remplace par une valeur simple qui (probablement) ne changera jamais. Notez que' configurable 'doit être défini à'true'.

définir le getter au niveau de l'instance lorsque la valeur memoized dépend du contenu de l'instance. La définition se déplace dans le constructeur, et l'objet de l'attention est 'ce'.

function TheConstructorI(side) {

  this.side = side;

  Object.defineProperties(this, {
    memoizedCalculation: {
        get: function() {
            delete this.memoizedCalculation;
            return this.memoizedCalculation = this.expensiveOperation();
        }
        ,configurable:  true
    }
  });
}

TheConstructorI.prototype.expensiveOperation = function() {
  return this.side * this.side * this.side;
}

//Test code:

var instance2 = new TheConstructorI(2);
var instance3 = new TheConstructorI(3);

console.log("memoizedCalculation 2:"+instance2.memoizedCalculation);
console.log("memoizedCalculation 3:"+instance3.memoizedCalculation);

Produit:

memoizedCalculation 2:8
memoizedCalculation 3:27

si vous voulez garantir (plutôt que de présumer) que le memoized la valeur ne sera jamais changé, la 'modifiable' attribut doit être changé. Qui rend le code un peu plus compliqué.

function TheConstructorJ(side) {

  this.side = side;

  Object.defineProperties(this, {
    memoizedCalculation: {
        get: function() {
            delete this.memoizedCalculation;
            Object.defineProperty( this, 'memoizedCalculation'
              ,{  value    : this.expensiveOperation()
                 ,writable : false
              });
            return this.memoizedCalculation;
        }
        ,configurable:  true
    }
  });
}

TheConstructorJ.prototype.expensiveOperation = function() {
  return this.side * this.side * this.side;
}

//Test code:

var instanceJ = new TheConstructorJ(2);

console.log("memoizedCalculation:"+instanceJ.memoizedCalculation);
instanceJ.memoizedCalculation = 42;  // results in error

Produit:

memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'

la question originale de L'OP, du 7 mars 2011, a présenté getter et setter syntaxe, a noté qu'il a fonctionné sur un objet mais pas sur 'ceci', et a demandé comment définir getters et setters dans constructeur. En plus de tous les exemples ci-dessus, il est aussi un façon de le faire" à bon marché": créer un nouvel objet à l'intérieur le constructeur, comme l'OP n'a, mais alors affecter l'objet être membre de "ceci". Donc, le code original ressemblerait à ceci:

var MyClass = function(value) {
    var test = !!value; // 'test' has to be a boolean
    this.data = {
        get test() { return test },
        set test(value) { test = !!value }
    };
};

var instance = new MyClass(true);

// But now 'data' is part of the access path
instance.data.test = 0;
console.log(instance.data.test);

Produit:

false

croyez-le ou non, j'ai en fait rencontré des situations où ce "bon plan" est la meilleure solution. Spécifiquement, j'ai utilisé ceci technique quand j'ai eu des enregistrements de plusieurs tables encapsulées une seule classe, et a voulu le présenter une vue unifiée comme si il s'agissait d'un enregistrement unique appelé "data".

amusez-vous bien.

IAM_AL_X

7
répondu IAM_AL_X 2016-11-19 22:06:13

mise à jour pour ES6 -- consultez la section 19.3.1 du livre d'Alex Rauschmayer Explorer ES6http://exploringjs.com/es6/ch_maps-sets.html#sec_weakmaps-private-data ce qui montre comment utiliser WeakMaps avec getters et setters pour conserver des données privées. Combinaison avec la section 16.2.2.3 http://exploringjs.com/es6/ch_classes.html#leanpub-auto-getters-and-setters se traduirait par quelque chose comme

# module test_WeakMap_getter.js
var _MyClassProp = new WeakMap();
class MyClass {
    get prop() {
        return _MyClassProp.get( this ); 
    }
    set prop(value) {
        _MyClassProp.set( this, value );
    }
}
var mc = new MyClass();
mc.prop = 5 ;
console.log( 'My value is', mc.prop );

$ node --use_strict test_WeakMap_getter.js 
My value is 5
5
répondu Jeff 2015-12-01 23:57:06
function Obj(value){
    this.value = !!value;
}

Obj.prototype = {
    get test () {
        return this.value;``
    },
    set test (value) {
        this.value = !!this.value;
    }
};
var obj = new Obj(true);
3
répondu Umit Silwal 2016-11-28 02:08:41

je sais que cela pourrait être extrêmement en retard, mais j'ai trouvé une autre façon d'accomplir ce que vous voulez et pour le bien des gens, comme moi, googling pour une réponse à cette voici.

function Constructor(input){
     this.input = input;
}
Object.__defineGetter__.call(Constructor.prototype, "value", function(){
    return this.input * 2;
});

var test = new Constructor(5);
alert(test.value) // 10

j'ai testé cela dans chrome, safari, Safari mobile, firefox et ils fonctionnent tous (dernières versions bien sûr)

1
répondu Akinos 2011-08-09 16:58:41

@Alex je le vois comme plus d'options et plus de puissance, la programmation est de l'art, @Nat partage sa découverte avec nous, et pour cela je le remercie. Peut-être que quelqu'un veut le faire de cette façon.

je suis sûr que la version du setter est la même, mais il suffit de changer ce g en un S.

j'.g:

function Constructor(input){
     this.input = input;
}

Object.__defineGetter__.call(Constructor.prototype, "value", function(){
    return this.input * 2;
});

Object.__defineSetter__.call(Constructor.prototype, "bar", function(foo){
    return this.input *= foo;
});

var test = new Constructor(5);
console.log(test.value); // 10
test.bar = 5;
console.log(test.input); //25

cela dit, Cette caractéristique est dépréciée, Conseils à ne pas utiliser dans le codage de production.

1
répondu user3552042 2014-06-11 02:15:21