Are `getter` and `setter` necessary in JavaScript?
Are
getter
andsetter
necessary in JavaScript?
Necessary is a bit of an undefined word. Any problem can be solved without a getter and setter (usually by changing the interface to methods instead of direct property access), just like any problem can be solved without a for
loop (by substituting a while
loop and manually maintained loop variables), but both are useful features to have in a programming language because when they are a match for a use case, then they are the cleaner and more convenient way to program. Thus, they are "useful" and "helpful".
A getter
or setter
can be very useful at times, but they only need to be used when their specific functionality is required - otherwise plain property access without a getter
or setter
can be just fine.
A getter
has use when you want to run some code every time a property is requested. In your example, the getter
always returns an uppercase version of the name regardless of what the case of the name is, while reserving the actual case for internal use.
A setter
has use when you want to run some code every time a property is set. In your case, it prevents the setting of a falsey name. You can't implement either of those features without a getter
/setter
.
Direct property access is a perfectly fine way to do things when you don't need getter
or setter
special logic.
It's also possible that getters
and setters
may get and set some property that is not publicly available (so not available at all via a normal property access), either because it's stored somewhere differently (not as a property on this object) or stored privately or even that it's stored else such as in some hardware device. In these cases, the getter
and setter
simulate the value being in a property when it actually isn't. So, they simplify the interface while allowing the actual value to be stored or retrieved from anywhere.
Necessary? No.
Are there scenarios that would best be expressed as a getter/setter? Yes. Consider "Computed Properties":
//declare an object
rectangle = {
x: 10,
y: 20,
get area() { return this.x * this.y } //won't occupy storage
}
//usage
log('the area is', rectangle.area) //=> 'the area is 200'.
// cleaner syntax (no parentheses)
Consider the needs of API designers - most likely they'll be hyper-sensitive about how users perceive their API. Every bit (or in this case, parentheses) counts towards cleaning up the interface.
Yes they are very necessary. Just not for every property.
Here are two reasons to have setters:
1. You need to validate the incoming value.
Let's say that you have a value that must be an integer between 0 and 100. Your setter can validate that the incoming value is of the correct type and within correct range:
class Range {
set value(newVal) {
let val = Number(newVal);
if( newVal == null || typeof newVal === 'string' || !Number.isInteger(val) || val < 0 || val > 100 ) {
const err = `'value' must be an integer from 0 to 100 and not ${newVal}`;
console.error(err);
//throw new TypeError(err);
}
// save newVal
}
}
const x = new Range();
x.value = 200;
x.value = 10;
x.value = "10";
x.value = new Number(22);
2. You need to do something every time a value changes
class ValOutput {
constructor(el) {
this._el = el;
}
set value(newVal) {
this._el.innerHTML = `The value is ${newVal}`;
}
}
const output = document.getElementById('output');
const x = new ValOutput(output);
x.value = 100;
setTimeout(() => {
x.value="after timeout";
}, 2000);
<div id="output"></div>
Here are two reasons to have a getter:
1. The value is computed
class Rect {
constructor(l = 0,t = 0,r = 0,b = 0) {
this.left = l;
this.top = t;
this.right = r;
this.bottom = b;
}
get height() {
return this.bottom - this.top;
}
get width() {
return this.right - this.left;
}
}
let a = new Rect(0, 10, 33, 55);
console.log(a.width, a.height);
a = new Rect(35, 50, 200, 200);
console.log(a.width, a.height);
2. You are proxying a value from another object
class OtherThing {
constructor(a) {
this.a = a;
}
}
class MyObj {
constructor(oVal = 0) {
this._otherThing = new OtherThing(oVal);
}
get a() {
return this._otherThing.a;
}
}
const x = new MyObj();
console.log(x.a);
const y = new MyObj('tacos');
console.log(y.a);
Hiding private variables
getters
and setter
are a great way to hide private data. Yes I know that the ES spec is introducing private variable but here is an example that works until that spec is standard.
const privateVars = new WeakMap();
class MyObj {
constructor(inVal) {
const pVars = {
age: inVal,
dog: ''
}
privateVars.set(this, pVars);
}
get dog() {
return privateVars.get(this).dog;
}
set dog(newVal) {
privateVars.get(this).dog = newVal;
}
}
const x = new MyObj(10);
const y = new MyObj(20);
x.dog = "woof";
y.dog = "bark";
console.log(x.dog);
console.log(y.dog);
Do you need getter
and setters
? No. Are they necessary? Yes.
One of the main reason the getter and setter feature was implemented was to close a feature gap that required people to hack a js interpreter/browser to achieve one feature that browsers could do:
element.innerHTML = "<div> some HTML string </dif>";
Now, innerHTML
may look like a property but it actually behaves more like a function. It's actually an HTML compiler.
You can see this by trying to do:
element.innerHTML += "<table>";
element.innerHTML += "<tr><td>test</td></tr>"; // does not work
This is because the first "call" to innerHTML
compiles the html to:
<table></table>
The second line will fail because <tr>
outside of a table is invalid HTML.
With getter and setter you can finally implement a feature like this in pure javascript - make property access trigger a function call.
Of course, innerHTML
is just one example of getters and setters and a very bad example of an API at that. It's up to your creativity what you can actually do with getters and setters.
Now, for my personal opinion (and it's only my opinion so take it for what it's worth): I think innerHTML
provides a good example for why getter and setters should not normally be used. The confusion over <table>
is one good example of breaking user's expectation. If innerHTML
was implemented as element.appendHTML()
it's behavior would be less surprising. As a programmer I'd be very surprised if a property access does something I don't expect.
That said, I am glad that getters and setters exist to make language library features self-implementable without needing to resort to hacking in C/C++.