The concatenative implementation isn’t there yet, I’ll add it soon. The only bad performance hit that it has is during class definition, with the use of Object.getOwnPropertyDescriptor and Object.defineProperty, which is a little heavier than direct assignment, but other than that it just uses the constructor pattern with no closures, so it’s exactly like writing the constructor pattern manually.
Indeed, that is cool! I believe I can borrow some ideas for lowclass. stampit().enclose()
is interesting. So basically what that’s doing is letting you specify multiple constructor functions instead of just a single one. So constructor/class C can be the combination of constructors/classes A and B (composed). The downside compared to the prototype constructor pattern is that each instance has a new copy of all the methods, so if you have 100 instances, you’d be using 100 times more memory to store the methods than in 100 instances defined with the constructor pattern. F.e., in this example
var availability = stampit().enclose(function () {
var isOpen = false; // private
return stampit.extend(this, {
open: function open() {
isOpen = true;
return this;
},
close: function close() {
isOpen = false;
return this;
},
isOpen: function isOpenMethod() {
return isOpen;
}
});
});
each instance will contain new definitions of the same methods, defined during construction rather than on the prototype. It’s nice what benefits we get in terms of “private” vars, but with the huge expense of memory.
Well, i guess that’s just me being opinionated, and basically promoting the use of classical OO (f.e. Class ('Foo') .extends(Bar)
). The lack of private vars in the ES5 constructor pattern and ES6 class syntax (which is what I mean by “pure ES5 or ES6 classes”) will reduce memory by sharing a single method instance across all object instances as well as save some CPU because instance methods won’t need to be re-defined during every instance construction, hence is probably the reason that
Side note! Since we’re supporting IE 11 and up, we can have private vars in the ES5 constructor pattern or with ES6 classes now with WeakMap
, but it won’t work with IE10-. Here’s an example of ES6 classes with private vars (the same applies to ES5 constructor pattern):
let privates = new WeakMap() // keys in the WeakMap must be Objects.
class Foo {
constructor() {
// private properties are stored in the weakmap.
privates.set(this, {
foo: 'foo'
})
}
someMethod() {
// we can access the private properties in another *prototype* method,
// which isn't possible with the closure pattern of stampit.enclose,
// etc.
console.log(privates.get(this).foo)
}
}
I like classical OO, but I believe we can still take advantage of things like mixins/compose. I had added a .mixin
method to lowclass, but removed it before publishing, but it looked like this:
Class ('Foo') .compose (Bar, Baz) .extends (Lorem, {
...
})
What that would do is just mix Bar
and Baz
onto the new class’ prototype before finally mixing in the supplied body literal onto the new class’ prototype created from Lorem
.
The interesting thing about WeakMaps and private vars would be that when you mixin different classes defined in different modules, each class would have it’s own private collection, which may not behave as intended.
F.e. Suppose the following class extends the Foo class from my previous example:
let privates = new WeakMap()
Class ('Bar') .extends (Foo, {
constructor() {
super()
// private properties are stored in a second weakmap, not the same one
// as Foo! So the private weakmap from the Foo module is not
// inherited.
privates.set(this, {
foo: 'bar'
})
}
someMethod() {
super.someMethod()
// not the same `foo`!
console.log(privates.get(this).foo)
// output shows "foo" and "bar".
}
})
It’s per-instance privates, but on each level of the class hierarchy. We could fix this problem by storing a reference to some hierarchy-wide WeakMap in a separate module and importing it, if that’s what we want, which would be like “protected” members in Java.
@talves I’m not claiming that this classical pattern with private variables in WeakMaps is better, but just putting it out there so we can weigh our options. I’m just jotting down ideas.
Here’s a thought: if we store a single WeakMap in some module, and import it into every class, that’d essentially be like “private” members for the top-level object of every instance anywhere in an app that uses such a library, except that a user could also import that WeakMap reference and get the “privates” of any object, but it just might be better than prefixing local proeprties with underscores (f.e. this._somePrivateProperty
). A use would need to import the WeakMap, then privates.get(someInstance).somePrivateProperty
which is at least a little more warning-ful that danger may ensue from messing with the privates.
What can we gleen from that? This: “protected members” are still not possible in ES6 without leaking information to the user.
Okay, less writing, more coding! I’m going to make my prototype using mostly classical OO (using concatenative classes wherever possible, falling back to constructor pattern prototypes as minimally as possible or when memory is exhausted), with optional mixins/compositions.