ref – https://javascriptplayground.com/es5-getters-setters/
this is ugly:
1 2 3 |
person.setLastName('Smith'); person.setFirstName('Jimmy'); person.getFullName(); // Jimmy Smith |
But this is ugly, and requires the users of your object to care that the properties are related; in a more complex example, that might not be as obvious as with names. Luckily, there’s a better way, added in ECMAScript 5.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// we have literal object referenced by variable person var person = { firstName: 'Jimmy', lastName: 'Smith', get fullName() { return this.firstName + ' ' + this.lastName; }, set fullName (name) { var words = name.toString().split(' '); this.firstName = words[0] || ''; this.lastName = words[1] || ''; } } |
we see that we have a get/set for property fullName.
Following them is the property they relate to (fullName) and a function body that defines the behavior when the property is accessed (name = person.fullName) or modified (person.fullName = ‘Some Name’).
In other words, get simply means to access the data, so we naturally return the property data. set means to set some properties in the object through one parameter. We can only provide one parameter because in an assignment, there’s only one parameter on the right side.
These two keywords define accessor functions: a getter and a setter for the fullName property.
When the property is accessed, the return value from the getter is used.
When a value is set, the setter is called and passed the value that was set. It’s up to you what you do with that value, but what is returned from the setter is the value that was passed in – so you don’t need to return anything.
1 2 3 |
person.fullName = 'Jack Franklin'; console.log(person.firstName); // Jack console.log(person.lastName) // Franklin |
The official way: Object.defineProperty
This method takes three arguments.
1) The first is the object to add the property to
2) the second is the name of the property
3) and the third is an object that describes the property (known as the property’s descriptor).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var person = { firstName: 'Jimmy', lastName: 'Smith' }; Object.defineProperty(person, 'fullName', { get: function() { return firstName + ' ' + lastName; }, set: function(name) { var words = name.split(' '); this.firstName = words[0] || ''; this.lastName = words[1] || ''; } }); |
But why do it this way?
The advantage here isn’t immediately apparent. Other than being able to add properties after creating the initial object, is there a real benefit?
When you define a property this way, you can do much more than just define a setter or getter. You may also pass following keys:
configurable (false by default): if this is true, the property’s configuration will be modifiable in future.
enumerable (false by default): if true, the property will appear when looping over the object (for (var key in obj)).
We can also define properties that don’t have explicit getters or setters:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
var person = { firstName: 'Jimmy', lastName: 'Smith' }; Object.defineProperty(person, 'fullName', { get: function() { return this.firstName + ' ' + this.lastName; }, set: function(name) { var words = name.split(' '); this.firstName = words[0] || ''; this.lastName = words[1] || ''; } }); // accesses get console.log(person.fullName); person.fullName = "Ricky Tsao" Object.defineProperty(person, 'ssn', { value: 12345678 //writeable defaults to false }); console.log(person.ssn); // get age person.ssn = 29487384728; // try to set age console.log(person.ssn); // age won't change Object.defineProperty(person, 'age', { value: 37, configurable: true, writable: true }); console.log(person.age); // 37 person.age = 18; console.log(person.age); // 18 |
This will create person.age, and set it to the value 42. It’s important to note that this property isn’t writable. Calling person.age = 99 will have no effect. In this way you can create read-only properties. If a property has a value key set, it cannot have a getter or setter. Properties can have values or accessors, not both.
Not only that, but because the enumerable property defaults to false, this property will not appear when we loop over the object’s keys.
If we wanted to make a property writable, we would need to set the writable property:
1 2 3 4 |
Object.defineProperty(person, 'age', { value: 42, writable: true }); |
Example 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
var p = { // x and y are regular read-write data properties. x: 1.0, y: 1.0, // r is a read-write accessor property with getter and setter. // Don't forget to put a comma after accessor methods. get r() { return Math.sqrt(this.x*this.x + this.y*this.y); }, set r(newvalue) { var oldvalue = Math.sqrt(this.x*this.x + this.y*this.y); var ratio = newvalue/oldvalue; this.x *= ratio; this.y *= ratio; } }; p.r = 10; console.log(p.r); console.log(p.x); console.log(p.y); |
output:
10
7.071067811865475
7.071067811865475
Example 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// This object generates strictly increasing serial numbers var serialnum = { // This data property holds the next serial number. // The $ in the property name hints that it is a private property. $n: 0, // Return the current value and increment it get next() { return this.$n++; }, // Set a new value of n, but only if it is larger than current set next(n) { if (n >= this.$n) this.$n = n; else throw "serial number can only be set to a larger value"; } }; console.log(serialnum.$n); // 0 console.log(serialnum.next); // 0 serialnum.next = 2; console.log(serialnum.next); // 2 |
Example 3
1 2 3 4 5 6 7 8 9 |
var random = { get octet() { return Math.floor(Math.random()*256); }, get uint16() { return Math.floor(Math.random()*65536); }, get int16() { return Math.floor(Math.random()*65536)-32768; } }; console.log(random.octet); console.log(random.uint16); console.log(random.int16); |