The aim of this series is to show you how to program faster by writing less code. In part one you get an overview what I mean by writing less code, part 2 shows how destructuring works, how you can use the arrow function and default Params to reduce code. Here in part 3 you’ll learn more about magic getters/setters and the Variable Function Names.
Magic Getter/Setter (ES6)
ES6 knows – like some other languages – magic getters and setters. That’s how they work:
class Person { constructor() { this.data = {}; } get first() { return this.data.first || 'UNKNOWN'; }; set first(name) { this.data.first = name; }; } var p = new Person(); console.log(p.first); // UNKNOWN p.first = 'Hugo'; console.log(p.first); // Hugo
You can remove and add (and so change) the getters and setters at runtime! This enables some kind of clever self-modifying code. You can use it for lazy-loading or other types of clever caches without cache-miss errors. In this example we calculate something only once and only as getter:
class Person { constructor(data) { this.data = data; } get first() { console.log('The Getter is called'); // calculate or load complex stuff here: var calculatedValue = this.data.first || 'UNKNOWN'; Object.defineProperty(this, 'first', { value: calculatedValue }); return calculatedValue; }; } var p = new Person( {first: ''} );
> console.log(p.first); // initial call The Getter is called UNKNOWN > console.log(p.first); // second call UNKNOWN
What does this?
I call it a one-time lazy loading cache. When calling the getter the first time, it calculates or loads some complex stuff (line 9) – something which takes some time to compute, not just this simple code. Then we replace the getter by the calculated value using defineProperty(). That replaces the getter with the value of what we have calculated. And to end this function up we return that value.
For the second call, the getter has been overwritten with the value, so ES6 takes the value and returns it. Which creates a very simple one-time-cache, useful for such cases, where you need the cached value either never or many times.
Some of my colleagues don’t like it, and they are right. Such constructs are a typical case which needs to be discussed in a team. Because it has pros and cons. Pro: it‘s not possible to write a one-time lazy loading cache much smaller, but still readable. Con: it has some unexpected behavior. Like you cannot set the value from outside, even if you define it publicly, because the prototype is by default not writeable. See more about object prototypes.
Variable Function Names
JavaScript enables more strange things. Names of properties can be expressions. We can combine that with magic getter/setter:
// you need to define the expression, before you define the function firstname = 'surname'; class Person { constructor() { this.data = {}; } // this is the value of the variable firstname, which is surname get [firstname]() { console.log('The Getter is called'); return this.data.first || 'UNKNOWN'; }; set [firstname](name) { console.log('The Setter is called'); this.data.first = name; }; } var p = new Person();
> console.log(p.firstname); undefined > console.log(p.surname); The Getter is called UNKNOWN > p.surname = 'Hugo'; The Setter is called 'Hugo' > p.first = 'Gerd'; 'Gerd' > console.log(p.surname); The Getter is called Hugo > console.log(p.first); Gerd
Define propper public/private variables because otherwise it may become quite confusing.
A better use-case is in TypeScript, that you can walk through the interface definition. So this is used as generic (data) mapper or generator. Like mapping the name and shipping address of Person to some Basket. You need just a list of left-to right-side-values. Something like that may be used in an ORM (and so normally hidden from you as a programmer). But for simpler tasks, this might be the right grade of complexity.
I discussed that in conjunction with the GDPR. The methods I show here can be used to guarantee that no data ever gets out of control. The more basic question here is if JavaScript is the right language for something like that.
This examplealso shows that self-modifying code is not always bad. It would be foolish not to use such techniques today, if useful (see part 1 about when it is useful and when not).
But I hope you have learned also, that you should never write such code alone. Try to explain the problem you want to solve to your team. Then try to explain your solution and discuss it with the goal to find a better solution. This is the most effective way to keep the code at an optimum between maintainability and complexity. Which gives me the cue for part 4 that will be about other unconventional techniques besides pure coding.
Schreibe einen Kommentar