PLEASE REFER TO THE FULL DOCUMENTATION ON THE PROTYPES HOMEPAGE!
Information provided in this README has illustrative purpose only, is limited and doesn't describe API of this library in details.
protypes
is: Joins Multiple Implementations Into A Single Class.
yarn add protypesnpm i protypes
Table of Contents
- Table of Contents
- What Is Protype?
- API
subtype(Target, opts, ...(Protype|Class))
is(obj, protypeName)
- Copyright & License
What Is Protype?
JavaScript is prototype-based language. Prototypes are objects from which other objects are created, for example, using Object.create()
. Since introduction of classes in ES5 standard, the programming was largely class-based, however there's another possibility: via protypes.
A protype is the type of prototypes. It's like a class but simpler, denoted in a standard object. Class-based approach is really a syntactic sugar over prototypes. There are limitations to the standard hierarchical model, such that larger systems can not be adequately expressed in terms of gradually refining implementations of specialising interfaces, and types need to be subtyped via alternative routes, that is, protypes, which simply allow to copy methods into objects without vertical inheritance.
// Standard class-based model: do console { console } // `MyClass` can inherit from `Doer`, but// how can we make it extend `Writer`, too? {} // Absence of multiple inheritance// is a limitation of JavaScript// solved by *Protypes*.
There's no support for multiple inheritance in JS, so the code reuse for types that specialise in multiple things isn't possible without a dynamic solution such that is provided by this library. Protypes allows to subtype types from their supertypes, i.e. to inherit methods horizontally rather than vertically:
const Doer = // <- protype do console const Writer = // <- protype { console } {} // <- target class const c = cc
doing work
hello world
As you can see, protypes is a more natural approach to prototype-based programming that is facilitated by JavaScript. Only because of the lack of theoretical background in programming and the mainstream approach to development that involves classes, they've been used very rarely. But once you discover their utility, you will fully realise their potential. Plus, nobody is taking classes away, and protypes can be mixed in any class with its hierarchical inheritance structure. When it becomes illogical to work "in depth", then protypes with their "in width" approach will come to the rescue.
API
The package is available by importing its named functions and symbols:
They are briefly described on this page, but the full description is found in the official documentation.
subtype(Target, opts, ...(Protype|Class))
Subtyping is a feature of OOP which allows objects of one type to be accepted in contexts expecting another type (class) [Sny86]. If you ever used down-casting, you were using this feature. It is also the basis for polymorphism. Just see the example below to immediately understand what subtyping is:
do console {} /** * The main function. * @param */ { doer} const c =
We defined a class, Doer, and implemented the do
method in it. We also declared a function main
that accepts a Doer as its argument. We then proceed to create a new instance of a MyClass which extends the Doer class. And although the function main
receives this instance, it's still type-sound since the instance is a subtype of Doer (which is a supertype of MyClass). This is what subtyping is.
doing work
When we want to extend classes with protypes (or other classes), we're subtyping the target class by those protypes (or classes). In formal notation, it would be written as Target <: Protype
(Subtype <: Supertype
). The target can thus become a subtype of any number of other types. Objects created via such class constructor, will inherit all methods from all its supertypes. Inheritance doesn't necessary mean standard top-down sharing of methods via the extends
keyword, but can also mean horizontal borrowing of methods from protypes.
Source | Output |
---|---|
|
|
The subtype accepts the target class as the first argument, some options (if no options are required, null
needs to be passed), and then the list of all protypes and classes that need to be subtyped. Their methods, getters, setters and even static data fields will be copied across.
There are some nuances that need to be considered, such as using super keyword, which is actually statically bound to the prototype's instance, and merging getters and setters. You can follow the links to read about these intricacies on the package's documentation website.
Options
The list of options that can be used with the subtype
method includes:
setProtypesPrototype: Protypes will usually come in form of simple objects. To be able to use super.
in their methods, their prototype needs to be set to the target's, but this can be done only once since super
is statically bound. Show example of setting prototypes of protypes.
{ console } {}let Protype = { supermethod }let t = try t catch err console// fix super {}t = t
(intermediate value).method is not a function
parent method
[paid
] methodDecorators: An array of decorators that can wrap methods which are being assigned to the prototype of the target class. Show example of method decorators.
{}let Protype = { console }const decorator = { return { console const res = method console return res }}const t = t
pre-condition on run
running protype with hello
post-condition on run
[paid
] mergeGettersSetters: When a class or a protype specifies only a getter or setter without its counter-part, the inheritance will be broken. This option allows to look up missing getters/setters in the prototype chain of the target. Show example.
Without Merging | With Merging |
---|---|
|
|
[paid
] fixTargetGettersSetters: Similar to the previous option, but applies to the target itself. Can be applied before or after assignment of protypes/classes. Click to preview example code.
Without Fix | With Fix |
---|---|
|
|
[paid
] bindings: Allows to set bindings on method's class automatically, so that they can be destructured and still access the this
keyword inside of them.
{ return 'target' } { console } let t = const example test = t
test: target
example: protype, target
is(obj, protypeName)
To determine whether the given instance's class is Proto|Class or a subclass of Proto|Class. In plain JS, we'd use instanceof
however with multiple inheritance / subtyping this does not work as the methods are copied across into the target class. The is
method from this library can be used to determine whether a Protype (or Class) has been implemented by the given object.
Source | Output |
---|---|
|
|
The check is performed on strings currently, but in the future version it'll be extended to passing of actual protypes. The reason for this limitation is that with non-string based implementation of the is
method, chances are high you might run into circular dependencies that would break the system. The name
must be defined on protypes and classes using the special $name
symbol exported by the library.
Copyright & License
The public free version of the package is provided under the GNU Affero General Public License v3.0 so that you can use it in AGPL-compatible projects. The full version requires purchasing of a license key and is distributed under an EULA terms. Please contact Art Deco on Keybase chat to purchase the key.
© Art Deco™ 2020 |
---|