How to define setter/getter on prototype

71.3k views Asked by At

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.

6

There are 6 answers

4
bfavaretto On BEST ANSWER

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); });
0
vsync On

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

8
Bergi On

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
    }
});
6
Павел Щеголев On

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

1
Mackraken On

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)

0
Michael Horojanski On

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