dcl() is the “class” composition helper, which simplifies creating constructors for new classes of objects:
- It reduces a boilerplate to set up a single inheritance.
- In case of mixin-based multiple inheritance it correctly linearizes all dependencies.
- While composing constructors, it can process advanced features:
- Super calls, when you need to call a method of a super class (a base).
- AOP advices.
- Automatic chaining of arbitrary methods.
Of course, an experienced programmer can do all this stuff manually, but
dcl() offers a less error-prone and more
compact way to achieve what you need. But, just in case, it will play nice with hand-made constructors, so your
constructors can inherit from them, or mix them in as mixins.
1 2 3
Arguments and a return value:
baseis a base “class”. It can be one of three:
null- no base “class” ⇒ base it directly on
- constructor object - a function created with
- array of constructors - a C3 superclass linearization will be performed on this array, and the result will be used to create a new constructor using a single inheritance. This array should be non-empty.
propsis an object, whose properties are used to specify unique features of the “class” — methods, and class-level variables. Following properties have a special meaning for
constructor- an optional function, which will be called when an object is created. A base constructor (if any) is always called before a derived constructor. There is no restrictions on what arguments can be used for a constructor. A return value of constructor (if any) is ignored.
declaredClass- an optional human-readable string, which is used to identify the created class in error messages or logs. See debug.js for more details.
dcl()returns a constructor created according to user’ specifications.
In this case produced “classes” are derived directly from
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
This is a familiar scenario for most programmers: we build on an existing “class” producing a new “class”.
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
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
Multiple inheritance with mixins
The dreaded diamond — the bane of multiple inheritance.
dcl() deals with it with ease:
1 2 3 4 5 6 7 8 9
A triangle will be handled too:
1 2 3 4 5 6 7
Generally a “class”, which inherits from an array, tries to use the first item as its base. In some cases it is not possible due constraints on relative position of all bases. If that is the case, the innermost base class of the first item is used.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
F can be based on
D, our right-most list of dependencies should look like this:
D, B, A, which is not the case.
dcl() cannot preserve this list because
C should go before
B as defined in
E. It forces
dcl() to base
- Constructors are chained using “after” chaining, meaning that a derived constructor will be called only after its base constructor.
- Missing constructors are treated as empty constructors logically.
- All constructors are called with the same set of arguments.
- If a constructor returns a value, it will be ignored.
- All unchained methods (the default), and other properties, override properties with the same name in base classes.
- Always use
newkeyword when creating objects with a constructor produced by
- Methods in
propswill be decorated with a meta information, and possibly copied. Because of that it is not recommended to reuse them for different classes. In general
propsshould be an object literal.
dcl()will assume full control over it.
What is the difference between
dcl.js modules and when should I use
Both mini.js and dcl.js return the same object.
mini.js provides the core functionality
(mixin support, and super calls), while
dcl.js adds chaining, and class-level AOP advices. By default, when you
dcl, it loads
If you application has very tight bandwidth constraints, and doesn’t use advanced features (e.g., small application
targeting mobile browsers), you may want to request
mini.js explicitly (assuming
1 2 3 4
How can I detect, if my class inherits directly or indirectly from
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
dcl() linearize superclasses?
dcl() uses the C3 superclass linearization algorithm. This is the
same proven algorithm that powers Python (since 2.3), Perl 6, Parrot, and Dylan. The best discussion of this algorithm
I know of is in this paper: The Python 2.3 Method Resolution Order.
Effectively this algorithm is a topological sorting of an inheritance graph with complexity O(n).
Why do I need the linearization? I don’t do no “diamonds” in my code.
Unfortunately dependency loops, and reversed dependencies can happen accidentally, especially in big projects, which are
targeted by this OOP package. It is very difficult to keep track of all mixins, and their dependencies, especially when
they are written by other programmers, or even 3rd-party shops.
dcl() keeps track of all these matters for you
linearizing bases, eliminating duplicates, and checking the overall consistency of your “classes”.
Many JS OOP packages skip the correct linearization step hoping that everything will be right as is. Unfortunately it can be a source of extremely hard-to-find bugs. Besides mixins use inheritance routinely to indicate their relative dependencies so duplicates will occure naturally.
I write only small programs. I don’t need no linearization, right?
If you write only small programs, chances are you don’t need OOP. See discussion of the OOP applicability area in OOP and JS, specifically “fail #2”.
Can I use hand-made constructors as bases or mixins with
Yes, you can. It was demonstrated in the code example above. Obviously your hand-made classes cannot use
facilities like super calls, or class-level advices, but other than that they can be used without restrictions.
Is it possible to chain methods other than constructor?
Is it possible to use advices with constructors?
Yes. The full set of advices can be used with constructors.
Is it possible to “break” a chain of constructors or other chained methods?
While it is not advised due to possible violation of object invariants, and potential maintenance problems, you can do it with super calls — just define a super call and doesn’t call a super.
See superCall() for more details.
Can I use
 instead of
null as my base?
No, it wouldn’t work.
In any case
null is a constant, which is shared, while
 is a newly-created object. The latter comes with
a penalty (it has to be created, and it will add a load to the garbage collector afterwards.
null is cheaper, and
clearly demonstrates programmer’s intent.
Can I use
[base] instead of
base as my base?
Yes, but why? The former will create an additional array object, which will be discarded right after the
increasing the load on the garbage collector. The latter is clearly cheaper, and more intentional.