1. Preface
Traditionally, developers create properties in JavaScript classes for any data that may be needed in an instance. This is not a problem for small chunks of data that are readily available in the constructor. However, if you need to calculate some data before it is available in the instance, you may not want to pay that fee upfront. For example, consider this class:
class MyClass { constructor() { = someExpensiveComputation(); } }
Here,data
Properties are created as the result of performing some expensive calculations. If you are not sure if you will use the property, performing the calculation in advance may be inefficient. Fortunately, there are several ways to delay these operations until later.
2. On-demand attribute mode
The easiest way to optimize the execution of expensive operations is to wait until data is needed before calculating. For example, you can use the accessor attribute with getter to calculate as needed, as follows:
class MyClass { get data() { return someExpensiveComputation(); } }
In this case, until someone reads the first timedata
Your expensive calculations only happen when properties are present, which is an improvement. But, every timedata
The same expensive calculations are performed when the properties are read, which is worse than the previous example, where the calculations are performed at least once. This is not a good solution, but you can create a better solution based on this.
3. Messy delay loading attribute mode
It is a good start to perform calculations only when accessing properties. What you really need is to cache the information after that point and use only the cached version. But where do you cache this information for easy access? The easiest way is to define a property with the same name and set its value to compute data, like this:
class MyClass { get data() { const actualData = someExpensiveComputation(); (this, "data", { value: actualData, writable: false, configurable: false, enumerable: false }); return actualData; } }
Here, thedata
The property is defined again as a getter on the class, but this time it caches the result. Call()
Create a new property nameddata
, this property has a fixed valueactualData
, and set to be non-writable, configurable, and non-enumerable (to match getters). After that, the return value itself. next timedata
When accessing the property, it will read from the newly created property instead of calling the getter:
const object = new MyClass(); // calls the getter const data1 = ; // reads from the data property const data2 = ;
In fact, all calculations are only the first timedata
Completed when reading properties. For thisdata
Each subsequent read of the property returns the cached version.
One disadvantage of this pattern isdata
Attributes start with non-enumerable prototype attributes, and ultimately, their own attributes that are non-enumerable:
const object = new MyClass(); (("data")); // false const data = ; (("data")); // true
While this distinction is not important in many cases, it is important to understand this pattern because it can cause subtle problems when passing objects. Fortunately, using the updated mode is easy to solve this problem.
4. The class's unique delay loading attribute pattern
If you have a use case where lazy loading attributes are always important in the instance, then you can use()
Create attributes in class constructor. It's a little confusing than the previous example, but it ensures that the property only exists on the instance. Here is an example:
class MyClass { constructor() { (this, "data", { get() { const actualData = someExpensiveComputation(); (this, "data", { value: actualData, writable: false, configurable: false }); return actualData; }, configurable: true, enumerable: true }); } }
Here, constructordata
use()
.This property is created on the instance (by usingthis
) and define a getter and specify that the property is enumerable and configurable (typical own property). Willdata
The properties set to configurable are particularly important so that you can()
Call it again.
Then getter function calculates and calls it again()
. Shoulddata
Attributes are now redefined as data attributes with specific values and are not writable and non-configurable to protect the final data. The calculation data is then returned from the getter. next timedata
When a property is read, it will be read from the stored value. As a reward,data
Property now exists only as its own property and acts the same before and after the first reading:
const object = new MyClass(); (("data")); // true const data = ; (("data")); // true
For classes, this is most likely the pattern you want to use; object literals, on the other hand, can be used in a simpler way.
5. Delay loading attribute mode for object literals
If you use object literals instead of classes, the process is much simpler, because the getter defined on the object literals is defined as an enumerable self-property property (rather than a prototype property), just like a data property. This means you can use messy lazy loading attribute patterns for classes without being messy with objects:
const object = { get data() { const actualData = someExpensiveComputation(); (this, "data", { value: actualData, writable: false, configurable: false, enumerable: false }); return actualData; } }; (("data")); // true const data = ; (("data")); // true
6. Conclusion
The ability to redefine object properties in JavaScript provides a unique opportunity to cache information that can be computationally expensive. By starting with an accessor attribute redefined as a data attribute, you can defer calculations until the first time the attribute is read, and then cache the results for later use. This approach works both for classes and object literals, and is a little simpler in object literals, because you don't have to worry about your getter ending on the prototype.
One of the best ways to improve performance is to avoid repeating the same work, so any time you can cache the results for later use, you can speed up your program. Techniques such as lazy loading attribute mode allow any attribute to become a cache layer for improved performance.
The above is a brief discussion on the detailed content of the lazy loading attribute mode in JavaScript. For more information about the JS lazy loading attribute mode, please pay attention to my other related articles!