Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/justinfagnani/mixwith.js
A mixin library for ES6
https://github.com/justinfagnani/mixwith.js
Last synced: 4 months ago
JSON representation
A mixin library for ES6
- Host: GitHub
- URL: https://github.com/justinfagnani/mixwith.js
- Owner: justinfagnani
- License: apache-2.0
- Created: 2015-07-13T18:48:27.000Z (almost 9 years ago)
- Default Branch: master
- Last Pushed: 2019-09-04T12:46:08.000Z (almost 5 years ago)
- Last Synced: 2024-03-05T03:48:02.601Z (4 months ago)
- Language: JavaScript
- Size: 46.9 KB
- Stars: 765
- Watchers: 13
- Forks: 58
- Open Issues: 15
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Lists
- awesome-stars - mixwith.js - A mixin library for ES6 (JavaScript)
- awesome-stars - mixwith.js - A mixin library for ES6 (JavaScript)
- awesome-stars - mixwith.js - A mixin library for ES6 (JavaScript)
README
# mixwith.js
A simple and powerful mixin library for ES6.
`mixwith` differs from other mixin approaches because it does not copy properties from one object to another. Instead, `mixwith` works with "subclass factories" which create a new class that extends a superclass with the mixin - this is called a _mixin_ _application_.
#### Example
my-mixin.js:
```javascript
let MyMixin = (superclass) => class extends superclass {
// mixin methods here
};
```my-class.js
```javascript
class MyClass extends MyMixin(MySuperClass) {
// class methods here, go ahead, use super!
}
```### mixwith.js Helpers and Decorators
The subclass factory pattern does not require any support from a library. It's just a natural use of JavaScript class expressions. mixwith.js provides a few helpers that make mixins a little more powerful and easier to use.
mixwith.js makes some use cases very easy:
* Determine if an object or class has had a particular mixin applied to it.
* Cache mixin applications so that a mixin repeatedly applied to the same superclass reuses its resulting subclass.
* De-duplicate mixin application so that including a mixin multiple times in a class hierarchy only applies it once to the prototype type chain.
* Add `instanceof` support to a mixin function.### mix().with()
mixwith.js also provides a little bit of sugar with the `mix()` function that makes applying mixins read a little more naturally:
```javascript
class MyClass extends mix(MySuperClass).with(MyMixin, OtherMixin) {
// class methods here, go ahead, use super!
}
```## Advantages of subclass factories over typical JavaScript mixins
Subclass factory style mixins preserve the object-oriented inheritance properties that classes provide, like method overriding and `super` calls, while letting you compose classes out of mixins without being constrained to a single inheritance hierarchy, and without monkey-patching or copying.
#### Method overriding that just works
Methods in subclasses can naturally override methods in the mixin or superclass, and mixins override methods in the superclass. This means that precedence is preserved - the order is: _subclass_ -> _mixin__1_ -> ... -> _mixin__N_ -> _superclass_.
#### `super` works
Subclasses and mixins can use `super` normally, as defined in standard Javascript, and without needing the mixin library to do special chaining of functions.
#### Mixins can have constructors
Since `super()` works, mixins can define constructors. Combined with ES6 rest arguments and the spread operator, mixins can have generic constructors that work with any super constructor by passing along all arguments.
#### Prototypes and instances are not mutated
Typical JavaScript mixins usually used to either mutate each instance as created, which can be bad for performance and maintainability, or modify a prototype, which means every object inheriting from that prototype gets the mixin. Subclass factories don't mutate objects, they define new classes to subclass, leaving the original superclass intact.
## Usage
### Defining Mixins
The `Mixin` decorator function wraps a plain subclass factory to add deduplication, caching and `instanceof` support:
```javascript
let MyMixin = Mixin((superclass) => class extends superclass {constructor(args...) {
// mixins should either 1) not define a constructor, 2) require a specific
// constructor signature, or 3) pass along all arguments.
super(...args);
}foo() {
console.log('foo from MyMixin');
// this will call superclass.foo()
super.foo();
}});
```Mixins defined with the mixwith.js decorators do not require any helpers to use, they still work like plain subclass factories.
### Using Mixins
Classes use mixins in their `extends` clause. Classes that use mixins can define and override constructors and methods as usual.
```javascript
class MyClass extends mix(MySuperClass).with(MyMixin) {constructor(a, b) {
super(a, b); // calls MyMixin(a, b)
}foo() {
console.log('foo from MyClass');
super.foo(); // calls MyMixin.foo()
}}
```# API Documentation
## apply(superclass, mixin) ⇒
function
Applies `mixin` to `superclass`.`apply` stores a reference from the mixin application to the unwrapped mixin
to make `isApplicationOf` and `hasMixin` work.This function is usefull for mixin wrappers that want to automatically enable
[hasMixin](#hasMixin) support.**Kind**: global function
**Returns**:function
- A subclass of `superclass` produced by `mixin`| Param | Type | Description |
| --- | --- | --- |
| superclass |function
| A class or constructor function |
| mixin |[MixinFunction](#MixinFunction)
| The mixin to apply |**Example**
```js
const Applier = (mixin) => wrap(mixin, (superclass) => apply(superclass, mixin));// M now works with `hasMixin` and `isApplicationOf`
const M = Applier((superclass) => class extends superclass {});class C extends M(Object) {}
let i = new C();
hasMixin(i, M); // true
```## isApplicationOf(proto, mixin) ⇒
boolean
Returns `true` iff `proto` is a prototype created by the application of
`mixin` to a superclass.`isApplicationOf` works by checking that `proto` has a reference to `mixin`
as created by `apply`.**Kind**: global function
**Returns**:boolean
- whether `proto` is a prototype created by the application of
`mixin` to a superclass| Param | Type | Description |
| --- | --- | --- |
| proto |Object
| A prototype object created by [apply](#apply). |
| mixin |[MixinFunction](#MixinFunction)
| A mixin function used with [apply](#apply). |## hasMixin(o, mixin) ⇒
boolean
Returns `true` iff `o` has an application of `mixin` on its prototype
chain.**Kind**: global function
**Returns**:boolean
- whether `o` has an application of `mixin` on its prototype
chain| Param | Type | Description |
| --- | --- | --- |
| o |Object
| An object |
| mixin |[MixinFunction](#MixinFunction)
| A mixin applied with [apply](#apply) |## wrap(mixin, wrapper) ⇒
[MixinFunction](#MixinFunction)
Sets up the function `mixin` to be wrapped by the function `wrapper`, while
allowing properties on `mixin` to be available via `wrapper`, and allowing
`wrapper` to be unwrapped to get to the original function.`wrap` does two things:
1. Sets the prototype of `mixin` to `wrapper` so that properties set on
`mixin` inherited by `wrapper`.
2. Sets a special property on `mixin` that points back to `mixin` so that
it can be retreived from `wrapper`**Kind**: global function
**Returns**:[MixinFunction](#MixinFunction)
- `wrapper`| Param | Type | Description |
| --- | --- | --- |
| mixin |[MixinFunction](#MixinFunction)
| A mixin function |
| wrapper |[MixinFunction](#MixinFunction)
| A function that wraps [mixin](mixin) |## unwrap(wrapper) ⇒
[MixinFunction](#MixinFunction)
Unwraps the function `wrapper` to return the original function wrapped by
one or more calls to `wrap`. Returns `wrapper` if it's not a wrapped
function.**Kind**: global function
**Returns**:[MixinFunction](#MixinFunction)
- The originally wrapped mixin| Param | Type | Description |
| --- | --- | --- |
| wrapper |[MixinFunction](#MixinFunction)
| A wrapped mixin produced by [wrap](#wrap) |## Cached(mixin) ⇒
[MixinFunction](#MixinFunction)
Decorates `mixin` so that it caches its applications. When applied multiple
times to the same superclass, `mixin` will only create one subclass, memoize
it and return it for each application.Note: If `mixin` somehow stores properties its classes constructor (static
properties), or on its classes prototype, it will be shared across all
applications of `mixin` to a super class. It's reccomended that `mixin` only
access instance state.**Kind**: global function
**Returns**:[MixinFunction](#MixinFunction)
- a new mixin function| Param | Type | Description |
| --- | --- | --- |
| mixin |[MixinFunction](#MixinFunction)
| The mixin to wrap with caching behavior |## DeDupe(mixin) ⇒
[MixinFunction](#MixinFunction)
Decorates `mixin` so that it only applies if it's not already on the
prototype chain.**Kind**: global function
**Returns**:[MixinFunction](#MixinFunction)
- a new mixin function| Param | Type | Description |
| --- | --- | --- |
| mixin |[MixinFunction](#MixinFunction)
| The mixin to wrap with deduplication behavior |## HasInstance(mixin) ⇒
[MixinFunction](#MixinFunction)
Adds [Symbol.hasInstance] (ES2015 custom instanceof support) to `mixin`.**Kind**: global function
**Returns**:[MixinFunction](#MixinFunction)
- the given mixin function| Param | Type | Description |
| --- | --- | --- |
| mixin |[MixinFunction](#MixinFunction)
| The mixin to add [Symbol.hasInstance] to |## BareMixin(mixin) ⇒
[MixinFunction](#MixinFunction)
A basic mixin decorator that applies the mixin with [apply](#apply) so that it
can be used with [isApplicationOf](#isApplicationOf), [hasMixin](#hasMixin) and the other
mixin decorator functions.**Kind**: global function
**Returns**:[MixinFunction](#MixinFunction)
- a new mixin function| Param | Type | Description |
| --- | --- | --- |
| mixin |[MixinFunction](#MixinFunction)
| The mixin to wrap |## Mixin(mixin) ⇒
[MixinFunction](#MixinFunction)
Decorates a mixin function to add deduplication, application caching and
instanceof support.**Kind**: global function
**Returns**:[MixinFunction](#MixinFunction)
- a new mixin function| Param | Type | Description |
| --- | --- | --- |
| mixin |[MixinFunction](#MixinFunction)
| The mixin to wrap |## mix([superclass]) ⇒
MixinBuilder
A fluent interface to apply a list of mixins to a superclass.```javascript
class X extends mix(Object).with(A, B, C) {}
```The mixins are applied in order to the superclass, so the prototype chain
will be: X->C'->B'->A'->Object.This is purely a convenience function. The above example is equivalent to:
```javascript
class X extends C(B(A(Object))) {}
```**Kind**: global function
| Param | Type | Default |
| --- | --- | --- |
| [superclass] |function
|Object
|## MixinFunction ⇒
function
A function that returns a subclass of its argument.**Kind**: global typedef
**Returns**:function
- A subclass of `superclass`| Param | Type |
| --- | --- |
| superclass |function
|**Example**
```js
const M = (superclass) => class extends superclass {
getMessage() {
return "Hello";
}
}
```