Good decorator source code to learn the third play
This is the 28th day of my participation in Gwen Challenge
preface
Through the first two articles “good decorator source code learning (a) : Time”, “good decorator source code learning (two)” study
@ Time,@deprecate, @ReadOnly, @Enumerable, @Non64x and other basic decorators
Here’s a look at a few slightly more complex decorators:
@mixin
: Blends methods into classes@lazyInitialize
: Initializes target properties only when they are used
@mixin
Mixing methods from objects into classes
Use the sample
Through @ mixins (obj1 obj2, obj3,..) You can mount properties from the object onto the prototype of the target class
Just mix some common methods with mixins in Vue
import { mixin } from '.. /index'
const obj1 = {
logA() {
console.log(this.a); }}const obj2 = {
printKeys() {
console.log(Object.keys(this)); }}@mixin(obj1, obj2)
class Test {
public a = 1
}
const t: any = new Test()
t.logA() / / 1
t.printKeys() // ['a']
Copy the code
The function structure
Passing in parameters:
- Objs: The rest parameter, which supports passing in multiple objects for mixing
function handleClass(target, mixins) {
if(! mixins.length) {throw new SyntaxError(`@mixin() class ${target.name} requires at least one mixin as an argument`); }}export default function mixin(. objs) {
return function (target) {
return handleClass(target, objs)
}
}
Copy the code
Realize the principle of
- Class decorator first argument
target
Class for identifying decorations - Prototype Identifies the prototype of the class
- Iterates over the incoming Object, passing the
getOwnPropertyNames
withgetOwnPropertyDescriptor
Method gets the target object’s own property (excluding the Symbol property) and the descriptor for the specified property, respectively - Through the
Object.defineProperty
Implementation inClass.prototype
To expand
A complete implementation of handleClass
/** * gets the descriptor */ for each attribute on the object
function getOwnPropertyDescriptors(obj) {
const descs = {};
Object.getOwnPropertyNames(obj).forEach(key= > {
descs[key] = Object.getOwnPropertyDescriptor(obj, key)
})
return descs;
}
function handleClass(target, mixins) {
if(! mixins.length) {throw new SyntaxError(`@mixin() class ${target.name} requires at least one mixin as an argument`);
}
for (let i = 0; i < mixins.length; i++) {
const descs = getOwnPropertyDescriptors(mixins[i])
const keys = Object.getOwnPropertyNames(mixins[i])
for (let j = 0; j < keys.length; j++) {
const key = keys[j];
if(! target.prototype.hasOwnProperty(key)) {Object.defineProperty(target.prototype, key, descs[key])
}
}
}
}
Copy the code
lazyInitialize
Lazy loading of specified properties, that is, the target property is initialized at use time
Use the sample
Put lazy logic into @lazyInitialize
import { lazyInitialize } from "..";
function getMaxArray(str=' '){
console.log(str,'init huge array');
return new Array(100)}class Test{
@lazyInitialize(() = >getMaxArray('a'))
public a
public b = getMaxArray('b')}const t = new Test()
const k = new Test()
k.a
Copy the code
The results
b init huge array
b init huge array
a init huge array
Copy the code
Realize the principle of
- Functions that are initialized using closures to store values
- Modify the property’s GET behavior so that initialization logic is performed when GET is called
- The initialized content is temporarily stored using intermediate variables
- The next time get calls the property, it returns the temporary contents directly
function
function createDefaultSetter(key) {
return function set(newValue) {
Object.defineProperty(this, key, {
configurable: true.writable: true.enumerable: true.value: newValue
});
return newValue;
};
}
export default function lazyInitialize(initializer) :any {
let t
return function (target, key) {
return {
get() {
t = t === undefined? initializer.call(this) : t
return t;
},
set: createDefaultSetter(key)
}
}
}
Copy the code
To be continued
The next one will learn:
@debounce
: if you@throttle
: the throttle