Purpose of getters and setters in Javascript classes - javascript

I have been learning how to use classes in JavaScript, and something that always confused me was the how getters and setters work. I think I now finally understand them, is the below explanation correct?
They are no different to normal methods, and simply provide an alternative syntax.
A getter is simply an alternative to a method which cannot have a parameter, and means you don't have to use () to call, e.g.:
get myGetter() { return { msg: "hello" } };
...
classInstance.myGetter.msg; // "hello"
Is equivalent to:
myGetter() { return { msg: "hello" } };
...
classInstance.myGetter().msg; // "hello"
A setter is simply an alternative for a method that does take a parameter, e.g.:
set mySetter(value) { this.value = value };
...
classInstance.mySetter = "hello";
Is equivalent to:
mySetter(value) { this.value = value };
...
classInstance.mySetter("hello");

Functionally, that explanation is mostly correct, however they also have a more semantic meaning. Getters/setters are very useful for updating things that depend on a value or calculating a value, but they shouldn't be used for triggering actions. For example, this is a wrong usage of a getter:
const alerter = new Alerter;
// [...]
alerter.alert = "Hi there!"; // Alerts "Hi there!"
This is a good one:
const player = new Player;
// [...]
player.health--; // Also updates the health bar
It's also worth noting that, while in most circumstances, they behave like methods, they aren't methods at all! They are part of properties.
In JS, properties can have data descriptors and accessor descriptors. Data descriptors are "normal" properties. They have a value and you can get/set it.
const obj = {
prop: 1;
};
console.log(obj.prop); // Get; logs 1
obj.prop = 2; // Set
Accessor descriptors don't hold a value, and allow for setting what happens when the property is get and set.
const obj = {};
Object.defineProperty(obj, "prop", {
get() {
console.log("Getter was called");
return 1;
},
set(v) {
console.log("Setter was called with the value %o.", v)
}
});
/* Alternative syntax:
class Example {
get prop() {
console.log("Getter was called");
return 1;
}
set prop(v) {
console.log("Setter was called with the value %o.", v)
}
}
const obj = new Example;
*/
console.log(obj.prop); // Get; logs 1
obj.prop = 2; // Set
That code logs:
Getter was called
1
Setter was called with the value 2.

There is a huge difference between getters/setters and normal properties, in their most simple form you could think of them as an alternative syntax. however getters/setters provide more convenient solutions for certain use cases - though eventually getters/setters and methods are properties, getters/setters has accessor descriptors while methods has data descriptors.
I'm gonna list some few use cases on top of my head
getters/setters enable you to trigger custom functionality when reading/setting a property without having to create two different methods
let xThatShouldBeHidden = 1;
const object = {
get x() {
return xThatShouldBeHidden
},
set x(newX) {
if (newX === 0) {
throw new Error('You can not set x to 0')
}
xThatShouldBeHidden = newX
}
}
Triggering custom functionality is a cool feature, it enables you to do optimizations while still abstracting that behind simple syntax.
Imagine you you have array of items that has values, and later you want to get the weight of the item (value / sum of all values of items)
const items = [{val: 2}, {val:4}]
one way to do it would be which required you to loop twice even if eventually the weight was read from only one item
const totalSum = items.reduce((acc,cur), acc + cur.val,0));
const itemsWithWeights = items.map(item => ({...item, weight: item.val / totalSum});
now with getters we do it in one loop plus number of actual reads
const getItemsWithWeightsGetter = () => {
let totalSum;
return items.map(item => ({
...item,
get weight() {
if (totalSum === undefined) {
totalSum = items.reduce((acc, cur) => acc + cur.val, 0);
}
return item.val / totalSum;
},
}));
};
const itemsWithWeightsGetter = getItemsWithWeightsGetter();
another use case is the example i just shared above, when you provide just a getter that makes the value read only, making code throws when you try to set the value - in strict mode only

The difference is, you can have a getter/setter pair with the same name. For example, when working with DOM, there is the innerHTML getter/setter pair.
const element = document.querySelector("div")
console.log(element.innerHTML) // Outputs HTML as string
element.innerHTML = "Hello!" // Sets the HTML of element to "Hello!"

Related

Unable to have get and set method in Object Constructor (not in class) in JavaScript? [duplicate]

I recently read about the fact that there is a possibility of defining getters/setters in JavaScript. It seems extremely helpful - the setter is a kind of 'helper' which can parse the value to be set first, before actually setting it.
For example, I currently have this 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);
This code always converts value to a boolean. So if you code instance.test = 0, then instance.test === false.
However, for this to work you have to actually return an object, which means that the new instance is not of type obj but just is a plain object. This means that changing the prototype of obj has no effect on instances. For example, this does not work - instance.func is undefined:
obj.prototype.func = function() { console.log(this.value); };
because instance is not of type obj. To get the prototype functions work, I guess I should not return a plain object, but rather not return anything so that instance would just be of type obj, like a regular constructor works.
The problem then is how to implement getters/setters? I can only find articles describing how to add these to an object, not as being part of the constructor of a custom type.
So how do I implement getters/setters in the constructor so as to be able to both use getters/setters and extending the prototype?
You can't do that.
You can set setter/getters for properties of objects though. I advice you use ES5 Object.defineProperties though. of course this only works in modern browsers.
var obj = function() {
...
Object.defineProperties(this, {
"test": {
"get": function() { ... },
"set": function() { ... }
}
});
}
obj.prototype.func = function() { ... }
var o = new obj;
o.test;
o.func();
Usually you want class methods. The answer by #Raynos on May 7, 2011 gets the job done, but it defines an instance method, not a class method.
The following illustrates a class definition with a the getter and setter being part of the class. This definition is a lot like the answer by #Raynos, but with two differences in the code: (1) The "defineProperties()" action has been moved out of the constructor. (2) The argument to "defineProperties()"as been changed from the instance object "this", to the constructor's prototype object.
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);
Which produces these results:
initial Area:4
modified Area:9
Although usually the distinction between class versus instance
definition is just a matter of style, there is a purpose to
good style, and there is a case where the distinction matters:
the memoized getter. The purpose for a memoized getter is
described here: Smart/self-overwriting/lazy getters
Define the getter at the class level when the memoized value is to
pertain to the entire class. For example, a configuration file
should be read only once; the resulting values should then apply
for the duration of the program. The following sample code
defines a memoized getter at the class level.
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);
Produces:
memoizedConfigParam:42
As can be seen in the example, memoized getters have the
characteristic that the getter function deletes itself,
then replaces itself with a simple value that
(presumably) will never change.
Note that 'configurable' must be set to 'true'.
Define the getter at the instance level when the memoized value
depends upon the contents of instance. The definition moves
inside the constructor, and the object of attention is 'this'.
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);
Produces:
memoizedCalculation 2:8
memoizedCalculation 3:27
If you want to guarantee (rather than presume) that the memoized
value will never be changed, the 'writable' attribute needs to
be changed. That makes the code a bit more complicated.
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
Produces:
memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'
The OP's original question, from March 7, 2011, presented basic
getter and setter syntax, noted that it worked on an object but
not on 'this', and asked how to define getters and setters within
a constructor. In addition to all the examples above, there is
also a "cheap-shot" way of doing it: create a new object within
the constructor, like the OP did, but then assign the object to
be a member within 'this'. So, the original code would look like
this:
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);
Produces:
false
Believe it or not, I have actually run into situations where
this "cheap-shot" is the best solution. Specifically, I used this
technique when I had records from several tables encapsulated within
a single class, and wanted to present a unified view as though
they were a single record called 'data'.
Have fun.
IAM_AL_X
Update for ES6 -- have a look at section 19.3.1 of Alex Rauschmayer's book Exploring ES6 http://exploringjs.com/es6/ch_maps-sets.html#sec_weakmaps-private-data which demonstrates how to use WeakMaps with getters and setters to hold private data. Combining with section 16.2.2.3 http://exploringjs.com/es6/ch_classes.html#leanpub-auto-getters-and-setters would result in something like
# 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
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);
I know this might be extremely late but I figured out a different way to accomplish what you want and for the sake of people, like myself, googling for an answer to this here it is.
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
I've tested this in chrome, safari, mobile safari, firefox and they all work (latest versions of course)
#Alex I see it as more option and more power, programming is art, #Nat share his finding with us, and for that I thank him. Maybe someone want to do it that way.
I'm sure the setter version is the same but just changing that g to a s.
i.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
With that said, this feature is deprecated, advices to not to use in production coding.

What is difference between two functions in class returning the same value [duplicate]

I've been trying to get my head around getters and setters and its not sinking in. I've read JavaScript Getters and Setters and Defining Getters and Setters and just not getting it.
Can someone clearly state:
What a getter and setter are meant to do, and
Give some VERY simple examples?
In addition to #millimoose's answer, setters can also be used to update other values.
function Name(first, last) {
this.first = first;
this.last = last;
}
Name.prototype = {
get fullName() {
return this.first + " " + this.last;
},
set fullName(name) {
var names = name.split(" ");
this.first = names[0];
this.last = names[1];
}
};
Now, you can set fullName, and first and last will be updated and vice versa.
n = new Name('Claude', 'Monet')
n.first # "Claude"
n.last # "Monet"
n.fullName # "Claude Monet"
n.fullName = "Gustav Klimt"
n.first # "Gustav"
n.last # "Klimt"
Getters and Setters in JavaScript
Overview
Getters and setters in JavaScript are used for defining computed properties, or accessors. A computed property is one that uses a function to get or set an object value. The basic theory is doing something like this:
var user = { /* ... object with getters and setters ... */ };
user.phone = '+1 (123) 456-7890'; // updates a database
console.log( user.areaCode ); // displays '123'
console.log( user.area ); // displays 'Anytown, USA'
This is useful for automatically doing things behind-the-scenes when a property is accessed, like keeping numbers in range, reformatting strings, triggering value-has-changed events, updating relational data, providing access to private properties, and more.
The examples below show the basic syntax, though they simply get and set the internal object value without doing anything special. In real-world cases you would modify the input and/or output value to suit your needs, as noted above.
get/set Keywords
ECMAScript 5 supports get and set keywords for defining computed properties. They work with all modern browsers except IE 8 and below.
var foo = {
bar : 123,
get bar(){ return bar; },
set bar( value ){ this.bar = value; }
};
foo.bar = 456;
var gaz = foo.bar;
Custom Getters and Setters
get and set aren't reserved words, so they can be overloaded to create your own custom, cross-browser computed property functions. This will work in any browser.
var foo = {
_bar : 123,
get : function( name ){ return this[ '_' + name ]; },
set : function( name, value ){ this[ '_' + name ] = value; }
};
foo.set( 'bar', 456 );
var gaz = foo.get( 'bar' );
Or for a more compact approach, a single function may be used.
var foo = {
_bar : 123,
value : function( name /*, value */ ){
if( arguments.length < 2 ){ return this[ '_' + name ]; }
this[ '_' + name ] = value;
}
};
foo.value( 'bar', 456 );
var gaz = foo.value( 'bar' );
Avoid doing something like this, which can lead to code bloat.
var foo = {
_a : 123, _b : 456, _c : 789,
getA : function(){ return this._a; },
getB : ..., getC : ..., setA : ..., setB : ..., setC : ...
};
For the above examples, the internal property names are abstracted with an underscore in order to discourage users from simply doing foo.bar vs. foo.get( 'bar' ) and getting an "uncooked" value. You can use conditional code to do different things depending on the name of the property being accessed (via the name parameter).
Object.defineProperty()
Using Object.defineProperty() is another way to add getters and setters, and can be used on objects after they're defined. It can also be used to set configurable and enumerable behaviors. This syntax also works with IE 8, but unfortunately only on DOM objects.
var foo = { _bar : 123 };
Object.defineProperty( foo, 'bar', {
get : function(){ return this._bar; },
set : function( value ){ this._bar = value; }
} );
foo.bar = 456;
var gaz = foo.bar;
__defineGetter__()
Finally, __defineGetter__() is another option. It's deprecated, but still widely used around the web and thus unlikely to disappear anytime soon. It works on all browsers except IE 10 and below. Though the other options also work well on non-IE, so this one isn't that useful.
var foo = { _bar : 123; }
foo.__defineGetter__( 'bar', function(){ return this._bar; } );
foo.__defineSetter__( 'bar', function( value ){ this._bar = value; } );
Also worth noting is that in the latter examples, the internal names must be different than the accessor names to avoid recursion (ie, foo.bar calling foo.get(bar) calling foo.bar calling foo.get(bar)...).
See Also
MDN get, set,
Object.defineProperty(), __defineGetter__(), __defineSetter__()
MSDN
IE8 Getter Support
You'd use them for instance to implement computed properties.
For example:
function Circle(radius) {
this.radius = radius;
}
Object.defineProperty(Circle.prototype, 'circumference', {
get: function() { return 2*Math.PI*this.radius; }
});
Object.defineProperty(Circle.prototype, 'area', {
get: function() { return Math.PI*this.radius*this.radius; }
});
c = new Circle(10);
console.log(c.area); // Should output 314.159
console.log(c.circumference); // Should output 62.832
(CodePen)
Sorry to resurrect an old question, but I thought I might contribute a couple of very basic examples and for-dummies explanations. None of the other answers posted thusfar illustrate syntax like the MDN guide's first example, which is about as basic as one can get.
Getter:
var settings = {
firstname: 'John',
lastname: 'Smith',
get fullname() { return this.firstname + ' ' + this.lastname; }
};
console.log(settings.fullname);
... will log John Smith, of course. A getter behaves like a variable object property, but offers the flexibility of a function to calculate its returned value on the fly. It's basically a fancy way to create a function that doesn't require () when calling.
Setter:
var address = {
set raw(what) {
var loc = what.split(/\s*;\s*/),
area = loc[1].split(/,?\s+(\w{2})\s+(?=\d{5})/);
this.street = loc[0];
this.city = area[0];
this.state = area[1];
this.zip = area[2];
}
};
address.raw = '123 Lexington Ave; New York NY 10001';
console.log(address.city);
... will log New York to the console. Like getters, setters are called with the same syntax as setting an object property's value, but are yet another fancy way to call a function without ().
See this jsfiddle for a more thorough, perhaps more practical example. Passing values into the object's setter triggers the creation or population of other object items. Specifically, in the jsfiddle example, passing an array of numbers prompts the setter to calculate mean, median, mode, and range; then sets object properties for each result.
Getters and setters really only make sense when you have private properties of classes. Since Javascript doesn't really have private class properties as you would normally think of from Object Oriented Languages, it can be hard to understand. Here is one example of a private counter object. The nice thing about this object is that the internal variable "count" cannot be accessed from outside the object.
var counter = function() {
var count = 0;
this.inc = function() {
count++;
};
this.getCount = function() {
return count;
};
};
var i = new Counter();
i.inc();
i.inc();
// writes "2" to the document
document.write( i.getCount());
If you are still confused, take a look at Crockford's article on Private Members in Javascript.
I think the first article you link to states it pretty clearly:
The obvious advantage to writing JavaScript in this manner is that you can use it obscure values that you don't want the user to directly access.
The goal here is to encapsulate and abstract away the fields by only allowing access to them thru a get() or set() method. This way, you can store the field/data internally in whichever way you want, but outside components are only away of your published interface. This allows you to make internal changes without changing external interfaces, to do some validation or error-checking within the set() method, etc.
Although often we are used to seeing objects with public properties without any access
control, JavaScript allows us to accurately describe properties. In fact, we can use
descriptors in order to control how a property can be accessed and which logic we can
apply to it. Consider the following example:
var employee = {
first: "Boris",
last: "Sergeev",
get fullName() {
return this.first + " " + this.last;
},
set fullName(value) {
var parts = value.toString().split(" ");
this.first = parts[0] || "";
this.last = parts[1] || "";
},
email: "boris.sergeev#example.com"
};
The final result:
console.log(employee.fullName); //Boris Sergeev
employee.fullName = "Alex Makarenko";
console.log(employee.first);//Alex
console.log(employee.last);//Makarenko
console.log(employee.fullName);//Alex Makarenko
You can define instance method for js class, via prototype of the constructor.
Following is the sample code:
// BaseClass
var BaseClass = function(name) {
// instance property
this.name = name;
};
// instance method
BaseClass.prototype.getName = function() {
return this.name;
};
BaseClass.prototype.setName = function(name) {
return this.name = name;
};
// test - start
function test() {
var b1 = new BaseClass("b1");
var b2 = new BaseClass("b2");
console.log(b1.getName());
console.log(b2.getName());
b1.setName("b1_new");
console.log(b1.getName());
console.log(b2.getName());
}
test();
// test - end
And, this should work for any browser, you can also simply use nodejs to run this code.
If you're referring to the concept of accessors, then the simple goal is to hide the underlying storage from arbitrary manipulation. The most extreme mechanism for this is
function Foo(someValue) {
this.getValue = function() { return someValue; }
return this;
}
var myFoo = new Foo(5);
/* We can read someValue through getValue(), but there is no mechanism
* to modify it -- hurrah, we have achieved encapsulation!
*/
myFoo.getValue();
If you're referring to the actual JS getter/setter feature, eg. defineGetter/defineSetter, or { get Foo() { /* code */ } }, then it's worth noting that in most modern engines subsequent usage of those properties will be much much slower than it would otherwise be. eg. compare performance of
var a = { getValue: function(){ return 5; }; }
for (var i = 0; i < 100000; i++)
a.getValue();
vs.
var a = { get value(){ return 5; }; }
for (var i = 0; i < 100000; i++)
a.value;
What's so confusing about it... getters are functions that are called when you get a property, setters, when you set it.
example, if you do
obj.prop = "abc";
You're setting the property prop, if you're using getters/setters, then the setter function will be called, with "abc" as an argument.
The setter function definition inside the object would ideally look something like this:
set prop(var) {
// do stuff with var...
}
I'm not sure how well that is implemented across browsers. It seems Firefox also has an alternative syntax, with double-underscored special ("magic") methods. As usual Internet Explorer does not support any of this.
I was also somewhat confused by the explanation I read, because I was trying to add a property to an existing prototype that I did not write, so replacing the prototype seemed like the wrong approach. So, for posterity, here's how I added a last property to Array:
Object.defineProperty(Array.prototype, "last", {
get: function() { return this[this.length - 1] }
});
Ever so slightly nicer than adding a function IMHO.
You can also use __defineGetter__:
function Vector2(x,y) {
this.x = x;
this.y = y;
}
Vector2.prototype.__defineGetter__("magnitude", function () {
return Math.sqrt(this.x*this.x+this.y*this.y);
});
console.log(new Vector2(1,1).magnitude)
Or, if you prefer:
function Vector2(x,y) {
this.x = x;
this.y = y;
this.__defineGetter__("magnitude", function () {
return Math.sqrt(this.x*this.x+this.y*this.y);
});
}
console.log(new Vector2(1,1).magnitude)
But this function has been flagged as "legacy" recently, being dropped in favor of Object.defineProperty().
There's no example here with ES6 class (which is not even 'new' now, it's the norm):
class Student {
contructor(firstName, lastName){
this.firstName = firstName
this.lastName = lastName
this.secretId = Math.random()
}
get fullName() {
return `${this.firstName} ${this.lastName}`; // this is backtick in js, u can check it out here: https://stackoverflow.com/a/27678299/12056841
}
set firstName(newFirstName) {
// validate that newFirstName is a string (and maybe limit length)
this.firstName = newFirstName
}
get studentId() { return this.secretId }
}
and no setter for secretId because we don't want anyone to change it.
** if secretId shouldn't be changed at all, a nice approach is to declare it as 'private' to this class by adding a '#' to it
(e.g: this.#secretId = Math.random(), and return this.#secretId
Update: about backing fields
You might need to rename your field - or your setter function but it makes more sense to me to change your field name. One option is like I mentioned above (using a # for declaring the field as 'private'). Another way is to just rename it (_firstName, firstName_...)
I've got one for you guys that might be a little ugly, but it does get'er done across platforms
function myFunc () {
var _myAttribute = "default";
this.myAttribute = function() {
if (arguments.length > 0) _myAttribute = arguments[0];
return _myAttribute;
}
}
this way, when you call
var test = new myFunc();
test.myAttribute(); //-> "default"
test.myAttribute("ok"); //-> "ok"
test.myAttribute(); //-> "ok"
If you really want to spice things up.. you can insert a typeof check:
if (arguments.length > 0 && typeof arguments[0] == "boolean") _myAttribute = arguments[0];
if (arguments.length > 0 && typeof arguments[0] == "number") _myAttribute = arguments[0];
if (arguments.length > 0 && typeof arguments[0] == "string") _myAttribute = arguments[0];
or go even crazier with the advanced typeof check: type.of() code at codingforums.com

Unfamiliar syntax for JavaScript object key [duplicate]

I've been trying to get my head around getters and setters and its not sinking in. I've read JavaScript Getters and Setters and Defining Getters and Setters and just not getting it.
Can someone clearly state:
What a getter and setter are meant to do, and
Give some VERY simple examples?
In addition to #millimoose's answer, setters can also be used to update other values.
function Name(first, last) {
this.first = first;
this.last = last;
}
Name.prototype = {
get fullName() {
return this.first + " " + this.last;
},
set fullName(name) {
var names = name.split(" ");
this.first = names[0];
this.last = names[1];
}
};
Now, you can set fullName, and first and last will be updated and vice versa.
n = new Name('Claude', 'Monet')
n.first # "Claude"
n.last # "Monet"
n.fullName # "Claude Monet"
n.fullName = "Gustav Klimt"
n.first # "Gustav"
n.last # "Klimt"
Getters and Setters in JavaScript
Overview
Getters and setters in JavaScript are used for defining computed properties, or accessors. A computed property is one that uses a function to get or set an object value. The basic theory is doing something like this:
var user = { /* ... object with getters and setters ... */ };
user.phone = '+1 (123) 456-7890'; // updates a database
console.log( user.areaCode ); // displays '123'
console.log( user.area ); // displays 'Anytown, USA'
This is useful for automatically doing things behind-the-scenes when a property is accessed, like keeping numbers in range, reformatting strings, triggering value-has-changed events, updating relational data, providing access to private properties, and more.
The examples below show the basic syntax, though they simply get and set the internal object value without doing anything special. In real-world cases you would modify the input and/or output value to suit your needs, as noted above.
get/set Keywords
ECMAScript 5 supports get and set keywords for defining computed properties. They work with all modern browsers except IE 8 and below.
var foo = {
bar : 123,
get bar(){ return bar; },
set bar( value ){ this.bar = value; }
};
foo.bar = 456;
var gaz = foo.bar;
Custom Getters and Setters
get and set aren't reserved words, so they can be overloaded to create your own custom, cross-browser computed property functions. This will work in any browser.
var foo = {
_bar : 123,
get : function( name ){ return this[ '_' + name ]; },
set : function( name, value ){ this[ '_' + name ] = value; }
};
foo.set( 'bar', 456 );
var gaz = foo.get( 'bar' );
Or for a more compact approach, a single function may be used.
var foo = {
_bar : 123,
value : function( name /*, value */ ){
if( arguments.length < 2 ){ return this[ '_' + name ]; }
this[ '_' + name ] = value;
}
};
foo.value( 'bar', 456 );
var gaz = foo.value( 'bar' );
Avoid doing something like this, which can lead to code bloat.
var foo = {
_a : 123, _b : 456, _c : 789,
getA : function(){ return this._a; },
getB : ..., getC : ..., setA : ..., setB : ..., setC : ...
};
For the above examples, the internal property names are abstracted with an underscore in order to discourage users from simply doing foo.bar vs. foo.get( 'bar' ) and getting an "uncooked" value. You can use conditional code to do different things depending on the name of the property being accessed (via the name parameter).
Object.defineProperty()
Using Object.defineProperty() is another way to add getters and setters, and can be used on objects after they're defined. It can also be used to set configurable and enumerable behaviors. This syntax also works with IE 8, but unfortunately only on DOM objects.
var foo = { _bar : 123 };
Object.defineProperty( foo, 'bar', {
get : function(){ return this._bar; },
set : function( value ){ this._bar = value; }
} );
foo.bar = 456;
var gaz = foo.bar;
__defineGetter__()
Finally, __defineGetter__() is another option. It's deprecated, but still widely used around the web and thus unlikely to disappear anytime soon. It works on all browsers except IE 10 and below. Though the other options also work well on non-IE, so this one isn't that useful.
var foo = { _bar : 123; }
foo.__defineGetter__( 'bar', function(){ return this._bar; } );
foo.__defineSetter__( 'bar', function( value ){ this._bar = value; } );
Also worth noting is that in the latter examples, the internal names must be different than the accessor names to avoid recursion (ie, foo.bar calling foo.get(bar) calling foo.bar calling foo.get(bar)...).
See Also
MDN get, set,
Object.defineProperty(), __defineGetter__(), __defineSetter__()
MSDN
IE8 Getter Support
You'd use them for instance to implement computed properties.
For example:
function Circle(radius) {
this.radius = radius;
}
Object.defineProperty(Circle.prototype, 'circumference', {
get: function() { return 2*Math.PI*this.radius; }
});
Object.defineProperty(Circle.prototype, 'area', {
get: function() { return Math.PI*this.radius*this.radius; }
});
c = new Circle(10);
console.log(c.area); // Should output 314.159
console.log(c.circumference); // Should output 62.832
(CodePen)
Sorry to resurrect an old question, but I thought I might contribute a couple of very basic examples and for-dummies explanations. None of the other answers posted thusfar illustrate syntax like the MDN guide's first example, which is about as basic as one can get.
Getter:
var settings = {
firstname: 'John',
lastname: 'Smith',
get fullname() { return this.firstname + ' ' + this.lastname; }
};
console.log(settings.fullname);
... will log John Smith, of course. A getter behaves like a variable object property, but offers the flexibility of a function to calculate its returned value on the fly. It's basically a fancy way to create a function that doesn't require () when calling.
Setter:
var address = {
set raw(what) {
var loc = what.split(/\s*;\s*/),
area = loc[1].split(/,?\s+(\w{2})\s+(?=\d{5})/);
this.street = loc[0];
this.city = area[0];
this.state = area[1];
this.zip = area[2];
}
};
address.raw = '123 Lexington Ave; New York NY 10001';
console.log(address.city);
... will log New York to the console. Like getters, setters are called with the same syntax as setting an object property's value, but are yet another fancy way to call a function without ().
See this jsfiddle for a more thorough, perhaps more practical example. Passing values into the object's setter triggers the creation or population of other object items. Specifically, in the jsfiddle example, passing an array of numbers prompts the setter to calculate mean, median, mode, and range; then sets object properties for each result.
Getters and setters really only make sense when you have private properties of classes. Since Javascript doesn't really have private class properties as you would normally think of from Object Oriented Languages, it can be hard to understand. Here is one example of a private counter object. The nice thing about this object is that the internal variable "count" cannot be accessed from outside the object.
var counter = function() {
var count = 0;
this.inc = function() {
count++;
};
this.getCount = function() {
return count;
};
};
var i = new Counter();
i.inc();
i.inc();
// writes "2" to the document
document.write( i.getCount());
If you are still confused, take a look at Crockford's article on Private Members in Javascript.
I think the first article you link to states it pretty clearly:
The obvious advantage to writing JavaScript in this manner is that you can use it obscure values that you don't want the user to directly access.
The goal here is to encapsulate and abstract away the fields by only allowing access to them thru a get() or set() method. This way, you can store the field/data internally in whichever way you want, but outside components are only away of your published interface. This allows you to make internal changes without changing external interfaces, to do some validation or error-checking within the set() method, etc.
Although often we are used to seeing objects with public properties without any access
control, JavaScript allows us to accurately describe properties. In fact, we can use
descriptors in order to control how a property can be accessed and which logic we can
apply to it. Consider the following example:
var employee = {
first: "Boris",
last: "Sergeev",
get fullName() {
return this.first + " " + this.last;
},
set fullName(value) {
var parts = value.toString().split(" ");
this.first = parts[0] || "";
this.last = parts[1] || "";
},
email: "boris.sergeev#example.com"
};
The final result:
console.log(employee.fullName); //Boris Sergeev
employee.fullName = "Alex Makarenko";
console.log(employee.first);//Alex
console.log(employee.last);//Makarenko
console.log(employee.fullName);//Alex Makarenko
You can define instance method for js class, via prototype of the constructor.
Following is the sample code:
// BaseClass
var BaseClass = function(name) {
// instance property
this.name = name;
};
// instance method
BaseClass.prototype.getName = function() {
return this.name;
};
BaseClass.prototype.setName = function(name) {
return this.name = name;
};
// test - start
function test() {
var b1 = new BaseClass("b1");
var b2 = new BaseClass("b2");
console.log(b1.getName());
console.log(b2.getName());
b1.setName("b1_new");
console.log(b1.getName());
console.log(b2.getName());
}
test();
// test - end
And, this should work for any browser, you can also simply use nodejs to run this code.
If you're referring to the concept of accessors, then the simple goal is to hide the underlying storage from arbitrary manipulation. The most extreme mechanism for this is
function Foo(someValue) {
this.getValue = function() { return someValue; }
return this;
}
var myFoo = new Foo(5);
/* We can read someValue through getValue(), but there is no mechanism
* to modify it -- hurrah, we have achieved encapsulation!
*/
myFoo.getValue();
If you're referring to the actual JS getter/setter feature, eg. defineGetter/defineSetter, or { get Foo() { /* code */ } }, then it's worth noting that in most modern engines subsequent usage of those properties will be much much slower than it would otherwise be. eg. compare performance of
var a = { getValue: function(){ return 5; }; }
for (var i = 0; i < 100000; i++)
a.getValue();
vs.
var a = { get value(){ return 5; }; }
for (var i = 0; i < 100000; i++)
a.value;
What's so confusing about it... getters are functions that are called when you get a property, setters, when you set it.
example, if you do
obj.prop = "abc";
You're setting the property prop, if you're using getters/setters, then the setter function will be called, with "abc" as an argument.
The setter function definition inside the object would ideally look something like this:
set prop(var) {
// do stuff with var...
}
I'm not sure how well that is implemented across browsers. It seems Firefox also has an alternative syntax, with double-underscored special ("magic") methods. As usual Internet Explorer does not support any of this.
I was also somewhat confused by the explanation I read, because I was trying to add a property to an existing prototype that I did not write, so replacing the prototype seemed like the wrong approach. So, for posterity, here's how I added a last property to Array:
Object.defineProperty(Array.prototype, "last", {
get: function() { return this[this.length - 1] }
});
Ever so slightly nicer than adding a function IMHO.
You can also use __defineGetter__:
function Vector2(x,y) {
this.x = x;
this.y = y;
}
Vector2.prototype.__defineGetter__("magnitude", function () {
return Math.sqrt(this.x*this.x+this.y*this.y);
});
console.log(new Vector2(1,1).magnitude)
Or, if you prefer:
function Vector2(x,y) {
this.x = x;
this.y = y;
this.__defineGetter__("magnitude", function () {
return Math.sqrt(this.x*this.x+this.y*this.y);
});
}
console.log(new Vector2(1,1).magnitude)
But this function has been flagged as "legacy" recently, being dropped in favor of Object.defineProperty().
There's no example here with ES6 class (which is not even 'new' now, it's the norm):
class Student {
contructor(firstName, lastName){
this.firstName = firstName
this.lastName = lastName
this.secretId = Math.random()
}
get fullName() {
return `${this.firstName} ${this.lastName}`; // this is backtick in js, u can check it out here: https://stackoverflow.com/a/27678299/12056841
}
set firstName(newFirstName) {
// validate that newFirstName is a string (and maybe limit length)
this.firstName = newFirstName
}
get studentId() { return this.secretId }
}
and no setter for secretId because we don't want anyone to change it.
** if secretId shouldn't be changed at all, a nice approach is to declare it as 'private' to this class by adding a '#' to it
(e.g: this.#secretId = Math.random(), and return this.#secretId
Update: about backing fields
You might need to rename your field - or your setter function but it makes more sense to me to change your field name. One option is like I mentioned above (using a # for declaring the field as 'private'). Another way is to just rename it (_firstName, firstName_...)
I've got one for you guys that might be a little ugly, but it does get'er done across platforms
function myFunc () {
var _myAttribute = "default";
this.myAttribute = function() {
if (arguments.length > 0) _myAttribute = arguments[0];
return _myAttribute;
}
}
this way, when you call
var test = new myFunc();
test.myAttribute(); //-> "default"
test.myAttribute("ok"); //-> "ok"
test.myAttribute(); //-> "ok"
If you really want to spice things up.. you can insert a typeof check:
if (arguments.length > 0 && typeof arguments[0] == "boolean") _myAttribute = arguments[0];
if (arguments.length > 0 && typeof arguments[0] == "number") _myAttribute = arguments[0];
if (arguments.length > 0 && typeof arguments[0] == "string") _myAttribute = arguments[0];
or go even crazier with the advanced typeof check: type.of() code at codingforums.com

Validate/Preprocess property before assigning it to object in JavaScript

What is the best-practice to validate and/or preprocess a property before assigning it to an object in JavaScript?
The application for that would be to create an object and to guarantee that a specific property of it will always have a specific type or maybe do some preprocessing with it.
For example, if I create an object:
var obj = {
settings: {}
};
Then when I do something like:
obj.settings = "{foo: bar}";
It would automatically check the type of the assignment - if it is a string, it will try to parse it to an object; if it's an object, it will just assign it; else it will throw an error. This would protect the object's property against being assigned to "anything".
Also, does this make sense at all to do in JavaScript or am I just trying to have strong typed features in a language that is weak typed?
You can do this with Object.defineProperty:
var obj = {}
Object.defineProperty(obj, 'settings', {
set: function (x) {
if (typeof x === 'string') {
this._settings = JSON.parse(x)
} else {
this._settings = x
}
},
get: function () {
return this._settings
}
})
obj.settings = {foo: 'bar'}
console.log(obj.settings)
obj.settings = '{foo: "baz"}'
console.log(obj.settings)
However, if this is desirable depends on your specific use case. I frankly never used it so far. My recommendation is: don't get fancy :)
IMHO this is not strong typing, but the opposite as you are more dynamic. If you want strong typing you could try flow or TypeScript
A simple solution could be to use a getter/setter like below that gets triggered if a value is assigned to a property and how to process it :
let obj = {}
Object.defineProperty(obj, "settings", {
set: function (value) { // preprocess
this._value = value;
},
get: function () {
return "changed";
}
});
You could do this afterward:
obj.settings = "{foo: bar}";
Let me know if this makes any sense.
Reference:
MDN Reference: Object.defineProperty()

How can I define a default getter and setter using ECMAScript 5?

How can I specify a default getter for a prototype?
With default getter I mean a function that is called if obj.undefinedProperty123 is called.
I tried Object.prototype.get = function(property) {..} but this is not called in this case.
In ECMAScript 5, you can only intercept get/set operations on specific named properties (not universally all properties) via Object.defineProperty:
Object.defineProperty(someObj, "someProp", {
get: function() {
console.log("you tried to get someObj.someProp");
return "foo";
}
});
Here, the get function will run any time code tries to read someObj.someProp.
In the upcoming ECMAScript 6 draft, this will be possible via proxies. A proxy has an underlying target object and set/get functions. Any time a set or get operation happens on any of a proxy's properties, the appropriate function runs, taking as arguments the proxy's target object, property name used, and the value used in a set attempt.
var proxyHandler = {
get: function(obj, name){
console.log("you're getting property " + name);
return target[name];
},
set: function(obj, name, value) {
console.log("you're setting property " + name);
target[name] = value;
}
}
var underlyingObj = {};
// use prox instead of underlyingObj to use get/set interceptor functions
var prox = new Proxy(underlyingObj, proxyHandler);
Here, setting to getting property values on prox will cause the set/get functions to run.
What Gareth said, except it's __noSuchMethod__.
Or maybe you were thinking of PHP?
Here's a very good article on the recently standardized property getters/setters, highlighting some previous non-standard incarnations.
http://whereswalden.com/2010/04/16/more-spidermonkey-changes-ancient-esoteric-very-rarely-used-syntax-for-creating-getters-and-setters-is-being-removed/
summary: there's no standard 'catch-all' getter / setter (yet), but Object.defineProperty is the future.
You need to wait for the implementation of the ECMA6 "Proxy" system, designed to do exactly this. See http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies.
You want to create a Proxy:
const data = {};
const proxy = new Proxy(data, {
get: (target, prop) => {
console.log({ target, prop });
return "My Value";
},
set: (target, prop, value) => {
console.log({ target, prop, value });
return true;
},
});
proxy["foo"] = "bar";
const bar = proxy["foo"];
Firefox it's possible with non-standard noSuchMethod:-
({__noSuchMethod__:function(){alert(1);}}).a();
I am not sure about what you are asking. But If you want a method to be called when the user attempts to Access object.nonExistingProperty . I dont think there is any way to do that.
maybe late to ther party, let just add simple ES5 friendly "class creation": both string props and getters - i needed it for some ancient rewrite, to temporarily support IE (yes, that hurts, still found someone relying on ActiveX)
var MyObj = function () {
var obj = {
url: 'aabbcc',
a: function(){ return this.url;}
}
Object.defineProperty(obj, "urltoo", { get: function () { return this.url; } })
return obj;
}
var X = new MyObj();
x.url // aabbcc
x.urltoo // aabbcc
x.urltoo() // error - not a function
x.a() // aabbcc
x.a // ƒ (){ return this.url;}
I ran into this question because I wanted this behavior: if object property is undefined, return some default value instead.
const options = {
foo: 'foo',
bar: 'bar',
baz: 'baz'
};
function useOptionValue(optionName) {
// I want `options` to return a default value if `optionName` does not exist
const value = options[optionName];
// etc...
}
The simple way to do this (without Proxy or Object.defineProperty overkill for this use case) is like so:
function useOptionValue(optionName) {
const value = options[optionName] || defaultValue;
}
Or, if you want to get fancy, you could use a Symbol:
const default = Symbol.for('default');
const options = {
foo: 'foo',
bar: 'bar',
baz: 'baz',
[default]: 'foobarbaz'
};
function useOptionValue(optionName) {
const value = options[optionName] || options[default];
}

Categories

Resources