2019-07-16 23:49:32 +03:00
|
|
|
# object.js
|
|
|
|
|
|
2020-05-07 03:39:21 +03:00
|
|
|
_object.js_ is a set of tools and abstractions to create and manage
|
|
|
|
|
constructors, objects and prototype chains in idiomatic JavaScript.
|
2019-07-16 23:49:32 +03:00
|
|
|
|
2020-04-25 01:14:00 +03:00
|
|
|
This is an alternative to the ES6 `class` syntax in JavaScript and provides
|
2019-07-16 23:49:32 +03:00
|
|
|
several advantages:
|
2020-05-07 03:40:23 +03:00
|
|
|
- _Uniform and minimalistic_ definition "syntax" based on basic JavaScript
|
2020-05-07 03:39:21 +03:00
|
|
|
object literals. No special cases, special syntax or _"the same but slightly
|
2020-05-08 16:00:00 +03:00
|
|
|
different"_ ways to do things, trying to adhere to
|
|
|
|
|
[POLS](https://en.wikipedia.org/wiki/Principle_of_least_astonishment)
|
|
|
|
|
as much as possible,
|
2020-04-26 21:43:00 +03:00
|
|
|
- _Transparently_ based on JavaScript's prototypical inheritance model,
|
2020-05-07 03:39:21 +03:00
|
|
|
- Produces fully introspectable constructors/instances,
|
2020-05-07 03:41:05 +03:00
|
|
|
- Does not try to emulate constructs foreign to JavaScript (i.e. classes),
|
2020-05-06 19:00:39 +03:00
|
|
|
- Granular 2-stage instance construction and initialization (a-la
|
|
|
|
|
_Python's_ `.__new__(..)` and `.__init__(..)` methods),
|
2020-04-26 21:43:00 +03:00
|
|
|
- Simple way to define callable instances (including a-la _Python's_
|
2020-05-06 13:45:54 +03:00
|
|
|
`.__call__(..)`),
|
2020-04-26 21:43:00 +03:00
|
|
|
- Less restrictive:
|
2020-05-06 13:45:54 +03:00
|
|
|
- `new` is optional,
|
2020-05-06 19:00:39 +03:00
|
|
|
- all input components are reusable JavaScript objects,
|
2020-05-06 13:45:54 +03:00
|
|
|
- no artificial restrictions.
|
2019-07-16 23:49:32 +03:00
|
|
|
|
|
|
|
|
Disadvantages compared to the `class` syntax:
|
2020-05-06 13:45:54 +03:00
|
|
|
- No _syntactic sugar_,
|
|
|
|
|
- Slightly more complicated calling of `parent` (_super_) methods.
|
2019-07-16 23:49:32 +03:00
|
|
|
|
2020-05-06 13:45:54 +03:00
|
|
|
Note that the produced constructors and objects are functionally
|
|
|
|
|
identical (almost) to the ones produced via ES6 classes and are
|
|
|
|
|
interchangeable with them.
|
|
|
|
|
|
2020-05-06 03:15:39 +03:00
|
|
|
|
|
|
|
|
Here is a basic comparison:
|
2020-05-06 14:30:32 +03:00
|
|
|
<table border="0" width="100%">
|
2020-05-06 02:37:08 +03:00
|
|
|
<tr valign="top">
|
2020-05-06 03:15:39 +03:00
|
|
|
<td width="50%">
|
2020-05-06 02:37:08 +03:00
|
|
|
|
|
|
|
|
_object.js_
|
|
|
|
|
```javascript
|
2020-05-06 13:11:54 +03:00
|
|
|
var A = object.Constructor('A', {
|
|
|
|
|
// prototype attribute (inherited)...
|
|
|
|
|
attr: 'prototype',
|
2020-05-06 02:37:08 +03:00
|
|
|
|
|
|
|
|
method: function(){
|
2020-05-06 13:11:54 +03:00
|
|
|
// ...
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
var B = object.Constructor('B', A, {
|
|
|
|
|
constructor_attr: 'constructor',
|
|
|
|
|
|
|
|
|
|
constructor_method: function(){
|
2020-05-06 02:37:08 +03:00
|
|
|
return 'constructor'
|
|
|
|
|
},
|
|
|
|
|
}, {
|
|
|
|
|
get prop(){
|
|
|
|
|
return 42 },
|
|
|
|
|
|
|
|
|
|
__init__: function(){
|
|
|
|
|
this.instance_attr = 7
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-06 13:56:14 +03:00
|
|
|
- No _direct_ way to do "private" definitions,
|
|
|
|
|
- Clear separation of constructor and `.prototype`
|
|
|
|
|
For example, in `B`:
|
2020-05-06 13:11:54 +03:00
|
|
|
- First block (optional) is merged with `B`,
|
|
|
|
|
- Second block _is_ the `B.prototype`,
|
2020-05-07 03:29:23 +03:00
|
|
|
- No special syntax, stands out less.
|
2020-05-06 03:15:39 +03:00
|
|
|
|
2020-05-06 02:37:08 +03:00
|
|
|
</td>
|
2020-05-06 03:15:39 +03:00
|
|
|
<td>
|
2020-05-06 02:37:08 +03:00
|
|
|
|
|
|
|
|
_ES6_
|
|
|
|
|
```javascript
|
2020-05-06 13:11:54 +03:00
|
|
|
class A {
|
|
|
|
|
// instance attribute (copied)...
|
|
|
|
|
attr = 'instance'
|
|
|
|
|
|
|
|
|
|
method(){
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class B extends A {
|
2020-05-06 02:37:08 +03:00
|
|
|
static constructor_attr = 'class'
|
|
|
|
|
|
2020-05-06 13:11:54 +03:00
|
|
|
static constructor_method(){
|
2020-05-06 02:37:08 +03:00
|
|
|
return 'class'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get prop(){
|
|
|
|
|
return 42 }
|
|
|
|
|
|
|
|
|
|
constructor(){
|
|
|
|
|
super(...arguments)
|
|
|
|
|
|
|
|
|
|
this.instance_attr = 7
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
2020-05-06 13:18:36 +03:00
|
|
|
- Syntax pretty but _misleading_;
|
2020-05-06 14:30:32 +03:00
|
|
|
calling a _constructor_ a class is not correct,
|
|
|
|
|
- `static` and instance definitions are not separated,
|
|
|
|
|
- lots of details done non-transparently under the hood.
|
2020-05-06 02:37:08 +03:00
|
|
|
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table>
|
|
|
|
|
|
2019-07-16 23:49:32 +03:00
|
|
|
|
2020-05-06 03:15:39 +03:00
|
|
|
|
2020-05-04 01:16:48 +03:00
|
|
|
## Contents
|
|
|
|
|
- [object.js](#objectjs)
|
|
|
|
|
- [Contents](#contents)
|
|
|
|
|
- [Installation](#installation)
|
|
|
|
|
- [Basic usage](#basic-usage)
|
|
|
|
|
- [Inheritance](#inheritance)
|
|
|
|
|
- [Callable instances](#callable-instances)
|
2020-05-06 05:30:11 +03:00
|
|
|
- [Mix-ins](#mix-ins)
|
2020-05-06 05:44:05 +03:00
|
|
|
- [Advanced usage](#advanced-usage)
|
2020-05-04 01:16:48 +03:00
|
|
|
- [Low level constructor](#low-level-constructor)
|
|
|
|
|
- [Extending the constructor](#extending-the-constructor)
|
|
|
|
|
- [Inheriting from native constructor objects](#inheriting-from-native-constructor-objects)
|
|
|
|
|
- [Extending native `.constructor(..)`](#extending-native-constructor)
|
2020-08-01 22:05:46 +03:00
|
|
|
- [Special methods](#special-methods)
|
|
|
|
|
- [`<object>.__new__(..)`](#object__new__)
|
|
|
|
|
- [`<object>.__init__(..)`](#object__init__)
|
|
|
|
|
- [`<object>.__call__(..)`](#object__call__)
|
2020-05-04 01:16:48 +03:00
|
|
|
- [Components](#components)
|
2020-05-24 00:37:52 +03:00
|
|
|
- [`STOP`](#stop)
|
2020-05-06 14:54:16 +03:00
|
|
|
- [`sources(..)`](#sources)
|
2020-05-23 17:02:37 +03:00
|
|
|
- [`values(..)`](#values)
|
2020-05-06 14:54:16 +03:00
|
|
|
- [`parent(..)`](#parent)
|
|
|
|
|
- [`parentProperty(..)`](#parentproperty)
|
|
|
|
|
- [`parentCall(..)`](#parentcall)
|
2020-08-09 14:05:30 +03:00
|
|
|
- [`parentOf(..)` / `childOf(..)` / `related(..)`](#parentof--childof--related)
|
2020-05-06 14:54:16 +03:00
|
|
|
- [`mixin(..)`](#mixin)
|
2020-05-07 21:20:20 +03:00
|
|
|
- [`mixins(..)`](#mixins)
|
|
|
|
|
- [`hasMixin(..)`](#hasmixin)
|
2020-05-06 14:54:16 +03:00
|
|
|
- [`mixout(..)`](#mixout)
|
|
|
|
|
- [`mixinFlat(..)`](#mixinflat)
|
2020-05-08 18:36:51 +03:00
|
|
|
- [`RawInstance(..)`](#rawinstance)
|
2020-05-06 14:54:16 +03:00
|
|
|
- [`Constructor(..)` / `C(..)`](#constructor--c)
|
2020-05-04 01:16:48 +03:00
|
|
|
- [Utilities](#utilities)
|
2020-08-02 20:29:45 +03:00
|
|
|
- [`normalizeIndent(..)` / `normalizeTextIndent(..)` / `doc` / `text`](#normalizeindent--normalizetextindent--doc--text)
|
2020-08-01 22:05:46 +03:00
|
|
|
- [`deepKeys(..)`](#deepkeys)
|
2020-05-07 21:20:20 +03:00
|
|
|
- [`match(..)`](#match)
|
2020-08-01 22:05:46 +03:00
|
|
|
- [`matchPartial(..)`](#matchpartial)
|
2020-05-04 01:16:48 +03:00
|
|
|
- [Limitations](#limitations)
|
|
|
|
|
- [Can not mix unrelated native types](#can-not-mix-unrelated-native-types)
|
2020-08-01 22:05:46 +03:00
|
|
|
- [More](#more)
|
2020-05-04 01:16:48 +03:00
|
|
|
- [License](#license)
|
|
|
|
|
|
2020-04-26 21:49:03 +03:00
|
|
|
|
|
|
|
|
## Installation
|
2019-07-16 23:49:32 +03:00
|
|
|
|
2020-05-03 03:03:18 +03:00
|
|
|
```shell
|
2020-04-26 21:43:00 +03:00
|
|
|
$ npm install ig-object
|
|
|
|
|
```
|
|
|
|
|
|
2020-04-26 21:50:56 +03:00
|
|
|
Or just download and drop [object.js](object.js) into your code.
|
2020-04-26 21:49:03 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-05-02 19:55:05 +03:00
|
|
|
## Basic usage
|
2020-04-26 21:49:03 +03:00
|
|
|
|
|
|
|
|
Include the code, this is compatible with both [node's](https://nodejs.org/) and
|
|
|
|
|
[RequireJS'](https://requirejs.org/) `require(..)`
|
2019-07-16 23:49:32 +03:00
|
|
|
```javascript
|
|
|
|
|
var object = require('ig-object')
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Create a basic constructor...
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// NOTE: new is optional here...
|
2020-05-08 18:29:22 +03:00
|
|
|
var A = new object.Constructor('A', {})
|
2019-07-16 23:49:32 +03:00
|
|
|
|
2020-05-06 03:39:32 +03:00
|
|
|
var B = object.Constructor('B', A, {})
|
2020-04-25 02:00:40 +03:00
|
|
|
|
2020-05-03 03:03:18 +03:00
|
|
|
var C = object.Constructor('C', B, {})
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-02 19:55:05 +03:00
|
|
|
Now we can test this...
|
2019-07-16 23:49:32 +03:00
|
|
|
```javascript
|
|
|
|
|
var c = C() // or new C()
|
|
|
|
|
|
|
|
|
|
c instanceof C // -> true
|
|
|
|
|
c instanceof B // -> true
|
|
|
|
|
c instanceof A // -> true
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-08 18:29:22 +03:00
|
|
|
**Note:**
|
|
|
|
|
- in `object.Constructor('X', A)` the second argument is used as the
|
|
|
|
|
_prototype_, to use `A` as a parent constructor add an empty object
|
|
|
|
|
as a third argument, i.e. 'object.Constructor('X', A, {})'
|
|
|
|
|
(see: [`Constructor(..)` / `C(..)`](#constructor--c) for more info)
|
|
|
|
|
|
2020-04-15 00:42:20 +03:00
|
|
|
|
|
|
|
|
### Inheritance
|
2019-07-16 23:49:32 +03:00
|
|
|
```javascript
|
2020-04-15 00:42:20 +03:00
|
|
|
//
|
2020-05-06 21:48:38 +03:00
|
|
|
// Base <--- Item <--- SubItem
|
2020-04-15 00:42:20 +03:00
|
|
|
//
|
2019-07-17 00:26:39 +03:00
|
|
|
var Base = object.Constructor('Base', {
|
2020-05-02 00:48:55 +03:00
|
|
|
proto_attr: 'prototype attr value',
|
2020-04-15 00:42:20 +03:00
|
|
|
|
2020-05-02 00:48:55 +03:00
|
|
|
get prop(){
|
|
|
|
|
return 'propery value' },
|
2020-04-15 00:42:20 +03:00
|
|
|
|
2020-05-02 00:48:55 +03:00
|
|
|
method: function(){
|
|
|
|
|
console.log('Base.method()') },
|
2019-07-17 00:26:39 +03:00
|
|
|
|
2020-05-02 00:48:55 +03:00
|
|
|
// initializer...
|
|
|
|
|
__init__: function(){
|
|
|
|
|
this.instance_attr = 'instance'
|
|
|
|
|
},
|
2019-07-17 00:26:39 +03:00
|
|
|
})
|
|
|
|
|
|
2020-05-06 03:39:32 +03:00
|
|
|
var Item = object.Constructor('Item', Base, {
|
2020-05-24 23:59:01 +03:00
|
|
|
method: function(){
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
|
|
// call the "super" method...
|
|
|
|
|
return object.parentCall(Item.prototype, 'method', this, ...arguments)
|
|
|
|
|
},
|
|
|
|
|
|
2020-08-01 22:05:46 +03:00
|
|
|
__init__: function(...args){
|
2020-05-02 00:48:55 +03:00
|
|
|
// call the "super" method...
|
2020-08-01 22:05:46 +03:00
|
|
|
object.parentCall(this.__init__, this, ...args)
|
2020-04-25 02:00:40 +03:00
|
|
|
|
2020-05-02 00:48:55 +03:00
|
|
|
this.item_attr = 'instance attribute value'
|
|
|
|
|
},
|
2019-07-17 00:26:39 +03:00
|
|
|
})
|
|
|
|
|
|
2020-05-03 03:03:18 +03:00
|
|
|
var SubItem = object.Constructor('SubItem', Item, {
|
|
|
|
|
// ...
|
|
|
|
|
})
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
|
|
|
|
|
2020-04-15 00:42:20 +03:00
|
|
|
|
|
|
|
|
### Callable instances
|
2020-04-25 01:43:31 +03:00
|
|
|
|
2020-04-10 18:30:55 +03:00
|
|
|
```javascript
|
|
|
|
|
var Action = object.Constructor('Action',
|
2020-05-02 00:48:55 +03:00
|
|
|
// constructor as a function...
|
|
|
|
|
function(context, ...args){
|
|
|
|
|
// return the instance...
|
|
|
|
|
return this
|
|
|
|
|
})
|
2020-04-10 18:30:55 +03:00
|
|
|
|
2020-05-08 14:42:21 +03:00
|
|
|
// a more flexible approach...
|
2020-04-25 01:43:31 +03:00
|
|
|
//
|
|
|
|
|
// This is the same as the above but a bit more convenient as we do
|
|
|
|
|
// not need to use Object.assign(..) or object.mixinFlat(..) to define
|
|
|
|
|
// attributes and props.
|
|
|
|
|
|
2020-04-10 18:30:55 +03:00
|
|
|
var Action2 = object.Constructor('Action2', {
|
2020-05-02 00:48:55 +03:00
|
|
|
__call__: function(context, ...args){
|
2020-05-24 23:59:01 +03:00
|
|
|
// call the callable parent...
|
|
|
|
|
return object.parentCall(Action2.prototype, '__call__', this, ...arguments)
|
2020-05-02 00:48:55 +03:00
|
|
|
},
|
2020-04-10 18:30:55 +03:00
|
|
|
})
|
|
|
|
|
|
2020-05-08 14:42:21 +03:00
|
|
|
|
|
|
|
|
var action = Action()
|
|
|
|
|
var action2 = new Action2()
|
|
|
|
|
|
|
|
|
|
// the instances are now functions...
|
|
|
|
|
action()
|
|
|
|
|
action2()
|
2020-04-10 18:30:55 +03:00
|
|
|
```
|
|
|
|
|
|
2020-04-25 02:08:27 +03:00
|
|
|
In the above cases both the _function constructor_ and the `.__call__(..)`
|
|
|
|
|
method receive a `context` argument in addition to `this` context, those
|
|
|
|
|
represent the two contexts relevant to the callable instance:
|
2020-04-25 02:00:40 +03:00
|
|
|
- Internal context (`this`)
|
2020-04-25 01:43:31 +03:00
|
|
|
This always references the instance being called
|
2020-05-02 00:48:55 +03:00
|
|
|
- External context (`context`)
|
2020-04-25 02:03:42 +03:00
|
|
|
This is the object the instance is called from, i.e. the call _context_
|
|
|
|
|
(`window` or `global` by default)
|
2020-04-25 01:43:31 +03:00
|
|
|
|
|
|
|
|
If the prototype is explicitly defined as a function then it is the
|
2020-04-25 02:05:54 +03:00
|
|
|
user's responsibility to call `.__call__(..)` method.
|
2020-04-25 01:43:31 +03:00
|
|
|
|
2020-05-24 23:59:01 +03:00
|
|
|
When calling the parent passing `'__call__'` will get the parent in both
|
|
|
|
|
the function and `.__call__(..)` implementations, but extra care must be
|
|
|
|
|
taken in passing the reference prototype to `.parentCall(..)`, the instance
|
|
|
|
|
is implemented as a proxy function that will pass the arguments to the
|
|
|
|
|
implementation (i.e. `this.constructor.prototype(..)`) so this proxy
|
|
|
|
|
function as well as the `.constructor.prototype(..)` are valid implementations
|
|
|
|
|
and both will be retrieved by `sources(this, '__call__')`,
|
|
|
|
|
`values(this, '__call__')` and by extension `parent(this, '__call__')`
|
|
|
|
|
and friends, so this is another reason not to use `this` in the general
|
|
|
|
|
case.
|
|
|
|
|
|
2020-04-25 01:43:31 +03:00
|
|
|
|
2020-05-03 03:03:18 +03:00
|
|
|
**Notes:**
|
2020-05-25 00:53:56 +03:00
|
|
|
- The two approaches (_function_ vs. `.__call__(..)`) will produce
|
2020-05-08 14:42:21 +03:00
|
|
|
functionally identical but structurally different constructors/objects,
|
2020-05-08 15:42:32 +03:00
|
|
|
the difference is in `.prototype` -- what is defined as the prototype
|
2020-05-08 16:00:00 +03:00
|
|
|
_is_ the prototype (_POLS_), so we get:
|
2020-05-08 14:42:21 +03:00
|
|
|
|
2020-05-08 16:00:00 +03:00
|
|
|
- _prototype function_ -> `.prototype` is that exact function object,
|
|
|
|
|
- `.__call__(..)` -> `.prototype` is _the_ object with the `.__call__(..)`
|
|
|
|
|
method.
|
2020-05-08 14:42:21 +03:00
|
|
|
|
|
|
|
|
The instance in both cases is a function wrapper that will proxy the
|
|
|
|
|
call to the corresponding implementation.
|
2020-05-03 03:03:18 +03:00
|
|
|
(this may change in the future)
|
2020-05-25 00:53:56 +03:00
|
|
|
- Making an object callable does not guarantee that `<obj> instanceof Function`
|
|
|
|
|
will be `true`, though `typeof(<obj>) == 'function'`will always work.
|
|
|
|
|
To satisfy the `instanceof Function` test the prototype tree must be
|
|
|
|
|
rooted in `Function`.
|
2020-05-03 03:03:18 +03:00
|
|
|
|
|
|
|
|
|
2020-05-06 05:30:11 +03:00
|
|
|
### Mix-ins
|
|
|
|
|
|
|
|
|
|
Prototype-based mixin...
|
|
|
|
|
```javascript
|
|
|
|
|
var utilityMixin = {
|
|
|
|
|
utility: function(){
|
|
|
|
|
// ...
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var Base = object.Constructor('Base')
|
|
|
|
|
|
2020-05-07 21:35:48 +03:00
|
|
|
// normal instance prototype chain:
|
|
|
|
|
// b -> Base.prototype -> ..
|
|
|
|
|
//
|
|
|
|
|
var b = Base()
|
2020-05-06 05:30:11 +03:00
|
|
|
|
|
|
|
|
// mixin directly into the instance...
|
2020-05-07 21:35:48 +03:00
|
|
|
//
|
|
|
|
|
// now the prototype chain looks like this:
|
|
|
|
|
// b -> mixinFlat({}, utilityMixin) -> Base.prototype -> ..
|
|
|
|
|
//
|
|
|
|
|
object.mixin(b, utilityMixin)
|
2020-05-06 05:30:11 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`.mixin(..)` will copy the contents of `utilityMixin` into the prototype
|
2020-05-07 21:35:48 +03:00
|
|
|
chain between `b` and `b.__proto__`.
|
2020-05-06 05:30:11 +03:00
|
|
|
|
2020-05-06 05:37:28 +03:00
|
|
|
We can also remove the mixin...
|
|
|
|
|
```javascript
|
2020-05-07 21:35:48 +03:00
|
|
|
o.mixout(b, utilityMixin)
|
2020-05-06 05:37:28 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-07 21:35:48 +03:00
|
|
|
The mixed-in data is removed iff a [matching](#match) object is found in
|
|
|
|
|
the chain with the same attributes as `utilityMixin` and with each
|
|
|
|
|
attribute matching identity with the corresponding attribute in the mixin.
|
2020-05-06 05:37:28 +03:00
|
|
|
|
2020-05-06 05:30:11 +03:00
|
|
|
|
|
|
|
|
Constructor-based mixin...
|
|
|
|
|
```javascript
|
|
|
|
|
var UtilityMixin = function(parent){
|
|
|
|
|
return object.Constructor(parent.name + '+utils', parent, utilityMixin) }
|
|
|
|
|
|
|
|
|
|
var Mixed = object.Constructor('Mixed', UtilityMixin(Base), {
|
|
|
|
|
// ...
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
var m = Mixed()
|
|
|
|
|
```
|
|
|
|
|
|
2020-06-08 16:07:54 +03:00
|
|
|
**Notes:**
|
|
|
|
|
- It is not recommended to `.mixin(..)` into constructors directly, use
|
|
|
|
|
`.mixinFlat(..)` instead.
|
|
|
|
|
|
|
|
|
|
|
2020-05-02 21:48:59 +03:00
|
|
|
|
|
|
|
|
## Advanced usage
|
|
|
|
|
|
2020-04-15 00:42:20 +03:00
|
|
|
### Low level constructor
|
2020-04-25 01:43:31 +03:00
|
|
|
|
2020-04-10 18:30:55 +03:00
|
|
|
```javascript
|
|
|
|
|
var LowLevel = object.Constructor('LowLevel', {
|
2020-05-02 00:48:55 +03:00
|
|
|
__new__: function(context, ...args){
|
|
|
|
|
return {}
|
|
|
|
|
},
|
2020-04-10 18:30:55 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
```
|
2019-07-16 23:49:32 +03:00
|
|
|
|
2020-06-01 02:49:00 +03:00
|
|
|
The value `.__new__(..)` returns is used as the instance and gets linked
|
|
|
|
|
to the prototype chain by the calling constructor's `.__rawinstance__(..)`,
|
|
|
|
|
the constructor then will call `.__init__(..)` if defined.
|
|
|
|
|
|
|
|
|
|
_Note that `.__init__(..)` is called by the constructor and not by
|
|
|
|
|
`RawInstance(..)` or `.__rawinstance__(..)`._
|
|
|
|
|
|
2020-05-06 21:56:24 +03:00
|
|
|
Like [_function constructor_ and `.__call__(..)`](#callable-instances)
|
|
|
|
|
this also has two contexts, but the internal context is different -- as
|
|
|
|
|
it is the job of `.__new__(..)` to create an instance, at time of call
|
|
|
|
|
the instance does not exist and `this` references the `.prototype`
|
|
|
|
|
object.
|
|
|
|
|
|
2020-04-25 01:43:31 +03:00
|
|
|
The external context is the same as above.
|
|
|
|
|
|
|
|
|
|
Contexts:
|
2020-04-25 02:00:40 +03:00
|
|
|
- Internal context (`this`)
|
2020-04-25 01:43:31 +03:00
|
|
|
References the `.prototype` of the constructor.
|
2020-05-02 00:48:55 +03:00
|
|
|
- External context (`context`)
|
2020-04-25 02:03:42 +03:00
|
|
|
This is the object the instance is called from, i.e. the call _context_
|
|
|
|
|
(`window` or `global` by default), the same as for function constructor
|
|
|
|
|
and `.__call__(..)`.
|
2020-04-25 01:43:31 +03:00
|
|
|
|
|
|
|
|
This has priority over the callable protocols above, thus the user must
|
2020-04-25 02:08:27 +03:00
|
|
|
take care of both the _function constructor_ and `prototype.__call__(..)`
|
2020-04-25 01:43:31 +03:00
|
|
|
handling.
|
|
|
|
|
|
|
|
|
|
|
2020-05-02 22:09:37 +03:00
|
|
|
### Extending the constructor
|
|
|
|
|
|
|
|
|
|
```javascript
|
2020-05-08 04:17:03 +03:00
|
|
|
var C = object.Constructor('C', {
|
2020-05-03 03:03:18 +03:00
|
|
|
// this will get mixed into the constructor C...
|
|
|
|
|
|
2020-05-08 04:17:03 +03:00
|
|
|
constructor_attr: 123,
|
2020-05-02 22:09:37 +03:00
|
|
|
|
2020-05-08 04:17:03 +03:00
|
|
|
constructorMethod: function(){
|
2020-05-02 22:09:37 +03:00
|
|
|
// ...
|
2020-05-08 04:17:03 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
}, {
|
|
|
|
|
instanceMethod: function(){
|
|
|
|
|
// get constructor data...
|
|
|
|
|
var x = this.constructor.constructor_attr
|
2020-05-02 22:09:37 +03:00
|
|
|
|
|
|
|
|
// ...
|
2020-05-08 04:17:03 +03:00
|
|
|
},
|
|
|
|
|
// ...
|
|
|
|
|
})
|
2020-05-02 22:09:37 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-03 03:03:18 +03:00
|
|
|
And the same thing while extending...
|
2020-05-02 22:09:37 +03:00
|
|
|
```javascript
|
2020-05-06 19:34:10 +03:00
|
|
|
var D = object.Constructor('D', C, {
|
2020-05-08 04:16:13 +03:00
|
|
|
// ...
|
|
|
|
|
}, {
|
|
|
|
|
// ...
|
|
|
|
|
})
|
2020-05-02 22:09:37 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2020-05-02 22:19:55 +03:00
|
|
|
### Inheriting from native constructor objects
|
2020-05-02 19:55:05 +03:00
|
|
|
|
|
|
|
|
```javascript
|
2020-05-06 03:39:32 +03:00
|
|
|
var myArray = object.Constructor('myArray', Array, {
|
2020-05-02 19:55:05 +03:00
|
|
|
// ...
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-02 22:19:55 +03:00
|
|
|
All special methods and protocols defined by _object.js_ except for
|
|
|
|
|
`.__new__(..)` will work here without change.
|
|
|
|
|
|
|
|
|
|
For details on `.__new__(..)` and native `.constructor(..)` interaction
|
|
|
|
|
see: [Extending native `.constructor(..)`](#extending-native-constructor)
|
2020-05-02 19:55:05 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
### Extending native `.constructor(..)`
|
|
|
|
|
|
|
|
|
|
Extending `.constructor(..)` is not necessary in most cases as
|
|
|
|
|
`.__init__(..)` will do everything generally needed, except for instance
|
|
|
|
|
replacement.
|
|
|
|
|
|
|
|
|
|
```javascript
|
2020-05-06 03:39:32 +03:00
|
|
|
var myArray = object.Constructor('myArray', Array, {
|
2020-05-02 19:55:05 +03:00
|
|
|
__new__: function(context, ...args){
|
|
|
|
|
var obj = Reflect.construct(myArray.__proto__, args, myArray)
|
|
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
|
|
return obj
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-08-01 22:05:46 +03:00
|
|
|
## Special methods
|
|
|
|
|
|
|
|
|
|
### `<object>.__new__(..)`
|
|
|
|
|
|
|
|
|
|
Create new instance object.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
<object>.__new__(<context>, ..)
|
|
|
|
|
-> <instance>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This is called in the context of `<constructor>` as at time of call
|
|
|
|
|
no instance exists yet.
|
|
|
|
|
|
|
|
|
|
`<context>` is the _outer_ context of the call, i.e. the object from which
|
|
|
|
|
`<constructor>` was referenced before it was called.
|
|
|
|
|
|
|
|
|
|
For more info see:
|
|
|
|
|
- [Low level constructor](#low-level-constructor),
|
|
|
|
|
- [Inheriting from native constructor objects](#inheriting-from-native-constructor-objects)
|
|
|
|
|
- [Extending native `.constructor(..)`](#extending-native-constructor)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### `<object>.__init__(..)`
|
|
|
|
|
|
|
|
|
|
Initialize the instance.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
<object>.__init__(..)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Return value is ignored.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### `<object>.__call__(..)`
|
|
|
|
|
|
|
|
|
|
Call the object.
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
<object>.__call__(<context>, ..)
|
|
|
|
|
-> <result>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This is called in the context of `<object>`.
|
|
|
|
|
|
|
|
|
|
`<context>` is the _outer_ context of the call, i.e. the object from which
|
|
|
|
|
`<object>` was referenced before it was called.
|
|
|
|
|
|
|
|
|
|
For more info see: [Callable instances](#callable-instances)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-07-16 23:49:32 +03:00
|
|
|
## Components
|
|
|
|
|
|
2020-05-06 14:59:55 +03:00
|
|
|
Note that all of the following are generic and will work on any relevant
|
|
|
|
|
JavaScript object.
|
|
|
|
|
|
2020-05-07 21:53:31 +03:00
|
|
|
For example, this will happily create a normal native array object
|
|
|
|
|
`['a', 'b', 'c']`:
|
2020-05-06 14:59:55 +03:00
|
|
|
```javascript
|
2020-05-08 18:36:51 +03:00
|
|
|
var l = object.RawInstance(null, Array, 'a', 'b', 'c')
|
2020-05-06 14:59:55 +03:00
|
|
|
```
|
2020-05-06 14:54:16 +03:00
|
|
|
|
|
|
|
|
|
2020-05-24 00:37:52 +03:00
|
|
|
### `STOP`
|
|
|
|
|
|
|
|
|
|
Used in [`sources(..)`](#sources), [`values(..)`](#values) and
|
|
|
|
|
[`mixins(..)`](#mixins) to stop the search before it reaches the top of
|
|
|
|
|
the prototype chain.
|
|
|
|
|
|
|
|
|
|
|
2020-05-06 14:37:46 +03:00
|
|
|
### `sources(..)`
|
|
|
|
|
|
2020-04-25 01:43:31 +03:00
|
|
|
Get sources for attribute
|
2020-04-15 00:42:20 +03:00
|
|
|
```
|
|
|
|
|
sources(<object>, <name>)
|
|
|
|
|
sources(<object>, <name>, <callback>)
|
2020-05-02 00:48:55 +03:00
|
|
|
-> <list>
|
2020-04-15 00:42:20 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-07 21:02:05 +03:00
|
|
|
```
|
|
|
|
|
callback(<source>)
|
2020-05-24 00:37:52 +03:00
|
|
|
-> STOP
|
2020-05-07 21:02:05 +03:00
|
|
|
-> undefined
|
2020-05-24 00:37:52 +03:00
|
|
|
-> <value>
|
2020-05-07 21:02:05 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-24 00:37:52 +03:00
|
|
|
The `callback(..)` controls the output of `sources(..)` by returning
|
|
|
|
|
one of the following:
|
|
|
|
|
|
|
|
|
|
- `object.STOP`
|
2020-05-24 00:49:51 +03:00
|
|
|
This will make `sources(..)` stop and return the `<list>` up to and
|
|
|
|
|
including the object that triggered the _stop_.
|
2020-05-24 00:37:52 +03:00
|
|
|
- `undefined`
|
2020-05-24 00:49:51 +03:00
|
|
|
Add the object triggering `callback(..)` in `<list>` as-is and continue.
|
2020-05-24 00:37:52 +03:00
|
|
|
- array
|
2020-05-24 00:49:51 +03:00
|
|
|
The containing values will be merged into the result list and continue.
|
|
|
|
|
This is a way to either skip an object by returning `[]` or multiple
|
|
|
|
|
values instead of one.
|
2020-05-24 00:46:32 +03:00
|
|
|
- `<value>`
|
2020-05-24 00:49:51 +03:00
|
|
|
Add to the resulting `<list>` as-is instead of the object triggering
|
|
|
|
|
`callback(..)` and continue.
|
2020-05-24 00:37:52 +03:00
|
|
|
|
2020-04-30 16:41:19 +03:00
|
|
|
|
2020-05-24 23:59:01 +03:00
|
|
|
Special case: get callable implementations
|
|
|
|
|
```
|
2020-05-30 01:17:13 +03:00
|
|
|
sources(<object>, '__call__', ..)
|
2020-05-24 23:59:01 +03:00
|
|
|
-> <list>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This will get the callable implementations regardless of the actual
|
|
|
|
|
implementation details, i.e. both function prototype or `.__call__(..)`
|
|
|
|
|
methods will be matched.
|
|
|
|
|
|
|
|
|
|
|
2020-05-23 16:56:39 +03:00
|
|
|
### `values(..)`
|
|
|
|
|
|
|
|
|
|
Get values for attribute in prototype chain
|
|
|
|
|
```
|
|
|
|
|
values(<object>, <name>)
|
|
|
|
|
values(<object>, <name>, <callback>)
|
|
|
|
|
-> <list>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
callback(<value>, <source>)
|
2020-05-24 00:37:52 +03:00
|
|
|
-> STOP
|
2020-05-23 16:56:39 +03:00
|
|
|
-> undefined
|
2020-05-24 00:37:52 +03:00
|
|
|
-> <value>
|
2020-05-23 16:56:39 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Get property descriptors for attribute in prototype chain
|
|
|
|
|
```
|
|
|
|
|
values(<object>, <name>, true)
|
|
|
|
|
values(<object>, <name>, <callback>, true)
|
|
|
|
|
-> <list>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
callback(<descriptor>, <source>)
|
2020-05-24 00:37:52 +03:00
|
|
|
-> STOP
|
2020-05-23 16:56:39 +03:00
|
|
|
-> undefined
|
2020-05-24 00:37:52 +03:00
|
|
|
-> <value>
|
2020-05-23 16:56:39 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-30 01:17:13 +03:00
|
|
|
|
|
|
|
|
Special case: get callable implementations
|
|
|
|
|
```
|
|
|
|
|
values(<object>, '__call__', ..)
|
|
|
|
|
-> <list>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This will return the callable objects themselves or the value of `.__call__`.
|
|
|
|
|
|
|
|
|
|
|
2020-05-24 23:59:01 +03:00
|
|
|
See [`sources(..)`](#sources) for docs on `callback(..)` and special cases.
|
2020-05-23 16:56:39 +03:00
|
|
|
|
2020-08-01 22:05:46 +03:00
|
|
|
|
2020-05-06 14:37:46 +03:00
|
|
|
### `parent(..)`
|
|
|
|
|
|
2020-04-30 02:01:00 +03:00
|
|
|
Get parent attribute value or method
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
2020-04-30 02:01:00 +03:00
|
|
|
parent(<prototype>, <name>)
|
|
|
|
|
-> <parent-value>
|
2020-04-27 04:16:35 +03:00
|
|
|
-> undefined
|
2020-05-24 23:59:01 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It is recommended to use the relative`<constructor>.prototype` as
|
|
|
|
|
`<prototype>` and in turn not recommended to use `this` or `this.__proto__`
|
|
|
|
|
as they will not provide the appropriate reference point in the prototype
|
|
|
|
|
chain for the current method and may result in infinite recursion.
|
2020-04-27 04:16:35 +03:00
|
|
|
|
2020-05-24 23:59:01 +03:00
|
|
|
For access to parent methods the following special case is better.
|
|
|
|
|
|
|
|
|
|
```
|
2020-04-30 02:01:00 +03:00
|
|
|
parent(<method>, <this>)
|
2020-05-02 00:48:55 +03:00
|
|
|
-> <parent-method>
|
2020-04-27 04:16:35 +03:00
|
|
|
-> undefined
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-24 23:59:01 +03:00
|
|
|
|
2020-04-30 02:28:08 +03:00
|
|
|
_Edge case: The `parent(<method>, ..)` has one potential pitfall -- in
|
|
|
|
|
the rare case where a prototype chain contains two or more references
|
|
|
|
|
to the same method under the same name, `parent(..)` can't distinguish
|
2020-04-30 02:29:15 +03:00
|
|
|
between these references and will always return the second one._
|
2020-04-30 02:20:29 +03:00
|
|
|
|
|
|
|
|
|
2020-05-24 23:59:01 +03:00
|
|
|
Special case: get the parent callable implementation
|
|
|
|
|
```
|
|
|
|
|
parent(<prototype>, '__call__')
|
|
|
|
|
-> <parent-value>
|
|
|
|
|
-> undefined
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
See [`sources(..)`](#sources) for more info on the special case.
|
|
|
|
|
|
2020-05-06 14:37:46 +03:00
|
|
|
|
|
|
|
|
### `parentProperty(..)`
|
|
|
|
|
|
2020-04-30 16:41:19 +03:00
|
|
|
Get parent property descriptor
|
|
|
|
|
```
|
|
|
|
|
parentProperty(<prototype>, <name>)
|
|
|
|
|
-> <prop-descriptor>
|
|
|
|
|
-> undefined
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2020-05-06 14:37:46 +03:00
|
|
|
### `parentCall(..)`
|
|
|
|
|
|
2020-04-27 01:17:42 +03:00
|
|
|
Get parent method and call it
|
|
|
|
|
```
|
2020-04-27 04:16:35 +03:00
|
|
|
parentCall(<prototype>, <name>, <this>)
|
2020-05-02 00:48:55 +03:00
|
|
|
-> <result>
|
2020-04-30 02:20:29 +03:00
|
|
|
-> undefined
|
|
|
|
|
|
2020-04-30 02:10:01 +03:00
|
|
|
parentCall(<method>, <this>)
|
2020-05-02 00:48:55 +03:00
|
|
|
-> <result>
|
2020-04-27 04:16:35 +03:00
|
|
|
-> undefined
|
2020-04-27 01:17:42 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-24 23:59:01 +03:00
|
|
|
Special case: call the parent callable implementation
|
|
|
|
|
```
|
|
|
|
|
parentCall(<prototype>, '__call__', <this>)
|
|
|
|
|
-> <result>
|
|
|
|
|
-> undefined
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
See [`parent(..)`](#parent) and [`sources(..)`](#sources) for more details.
|
|
|
|
|
|
2020-08-09 14:05:30 +03:00
|
|
|
### `parentOf(..)` / `childOf(..)` / `related(..)`
|
|
|
|
|
|
|
|
|
|
Test if a is parent of b and/or vice-versa.
|
|
|
|
|
```
|
|
|
|
|
parentOf(<parent>, <child>)
|
|
|
|
|
-> <bool>
|
|
|
|
|
|
|
|
|
|
childOf(<child>, <parent>)
|
|
|
|
|
-> <bool>
|
|
|
|
|
|
|
|
|
|
related(<a>, <b>)
|
|
|
|
|
-> <bool>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
These are similar to `instanceof` but will test if the two objects are in the
|
|
|
|
|
same prototype chain and in case of `parentOf(..)`/`childOf(..)` in what order.
|
2020-04-27 01:17:42 +03:00
|
|
|
|
2020-05-06 14:37:46 +03:00
|
|
|
### `mixin(..)`
|
|
|
|
|
|
2020-05-06 14:54:16 +03:00
|
|
|
_Mixin_ objects into a prototype chain
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
2020-05-06 14:54:16 +03:00
|
|
|
mixin(<base>, <object>, ..)
|
|
|
|
|
-> <base>
|
2020-05-06 14:37:46 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-06 14:54:16 +03:00
|
|
|
This will link the base `.__proto__` to the last _mixin_ in chain,
|
|
|
|
|
keeping the prototype visibility the same.
|
|
|
|
|
|
|
|
|
|
This will copy the content of each input object without touching the
|
|
|
|
|
objects themselves, making them fully reusable.
|
|
|
|
|
|
2020-06-08 16:07:54 +03:00
|
|
|
It is not recommended to `.mixin(..)` into constructors directly, use
|
|
|
|
|
`.mixinFlat(..)` instead.
|
|
|
|
|
|
2020-05-06 14:37:46 +03:00
|
|
|
|
2020-05-07 21:02:05 +03:00
|
|
|
### `mixins(..)`
|
|
|
|
|
|
|
|
|
|
Get matching mixins
|
|
|
|
|
```
|
|
|
|
|
mixins(<base>, <object>)
|
|
|
|
|
mixins(<base>, [<object>, ..])
|
|
|
|
|
mixins(<base>, <object>, <callback>)
|
|
|
|
|
mixins(<base>, [<object>, ..], <callback>)
|
|
|
|
|
-> list
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
callback(<match>, <object>, <parent>)
|
2020-05-24 00:37:52 +03:00
|
|
|
-> STOP
|
2020-05-07 21:02:05 +03:00
|
|
|
-> undefined
|
2020-05-24 00:37:52 +03:00
|
|
|
-> <value>
|
2020-05-07 21:02:05 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-24 00:37:52 +03:00
|
|
|
See [`sources(..)`](#sources) for docs on `callback(..)`
|
|
|
|
|
|
2020-05-07 21:02:05 +03:00
|
|
|
|
|
|
|
|
### `hasMixin(..)`
|
|
|
|
|
|
2020-05-08 04:24:09 +03:00
|
|
|
Check if _base_ object has _mixin_
|
2020-05-07 21:02:05 +03:00
|
|
|
```
|
|
|
|
|
hasMixin(<base>, <mixin>)
|
|
|
|
|
-> <bool>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2020-05-06 14:37:46 +03:00
|
|
|
### `mixout(..)`
|
|
|
|
|
|
2020-05-08 04:24:09 +03:00
|
|
|
Remove the _first_ match matching input _mixin_ from _base_
|
2020-05-08 04:19:48 +03:00
|
|
|
of _base_
|
2020-05-06 14:37:46 +03:00
|
|
|
```
|
2020-05-06 14:54:16 +03:00
|
|
|
mixout(<base>, <object>, ..)
|
2020-05-06 19:27:34 +03:00
|
|
|
mixout(<base>, 'first', <object>, ..)
|
|
|
|
|
-> <base>
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-08 04:24:09 +03:00
|
|
|
Remove _all_ occurrences of each matching input _mixin_ from _base_
|
2020-05-06 19:27:34 +03:00
|
|
|
```
|
|
|
|
|
mixout(<base>, 'all', <object>, ..)
|
2020-05-06 14:54:16 +03:00
|
|
|
-> <base>
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-06 14:54:16 +03:00
|
|
|
This is the opposite of `mixin(..)`
|
|
|
|
|
|
2020-04-30 16:41:19 +03:00
|
|
|
|
2020-05-06 14:37:46 +03:00
|
|
|
### `mixinFlat(..)`
|
|
|
|
|
|
2020-05-06 19:34:10 +03:00
|
|
|
Mixin contents of objects into one _base_ object
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
2020-05-06 19:00:39 +03:00
|
|
|
mixinFlat(<base>, <object>, ..)
|
|
|
|
|
-> <base>
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
2020-05-06 14:54:16 +03:00
|
|
|
This is like `Object.assign(..)` but copies property descriptors rather
|
|
|
|
|
than property values.
|
2020-04-25 01:43:31 +03:00
|
|
|
|
2020-06-08 20:18:35 +03:00
|
|
|
Also like `Object.assign(..)` this _will_ overwrite attribute values in
|
|
|
|
|
`<base>`.
|
|
|
|
|
|
2020-04-30 16:41:19 +03:00
|
|
|
|
2020-05-08 18:36:51 +03:00
|
|
|
### `RawInstance(..)`
|
2020-05-06 14:37:46 +03:00
|
|
|
|
2020-04-26 21:32:13 +03:00
|
|
|
Make a raw (un-initialized) instance
|
2020-04-26 19:30:49 +03:00
|
|
|
```
|
2020-05-08 18:36:51 +03:00
|
|
|
RawInstance(<context>, <constructor>, ..)
|
2020-04-26 19:30:49 +03:00
|
|
|
-> <object>
|
|
|
|
|
```
|
2019-07-16 23:49:32 +03:00
|
|
|
|
2020-05-08 18:36:51 +03:00
|
|
|
`RawInstance(..)` will do the following:
|
2020-05-06 14:30:32 +03:00
|
|
|
- Create an instance object
|
|
|
|
|
- get result of `.__new__(..)` if defined, or
|
|
|
|
|
- if prototype is a function or `.__call__(..)` is defined, create a
|
|
|
|
|
wrapper function, or
|
2020-05-17 22:57:25 +03:00
|
|
|
- if constructor's `.__proto__` has a `.__rawinstance__(..)` use it
|
|
|
|
|
to create an instance, or
|
2020-05-06 14:30:32 +03:00
|
|
|
- if constructor's `.__proto__` is a function (constructor) use it
|
|
|
|
|
to create an instance, or
|
2020-05-06 14:32:00 +03:00
|
|
|
- use `{}`.
|
2020-05-06 14:30:32 +03:00
|
|
|
- Link the object into the prototype chain
|
|
|
|
|
|
2020-05-08 18:42:06 +03:00
|
|
|
|
2020-05-08 04:24:09 +03:00
|
|
|
_Un-initialized_ means this will not call `.__init__(..)`
|
|
|
|
|
|
2020-05-06 14:30:32 +03:00
|
|
|
|
2020-05-08 18:44:13 +03:00
|
|
|
`RawInstance(..)` can be called with and without `new`.
|
2020-05-08 18:42:06 +03:00
|
|
|
|
|
|
|
|
|
2020-05-06 14:54:16 +03:00
|
|
|
### `Constructor(..)` / `C(..)`
|
2020-05-06 14:37:46 +03:00
|
|
|
|
2020-04-25 01:43:31 +03:00
|
|
|
Define an object constructor
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
2020-05-02 19:55:05 +03:00
|
|
|
Constructor(<name>)
|
2020-04-10 18:30:55 +03:00
|
|
|
Constructor(<name>, <prototype>)
|
2020-05-03 03:03:18 +03:00
|
|
|
Constructor(<name>, <parent-constructor>, <prototype>)
|
2020-05-06 03:39:32 +03:00
|
|
|
Constructor(<name>, <parent-constructor>, <constructor-mixin>, <prototype>)
|
2020-05-03 03:03:18 +03:00
|
|
|
Constructor(<name>, <constructor-mixin>, <prototype>)
|
2020-05-02 00:48:55 +03:00
|
|
|
-> <constructor>
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-06 14:30:32 +03:00
|
|
|
`Constructor(..)` essentially does the following:
|
|
|
|
|
- Creates a _constructor_ function,
|
|
|
|
|
- Sets constructor `.name` and `.toString(..)` for introspection,
|
2020-05-08 18:36:51 +03:00
|
|
|
- Creates `.__rawinstance__(..)` wrapper to `RawInstance(..)`
|
2020-05-06 14:30:32 +03:00
|
|
|
- Sets constructor `.__proto__`, `.prototype` and `.prototype.constructor`,
|
|
|
|
|
- Mixes in _constructor-mixin_ if given.
|
|
|
|
|
|
|
|
|
|
The resulting _constructor_ function when called will:
|
2020-05-08 18:36:51 +03:00
|
|
|
- call constructor's `.__rawinstance__(..)` if defined or `RawInstance(..)`
|
2020-05-06 14:30:32 +03:00
|
|
|
to create an instance,
|
|
|
|
|
- call instance's `.__init__(..)` if present.
|
|
|
|
|
|
2020-04-30 16:41:19 +03:00
|
|
|
|
2020-05-08 18:29:22 +03:00
|
|
|
Note that `Constructor(<name>, <prototype>)` is intentionally set as default
|
|
|
|
|
instead of having the _parent-constructor_ as the last argument, this is
|
|
|
|
|
done for two reasons:
|
|
|
|
|
- The main cause to inherit from a constructor is to extend it,
|
|
|
|
|
- In real code the `Constructor(<name>, <prototype>)` is more common than
|
|
|
|
|
empty inheritance.
|
|
|
|
|
|
|
|
|
|
|
2020-04-25 01:52:00 +03:00
|
|
|
Shorthand to `Constructor(..)`
|
|
|
|
|
```
|
|
|
|
|
C(<name>, ..)
|
2020-05-02 00:48:55 +03:00
|
|
|
-> <constructor>
|
2020-04-25 01:52:00 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
2020-05-08 18:44:13 +03:00
|
|
|
`Constructor(..)` / `C(..)` can be called with and without `new`.
|
|
|
|
|
|
|
|
|
|
|
2020-05-03 03:11:36 +03:00
|
|
|
|
|
|
|
|
## Utilities
|
|
|
|
|
|
2020-08-02 20:29:45 +03:00
|
|
|
### `normalizeIndent(..)` / `normalizeTextIndent(..)` / `doc` / `text`
|
2020-05-06 14:54:16 +03:00
|
|
|
|
2020-05-08 15:42:32 +03:00
|
|
|
Align _code_ to shortest leading white-space
|
2020-05-03 03:11:36 +03:00
|
|
|
```
|
|
|
|
|
normalizeIndent(<text>)
|
|
|
|
|
normalizeIndent(<text>, <tab-size>)
|
2020-06-02 15:59:54 +03:00
|
|
|
normalizeIndent(<text>, <tab-size>, <leading-tabs>)
|
2020-05-03 03:11:36 +03:00
|
|
|
-> <text>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This is used to format `.toString(..)` return values for nested functions
|
|
|
|
|
to make source printing in console more pleasant to read.
|
|
|
|
|
|
2020-05-08 15:53:24 +03:00
|
|
|
`tab_size` defaults to `object.TAB_SIZE`
|
|
|
|
|
|
2020-06-02 15:59:54 +03:00
|
|
|
`leading_tabs` defaults to `object.LEADING_TABS`
|
2020-05-08 15:53:24 +03:00
|
|
|
|
2020-05-08 16:03:02 +03:00
|
|
|
|
2020-05-08 16:03:37 +03:00
|
|
|
A shorthand to `normalizeIndent(..)` optimized for text rather than code
|
2020-05-08 16:03:02 +03:00
|
|
|
```
|
|
|
|
|
normalizeTextIndent(..)
|
|
|
|
|
-> <text>
|
|
|
|
|
```
|
|
|
|
|
|
2020-06-02 15:59:54 +03:00
|
|
|
This ignores `object.LEADING_TABS` and `leading_tabs` is 0 by default.
|
2020-05-08 15:53:24 +03:00
|
|
|
|
2020-08-01 22:05:46 +03:00
|
|
|
|
2020-08-02 20:29:45 +03:00
|
|
|
`doc` and `text` are template string versions of `normalizeIndent(..)` and `normalizeTextIndent(..)` respectively.
|
|
|
|
|
|
|
|
|
|
|
2020-06-14 04:29:27 +03:00
|
|
|
### `deepKeys(..)`
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
deepKeys(<obj>)
|
|
|
|
|
-> <keys>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
deepKeys(<obj>, <stop>)
|
|
|
|
|
-> <keys>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This is like `Object.keys(..)` but will get the keys from the whole
|
|
|
|
|
prototype chain or until `<stop>` if given.
|
|
|
|
|
|
2020-05-03 03:11:36 +03:00
|
|
|
|
2020-05-07 21:02:05 +03:00
|
|
|
### `match(..)`
|
|
|
|
|
|
|
|
|
|
Test if the two objects match in attributes and attribute values
|
|
|
|
|
```
|
2020-06-14 04:29:27 +03:00
|
|
|
match(<base>, <obj>)
|
|
|
|
|
-> <bool>
|
2020-05-07 21:02:05 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-07 21:20:20 +03:00
|
|
|
This relies on first level object structure to match the input object, for
|
|
|
|
|
a successful match one of the following must apply:
|
|
|
|
|
- object are identical
|
2020-05-07 21:02:05 +03:00
|
|
|
|
2020-05-07 21:20:20 +03:00
|
|
|
or:
|
|
|
|
|
- `typeof` matches _and_,
|
|
|
|
|
- attribute count matches _and_,
|
|
|
|
|
- attribute names match _and_,
|
|
|
|
|
- attribute values are identical.
|
2020-05-07 21:02:05 +03:00
|
|
|
|
|
|
|
|
|
2020-06-07 21:28:04 +03:00
|
|
|
Non-strict match
|
|
|
|
|
```
|
2020-06-14 04:29:27 +03:00
|
|
|
match(<base., <obj>, true)
|
|
|
|
|
-> <bool>
|
2020-06-07 21:28:04 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Like the default case but uses _equality_ instead of _identity_ to match
|
|
|
|
|
values.
|
|
|
|
|
|
2020-05-03 03:11:36 +03:00
|
|
|
|
2020-06-08 04:16:03 +03:00
|
|
|
### `matchPartial(..)`
|
|
|
|
|
|
|
|
|
|
```
|
2020-06-14 04:29:27 +03:00
|
|
|
matchPartial(<base>, <obj>)
|
|
|
|
|
-> <bool>
|
2020-06-08 04:16:03 +03:00
|
|
|
|
|
|
|
|
// non-strict version...
|
2020-06-14 04:29:27 +03:00
|
|
|
matchPartial(<base>, <obj>, true)
|
|
|
|
|
-> <bool>
|
2020-06-08 04:16:03 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Like `.match(..)` but will check for a partial match, i.e. when `obj` is
|
|
|
|
|
a non-strict subset of `base`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-05-03 03:03:18 +03:00
|
|
|
## Limitations
|
|
|
|
|
|
2020-05-04 01:16:48 +03:00
|
|
|
### Can not mix unrelated native types
|
2020-05-03 03:03:18 +03:00
|
|
|
|
2020-05-03 03:11:36 +03:00
|
|
|
At this point we can't mix native types, for example it is not possible
|
|
|
|
|
to make a callable `Array` object...
|
2020-05-03 03:03:18 +03:00
|
|
|
|
2020-05-03 15:18:41 +03:00
|
|
|
This is not possible in current _JavaScript_ implementations directly
|
|
|
|
|
as most builtin objects rely on "hidden" mechanics and there is no way
|
|
|
|
|
to combine or inherit them.
|
|
|
|
|
|
|
|
|
|
To illustrate:
|
2020-05-03 03:03:18 +03:00
|
|
|
```javascript
|
2020-05-03 15:18:41 +03:00
|
|
|
// produces an Array that looks like a function but does not act like one...
|
|
|
|
|
var a = Reflect.construct(Array, [], Function)
|
|
|
|
|
|
|
|
|
|
// creates a function that looks like an array...
|
|
|
|
|
var b = Reflect.construct(Function, [], Array)
|
2020-05-03 03:03:18 +03:00
|
|
|
```
|
|
|
|
|
|
2020-05-03 15:18:41 +03:00
|
|
|
So these will produce partially broken instances:
|
2020-05-03 03:03:18 +03:00
|
|
|
```javascript
|
2020-05-03 15:18:41 +03:00
|
|
|
var A = object.Constructor('A', Array, function(){ .. })
|
|
|
|
|
|
|
|
|
|
var B = object.Constructor('B', Array, {
|
2020-05-03 03:03:18 +03:00
|
|
|
__call__: function(){ .. },
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2020-05-03 15:18:41 +03:00
|
|
|
Essentially this issue and the inability to implement it without
|
|
|
|
|
emulation, shows the side-effects of two "features" in _JavaScript_:
|
|
|
|
|
- lack of multiple inheritance
|
|
|
|
|
- _hidden_ protocols/functionality (namely: calls, attribute access)
|
|
|
|
|
|
|
|
|
|
Still, this is worth some thought.
|
2020-05-03 03:03:18 +03:00
|
|
|
|
|
|
|
|
|
2019-07-16 23:49:32 +03:00
|
|
|
|
2020-08-01 22:05:46 +03:00
|
|
|
## More
|
|
|
|
|
|
|
|
|
|
For more info see the [source...](./object.js)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-07-16 23:49:32 +03:00
|
|
|
## License
|
|
|
|
|
|
|
|
|
|
[BSD 3-Clause License](./LICENSE)
|
|
|
|
|
|
2020-05-30 05:47:41 +03:00
|
|
|
Copyright (c) 2016-2020, Alex A. Naanou,
|
2019-07-16 23:49:32 +03:00
|
|
|
All rights reserved.
|
|
|
|
|
|
2020-05-24 23:59:01 +03:00
|
|
|
|
2020-04-25 01:14:00 +03:00
|
|
|
<!-- vim:set ts=4 sw=4 spell : -->
|