Class.js
2.47 KB
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
/* Example usage:
var UIComponent = Class(function() {
this.init = function() { ... }
this.create = function() { ... this.createDOM() ... }
})
var PublisherMixin = {
init: function(){ ... },
publish_: function() { ... }
}
var Button = Class(UIComponent, PublisherMixin, function(supr) {
this.init = function(opts) {
// call UIComponents init method, with the passed in arguments
supr(this, 'init', arguments) // or, UIComponent.constructor.prototype.init.apply(this, arguments)
this.color_ = opts && opts.color
}
// createDOM overwrites abstract method from parent class UIComponent
this.createDOM = function() {
this.getElement().onclick = bind(this, function(e) {
// this.publish_ is a method added to Button by the Publisher mixin
this.publish_('Click', e)
})
}
})
*/
module.exports = function Class(/* optParent, optMixin1, optMixin2, ..., proto */) {
var args = arguments,
numOptArgs = args.length - 1,
mixins = []
// the prototype function is always the last argument
var proto = args[numOptArgs]
// if there's more than one argument, then the first argument is the parent class
if (numOptArgs) {
var parent = args[0]
if (parent) { proto.prototype = parent.prototype }
}
for (var i=1; i < numOptArgs; i++) { mixins.push(arguments[i]) }
// cls is the actual class function. Classes may implement this.init = function(){ ... },
// which gets called upon instantiation
var cls = function() {
if(this.init) { this.init.apply(this, arguments) }
for (var i=0, mixin; mixin = mixins[i]; i++) {
if (mixin.init) { mixin.init.apply(this) }
}
}
// the proto function gets called with the supr function as an argument. supr climbs the
// inheritence chain, looking for the named method
cls.prototype = new proto(function supr(context, method, args) {
var target = parent
while(target = target.prototype) {
if(target[method]) {
return target[method].apply(context, args || [])
}
}
throw new Error('supr: parent method ' + method + ' does not exist')
})
// add all mixins' properties to the class' prototype object
for (var i=0, mixin; mixin = mixins[i]; i++) {
for (var propertyName in mixin) {
if (!mixin.hasOwnProperty(propertyName) || propertyName == 'init') { continue }
if (cls.prototype.hasOwnProperty(propertyName)) {
throw new Error('Mixin property "'+propertyName+'" already exists on class')
}
cls.prototype[propertyName] = mixin[propertyName]
}
}
cls.prototype.constructor = cls
return cls
}