-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
/
Class.js
118 lines (92 loc) 路 2.92 KB
/
Class.js
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import * as Util from './Util.js';
// @class Class
// @aka L.Class
// @section
// @uninheritable
// Thanks to John Resig and Dean Edwards for inspiration!
export class Class {
// @function extend(props: Object): Function
// [Extends the current class](#class-inheritance) given the properties to be included.
// Returns a Javascript function that is a class constructor (to be called with `new`).
static extend({statics, includes, ...props}) {
const NewClass = class extends this {};
// inherit parent's static properties
Object.setPrototypeOf(NewClass, this);
const parentProto = this.prototype;
const proto = NewClass.prototype;
// mix static properties into the class
if (statics) {
Util.extend(NewClass, statics);
}
// mix includes into the prototype
if (includes) {
Util.extend.apply(null, [proto].concat(includes));
}
// mix given properties into the prototype
Util.extend(proto, props);
// merge options
if (proto.options) {
proto.options = parentProto.options ? Object.create(parentProto.options) : {};
Util.extend(proto.options, props.options);
}
proto._initHooks = [];
return NewClass;
}
// @function include(properties: Object): this
// [Includes a mixin](#class-includes) into the current class.
static include(props) {
const parentOptions = this.prototype.options;
Util.extend(this.prototype, props);
if (props.options) {
this.prototype.options = parentOptions;
this.mergeOptions(props.options);
}
return this;
}
// @function mergeOptions(options: Object): this
// [Merges `options`](#class-options) into the defaults of the class.
static mergeOptions(options) {
Util.extend(this.prototype.options, options);
return this;
}
// @function addInitHook(fn: Function): this
// Adds a [constructor hook](#class-constructor-hooks) to the class.
static addInitHook(fn, ...args) { // (Function) || (String, args...)
const init = typeof fn === 'function' ? fn : function () {
this[fn].apply(this, args);
};
this.prototype._initHooks = this.prototype._initHooks || [];
this.prototype._initHooks.push(init);
return this;
}
_initHooksCalled = false;
constructor(...args) {
Util.setOptions(this);
// call the constructor
if (this.initialize) {
this.initialize(...args);
}
// call all constructor hooks
this.callInitHooks();
}
callInitHooks() {
if (this._initHooksCalled) {
return;
}
// collect all prototypes in chain
const prototypes = [];
let current = this;
while ((current = Object.getPrototypeOf(current)) !== null) {
prototypes.push(current);
}
// reverse so the parent prototype is first
prototypes.reverse();
// call init hooks on each prototype
for (const proto of prototypes) {
for (const hook of proto._initHooks ?? []) {
hook.call(this);
}
}
this._initHooksCalled = true;
}
}