How to Define Setter/Getter on Prototype

How to define setter/getter on prototype

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); });

Getters & Setters in a prototype pattern

Why not do it on the prototype itself?

function Person(name){
this._name = name;
}

Object.defineProperty( Person.prototype, 'name', {
get:function(){ return this._name; }
})

Getters and setters on prototype

Not directly related to your question, but since you mentioned "getters and setters" on your title, I thought I might complement the answers with ES5 getters and setters:

function Person(age, name) {
this.name = name;
this.age = age;
};

Object.defineProperty(Person.prototype, "age", {
get: function() {
return this._age;
},

// Added a few things to demonstrate additional logic on the setter
set: function(num) {
num = parseInt(num, 10);
if(num > 0) {
this._age = num;
}
}
});

Then, it can be used transparently, as if it was a normal property:

var person = new Person(20, "Jane Doe");
person.age; // => 20

person.age = 15;
person.age; // => 15

person.age = "20";
person.age; // => 20 (number)

person.age = -2;
person.age; // => 20

Works on any recent browser, and IE >= 9.

Getter / Setter and Prototype Chain

When you try to retrieve a property, and the property isn't on the instance, the engine will look up the prototype chain for the first object in the chain which has a property descriptor for the property in question. When said descriptor is found, if it has a getter, that getter is invoked. Otherwise, if there is no getter, it will retrieve the plain value for that property, if there is one.

In the second case, the property descriptor is on B.prototype. But B.prototype does not have a getter for value (nor does B.prototype have a plain value for value)! So, undefined is returned.

If B.prototype had a getter for value, it would be invoked:

'use strict';
class A { constructor() { this.__value = null; }
get value() { return this.__value; }}
class B extends A { set value(value) { this.__value = value; } get value() { console.log('trying to get value'); }}
let b = new B();b.value = 2;b.value;

How to modify getters/setters on prototypes?

You don't want to retrieve the value inside adoptedStyleSheets (which obviously throws an error when called from the prototype) but its property descriptor in order to reuse it in your own adoptedStyleSheets:

(function () {
const oldAdoptedStyleSheetsGetter = Object.getOwnPropertyDescriptor(ShadowRoot.prototype, 'adoptedStyleSheets');

Object.defineProperty(ShadowRoot.prototype, "adoptedStyleSheets", {
get: function () {
console.log('adoptedStyleSheets was accessed!');
return oldAdoptedStyleSheetsGetter.get.call(this)
},
});
})();

customElements.define('web-component', class extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `Hi I'm a web component`;
console.log('this.shadowRoot.adoptedStyleSheets:', this.shadowRoot.adoptedStyleSheets);
}
});
<web-component></web-component>

how to define setter and getter in a class in javascript in GAS

Google Apps Script is an interesting dialect of JavaScript in that it is both very out of date (though I understand that's going to get fixed) and yet has the odd smattering of modern things in it.

There are a couple of older ways to do what you're trying to do, hopefully one of them works in GAS:

Object.defineProperty

This is still modern, but was added long enough ago GAS may have it: Using Object.defineProperty, like this:

var Test = function(a){
this._a = a;
};

Test.prototype.doSomething = function(){
//do something here
};

Object.defineProperty(Test.prototype, "a", {
get: function() {
return this._a;
},
set: function(value) {
this._a = value;
},
configurable: true
});

Using get and set Syntax

...to define a getter and a setter, as in your example in the question.

var Test = function(a){
this._a = a;
};

Test.prototype = {
constructor: Test,
doSomething: function(){
//do something here
},
get a() {
return this._a;
},
set a(value) {
this._a = a;
}
};

Note that when you completely replace the object on the prototype property like that, you want to be sure to define the constructor property as shown above (it's defined that way by default in the object you get automatically, but if you replace it, naturally it's not automatically provided on the replacement).

The REALLY Old Way

I doubt GAS supports it, but just in case, the really old way to do it uses __defineGetter__ and __defineSetter__ like this:

var Test = function(a){
this._a = a;
};

Test.prototype.doSomething = function(){
//do something here
};

Test.prototype.__defineGetter__("a", function() {
return this._a;
});
Test.prototype.__defineSetter__("a", function(value) {
this._a = value;
});

This was never officially part of ECMAScript, but it was in the dialect of JavaScript from Mozilla for years (and probably still is, for backward compatibility).

What is the standard way for adding getter and setter to Object.prototype?

  1. That is the standard way for an object literal.http://www.2ality.com/2015/08/object-literals-es5.html
  2. Yes, you could use defineProperty if you need to add properties dynamically

  3. Why do you need an alternative? It's hard to suggest something without knowing the use case

    • Create methods for getters and setters instead of properties?
    • Use defineProperties

defineProperties

var obj = {};
Object.defineProperties(obj, {
"property1": {
value: true,
writable: true
},
"property2": {
value: "Hello",
writable: false
}
});

defineProperty

function Archiver() {
var temperature = null;
var archive = [];

Object.defineProperty(this, 'temperature', {
get: function() {
console.log('get!');
return temperature;
},
set: function(value) {
temperature = value;
archive.push({ val: temperature });
}
});

this.getArchive = function() { return archive; };
}

var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]

Getters and setter on the prototype

function Archiver() {  // Can't define the array on the prototype or it would be shared by instances  this._archive = [];}
Archiver.prototype = { _temperature: null, getArchive: function() { return this._archive; }}
Object.defineProperty(Archiver.prototype, 'temperature', { get: function() { return this._temperature; }, set: function(value) { this._temperature = value; this._archive.push({ val: value }); }});
var arc = new Archiver();arc.temperature; // 'get!'arc.temperature = 11;arc.temperature = 13;console.log(arc.getArchive()); // [{ val: 11 }, { val: 13 }]
var arc2 = new Archiver();arc2.temperature; // 'get!'arc2.temperature = 10;arc2.temperature = 15;console.log(arc2.getArchive()); // [{ val: 10 }, { val: 15 }]

The set and get methods of a property on the prototype aren't shadowed from the corresponding property on the instance which has no set-get methods

In the constructor, the assignment to this.radius is an assignment to the "radius" property of this. The value of this is of course the newly-created object, and the prototype chain of that object is already set up by the time the constructor code runs. Thus the setter for "radius" will definitely be called, and you'll get the exception.

The setter and getter methods are indeed on the prototype, but the whole point of doing that is to make it so that every instance shares that behavior. The object referenced by this in the constructor function is one of those instances.

For "simple" prototype properties that don't have getter/setter functions, assignment to this.whatever in the constructor function or anywhere else will always create a property locally on the target object instead of changing the property value on the prototype. Properties with getter/setter methods don't work that way.

Note that the code in the constructor function does not follow different rules than code in any other function. Special things happen on the way in to the function and on the way out, but otherwise it's plain ordinary code.



Related Topics



Leave a reply



Submit