How to define setter/getter on prototype
Asked Answered
B

6

91

EDIT Oct 2016: Please note this question was asked in 2012. Every month or so someone adds a new answer or comment that refutes an answer, but doesn't really make sense to do so as the question is probably out of date (remember, it was for Gnome Javascript to write gnome-shell extensions, not browser stuff, which is quite specific).

Following my previous question on how to do subclassing in Javascript, I'm making a subclass of a superclass like so:

function inherits(Child,Parent) {
    var Tmp = function {};
    Tmp.prototype = Parent.prototype;
    Child.prototype = new Tmp();
    Child.prototype.constructor = Child;
}
/* Define subclass */
function Subclass() {
    Superclass.apply(this,arguments);
    /* other initialisation */
}
/* Set up inheritance */
inherits(Subclass,Superclass);
/* Add other methods */
Subclass.prototype.method1 = function ... // and so on.

My question is, how do I define a setter/getter on the prototype with this syntax?

I used to do:

Subclass.prototype = {
    __proto__: Superclass.prototype,
    /* other methods here ... */

    get myProperty() {
        // code.
    }
}

But obviously the following won't work:

Subclass.prototype.get myProperty() { /* code */ }

I'm using GJS (GNOME Javascript), and the engine is meant to be the more-or-less same as the Mozilla Spidermonkey one. My code is not intended for a browser so as long as it's supported by GJS (I guess that means Spidermonkey?), I don't mind if it's not cross-compatible.

Barry answered 15/5, 2012 at 0:29 Comment(5)
Mozilla docs mention __defineGetter__ and __defineSetter (but I never actually used those...). developer.mozilla.org/en/Core_JavaScript_1.5_Guide/…Mydriasis
Fantastic, that looks like what I'm after. If you post it as an answer I'll accept it. cheers! :)Barry
Done that, and added examples from MDN.Mydriasis
Please don't do this. Inheritance in JS is three lines: call the super class, set the prototype to the superclass and reset the constructor back to the child class. The End. Writing methods like this is a complete waste of time.Palmitate
linked: Getters \ setters for dummiesDrowse
M
85

Using an object literal declaration (simplest way):

var o = {
    a: 7,
    get b() {
        return this.a + 1;
    },
    set c(x) {
        this.a = x / 2
    }
};

Using Object.defineProperty (on modern browsers that support ES5):

Object.defineProperty(o, "myProperty", {
    get: function myProperty() {
        // code
    }
});

Or using __defineGetter__ and __defineSetter__ (DEPRECATED):

var d = Date.prototype;
d.__defineGetter__("year", function() { return this.getFullYear(); });
d.__defineSetter__("year", function(y) { this.setFullYear(y); });
Mydriasis answered 15/5, 2012 at 0:41 Comment(4)
Sorry, my mistake. I misread that for the new function notation introduced in es6: let foo = { bar () { return baz }}Drogue
@Drogue I see, the ES5 get/ set syntax seems to have inspired the new method syntax :)Mydriasis
I don't think that it is needed to write a function name for the getter again. The MDN docu examples use anonymous functions.Semicentennial
Also be carefull with lambdas in getters/setters. this in lambdas refers to global object.Artiste
A
121

Use Object.defineProperty() on Subclass.prototype. There are also __defineGetter__ and __defineSetter__ available on some browsers, but they are deprecated. For your example, it would be:

Object.defineProperty(Subclass.prototype, "myProperty", {
    get: function myProperty() {
        // code
    }
});
Autosome answered 15/5, 2012 at 0:39 Comment(8)
"they are deprecated (just as __proto__)" - note that __proto__ is being standardized as of ES6 draft.Ointment
@FabrícioMatté: I know (but I don't like it). However, the point here was to use Object.create instead.Autosome
Yes, I was just adding a note that it will not be considered deprecated once ES6 becomes a standard (even the leak for IE11 apparently has it implemented already). Though of course, there's a long time until we can think about using it in a real-world environment, for the few use cases that it may have.Ointment
Duh! "How do I define a setter/getter [property] on the prototype?" "You define it as a property on the prototype."Peccant
Yes, you can define it directly on the protoype, but you won't be able to operate on other class members when just using a flat property. For example, if you define a property for "begin" and another for "end", and want to tally the "duration" when you set begin or end -- well the only way to do that is to have a proper getter/setter situation.Bayadere
@Bayadere Not sure what your comment is supposed to mean. Sure, if you want to get the duration dynamically you will need a getter, but it can operate on other properties just as all other methods can: Object.defineProperty(…, "duration", { get: function() { return this.end - this.begin; }});Autosome
Can anyone clarify why the function is named? Won't get: function() {...} suffice?Indohittite
@Indohittite Yes, that would totally suffice. (Named functions have their advantages, though I have no idea how current debuggers do about getters). I guess I simply didn't care to remove it when editing the original code.Autosome
M
85

Using an object literal declaration (simplest way):

var o = {
    a: 7,
    get b() {
        return this.a + 1;
    },
    set c(x) {
        this.a = x / 2
    }
};

Using Object.defineProperty (on modern browsers that support ES5):

Object.defineProperty(o, "myProperty", {
    get: function myProperty() {
        // code
    }
});

Or using __defineGetter__ and __defineSetter__ (DEPRECATED):

var d = Date.prototype;
d.__defineGetter__("year", function() { return this.getFullYear(); });
d.__defineSetter__("year", function(y) { this.setFullYear(y); });
Mydriasis answered 15/5, 2012 at 0:41 Comment(4)
Sorry, my mistake. I misread that for the new function notation introduced in es6: let foo = { bar () { return baz }}Drogue
@Drogue I see, the ES5 get/ set syntax seems to have inspired the new method syntax :)Mydriasis
I don't think that it is needed to write a function name for the getter again. The MDN docu examples use anonymous functions.Semicentennial
Also be carefull with lambdas in getters/setters. this in lambdas refers to global object.Artiste
M
44

I think you wanted to do this way:

function Unit() {
   	this._data; // just temp value
}
Unit.prototype = {
 	get accreation() {
   		return this._data;
   	},
   	set accreation(value) {
   		this._data = value
   	},
}
Unit.prototype.edit = function(data) {
   	this.accreation = data; // setting
   	this.out();
};

Unit.prototype.out = function() {
    alert(this.accreation); // getting
};

var unit = new Unit();
unit.edit('setting and getting');

function Field() {
    // children
}

Field.prototype = Object.create(Unit.prototype);

Field.prototype.add = function(data) {
  this.accreation = data; // setting
   	this.out();
}

var field1 = new Field();
field1.add('new value for getter&setter');

var field2 = new Field();
field2.out();// because field2 object has no setting
Maurer answered 21/2, 2016 at 22:51 Comment(6)
Amazing. The only correct answer gets no votes. There is ZERO reason to use Object.defineProperty unless you have to suffer the insult of old IE.Palmitate
@Palmitate You're ignoring that this answer was posted almost 4 years after the question was posed.Indohittite
@Indohittite Actually, to correct myself, there are some reasons to use Object.defineProperty() instead of the above example, when you want to set enumerable, configurable, writable, etc. But you're right, a look at the date of the post is a good idea.Palmitate
Unit.prototype.constructor gets erased when you overwrite Unit.prototype like this. I do like this solution a lot though. Maybe you could do Object.assign(Unit.prototype, { get accreation() { ... } });.Filibeg
(I tried Object.assign, but it only assigns values. It doesn't define getters and setters).Filibeg
as @Filibeg mentions, there are some very valid reasons why this method can be a bad idea. You lose class inheritance, etc. You'd be better off using alternate methods if you're using a class hierarchy. eg you cannot put a property on Field using this method.Athanor
B
5

To define setters and getters "inside the object's prototype" you have to do something like this:

Object.defineProperties(obj.__proto__, {"property_name": {get: getfn, set: setfn}})

You can short that down with an utility function:

//creates get/set properties inside an object's proto
function prop (propname, getfn, setfn) {
    var obj = {};
    obj[propname] = { get: getfn, set: setfn };
    Object.defineProperties(this, obj);        
}

function Product () {
     this.name =  "Product";
     this.amount =  10;
     this.price =  1;
     this.discount =  0;
}

//how to use prop function
prop.apply(Product.prototype, ["total", function(){ return this.amount * this.price}]);

pr = new Product();
console.log(pr.total);

Here we use prop.apply to set the context Product.prototype as "this" when we call it.

With this code you end with a get/set property inside the object's prototype, not the instance, as the question asked.

(Tested Firefox 42, Chrome 45)

Bethsaida answered 11/12, 2015 at 0:13 Comment(1)
Very nice. Though, you didn't apply a setter in your example, so trying to set pr.total would fail silently. In practice, it's probably best to pass a setter function to throw an error when one only wants to create a getter.Indohittite
Z
4

Specify a getter or a setter in constructors by Object.defineProperty() method. This method takes three arguments: the first argument is the object to add the property to, the second is the name of the property, and the third is the property's descriptor. For instance, we can define the constructor for our person object as follows:

var Employee = (function() {
    function EmployeeConstructor() {
        this.first = "";
        this.last = "";
        Object.defineProperty(
            this,
            "fullName", {
                get: function() {
                    return this.first + " " +
                        this.last;
                },
                set: function(value) {
                    var parts = value.toString().split(" ");
                    this.name = parts[0] || "";
                    this.last = parts[1] || "";
                }
            });
    }
    return
    EmployeeConstructor;
}());

Using Object.defineProperty() gives more control over our property definition. For example, we can specify if the property we are describing can be dynamically deleted or redefined, if its value can be changed, and so on.

We can such constraints by setting the following properties of the descriptor object:

  • writable: This is a Boolean that says whether the value of the property can be changed; its default value is false
  • configurable: This is a Boolean that says whether the property's descriptor can be changed or the property itself can be deleted; its default value is false
  • enumerable: This is a Boolean indicating whether the property can be accessed in a loop over the object's properties; its default value is false
  • value: This represents the value associated to the property; its default value is undefined
Zelazny answered 25/10, 2016 at 14:22 Comment(0)
D
3

Here's a simple example of Animal → Dog inheritance, with Animal having a getter and a setter:

//////////////////////////////////////////
// General Animal constructor
function Animal({age, name}) {
  // if-statements prevent triggering the setter on initialization
  if(name) this.name = name
  if(age) this.age = age
}

// an alias "age" must be used, so the setter & getter can use an
// alternative variable, to avoid using "this.age", which will cause
// a stack overflow of "infinite" call stack when setting the value.
Object.defineProperty(Animal.prototype, "age", {
  get(){
    console.log("Get age:", this.name, this._age) // getting
    return this._age
  },
  set(value){
    this._age = value
    console.log("Set age:", this.name, this._age) // setting
  }
})




//////////////////////////////////////////
// Specific Animal (Dog) constructor
function Dog({age = 0, name = 'dog'}) {
  this.name = name
  this.age = age
}

// first, defined inheritance
Dog.prototype = new Animal({});

// add whatever additional methods to the prototype of Dog
Object.assign(Dog.prototype, {
  bark(woff){
    console.log(woff)
  }
})


//////////////////////////////////////////
// Instanciating
var koko = new Animal({age:300, name:'koko'})
var dog1 = new Dog({age:1, name:'blacky'})
var dog2 = new Dog({age:5, name:'shorty'})

console.log(dog1)
koko.age
dog1.age = 3;
dog1.age
dog2.age
Drowse answered 23/10, 2020 at 9:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.