2019-07-16 23:49:32 +03:00
|
|
|
# object.js
|
|
|
|
|
|
|
|
|
|
object.js provides a set of tools for constructing and maintaining object
|
2020-04-25 01:14:00 +03:00
|
|
|
constructors and for managing their inheritance relations.
|
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:
|
|
|
|
|
- simple way to define normal and class methods, properties and attributes,
|
|
|
|
|
- uniform and minimalistic definition syntax based on basic JavaScript
|
|
|
|
|
object syntax no special cases or special syntax,
|
2020-04-25 01:14:00 +03:00
|
|
|
- _transparently_ based on _JavaScript's_ prototypical inheritance model,
|
2019-07-16 23:49:32 +03:00
|
|
|
- more granular instance construction (a-la _Python's_ `.__new__(..)`
|
|
|
|
|
and `.__init__(..)` methods)
|
|
|
|
|
- less restrictive:
|
|
|
|
|
- `new` is optional
|
|
|
|
|
- all input components are reusable
|
|
|
|
|
|
|
|
|
|
Disadvantages compared to the `class` syntax:
|
2020-04-25 01:14:00 +03:00
|
|
|
- no _syntactic sugar_
|
2020-04-25 01:43:31 +03:00
|
|
|
- slightly more complicated calling of _super_ or `parent` methods
|
2019-07-16 23:49:32 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## Usage
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
var object = require('ig-object')
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Create a basic constructor...
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// NOTE: new is optional here...
|
|
|
|
|
var A = new object.Constructor('A', {})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In _JavaScript_ constructor `B` inherits from constructor `A` iff
|
|
|
|
|
`B.prototypes` is _prototype_ of `A.prototype`. So to implement inheritance
|
|
|
|
|
we simply need to _link_ the prototypes of two constructors via `.__proto__`,
|
|
|
|
|
`Object.create(..)` or other means.
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// NOTE: we could simply use A() or new A() here but that would call
|
|
|
|
|
// the active constructors if they are defined which might not be
|
|
|
|
|
// desirable at definition time...
|
|
|
|
|
var B = object.Constructor('B', {__proto__: A.prototype})
|
|
|
|
|
var C = object.Constructor('C', Object.create(B.prototype))
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
var c = C() // or new C()
|
|
|
|
|
|
|
|
|
|
c instanceof C // -> true
|
|
|
|
|
c instanceof B // -> true
|
|
|
|
|
c instanceof A // -> true
|
|
|
|
|
```
|
|
|
|
|
|
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
|
|
|
//
|
|
|
|
|
// Base
|
|
|
|
|
// ^
|
|
|
|
|
// |
|
|
|
|
|
// Item
|
|
|
|
|
//
|
2019-07-17 00:26:39 +03:00
|
|
|
var Base = object.Constructor('Base', {
|
2020-04-15 00:42:20 +03:00
|
|
|
proto_attr: 'prototype attr value',
|
|
|
|
|
|
2019-07-17 00:26:39 +03:00
|
|
|
get prop(){
|
2020-04-15 00:42:20 +03:00
|
|
|
return 'propery value' },
|
|
|
|
|
|
2019-07-17 00:26:39 +03:00
|
|
|
method: function(){
|
|
|
|
|
console.log('Base.method()') },
|
|
|
|
|
|
|
|
|
|
// initializer...
|
|
|
|
|
__init__: function(){
|
2020-04-15 00:42:20 +03:00
|
|
|
this.instance_attr = 'instance'
|
2019-07-17 00:26:39 +03:00
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
var Item = object.Constructor('Item', {
|
2019-07-17 00:29:52 +03:00
|
|
|
// inherit from Base...
|
2019-07-17 00:26:39 +03:00
|
|
|
__proto__: Base.prototype,
|
|
|
|
|
|
|
|
|
|
__init__: function(){
|
|
|
|
|
// call the "super" method...
|
|
|
|
|
object.parent(this.__init__, this).call(this)
|
2020-04-15 00:42:20 +03:00
|
|
|
this.item_attr = 'instance attribute value'
|
2019-07-17 00:26:39 +03:00
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
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
|
|
|
|
|
// callable instance constructor...
|
|
|
|
|
var Action = object.Constructor('Action',
|
2020-04-25 01:43:31 +03:00
|
|
|
// constructor as a function...
|
2020-04-10 18:30:55 +03:00
|
|
|
function(context, ...args){
|
|
|
|
|
// return the instance...
|
|
|
|
|
return this
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
var action = new Action()
|
|
|
|
|
|
|
|
|
|
action()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// a different way to do the above...
|
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', {
|
|
|
|
|
__call__: function(context, ...args){
|
|
|
|
|
return this
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
2020-04-25 01:43:31 +03:00
|
|
|
In the above cases both the base function and the `.__call__(..)` method
|
|
|
|
|
receive a `context` argument in addition to `this` context, those represent
|
|
|
|
|
the two contexts relevant to the callable instance:
|
|
|
|
|
- Internal context (`this`)
|
|
|
|
|
This always references the instance being called
|
|
|
|
|
- External context (`context`)
|
|
|
|
|
This is the object the instance is called from (`window` or `global` by
|
|
|
|
|
default), i.e. _the thing before the dot_
|
|
|
|
|
|
|
|
|
|
If the prototype is explicitly defined as a function then it is the
|
|
|
|
|
user's responsibility to call .__call__(..) method.
|
|
|
|
|
|
|
|
|
|
|
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', {
|
|
|
|
|
__new__: function(context, ...args){
|
|
|
|
|
return {}
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
```
|
2019-07-16 23:49:32 +03:00
|
|
|
|
2020-04-25 01:43:31 +03:00
|
|
|
Like function constructor and `.__call__(..)` 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.
|
|
|
|
|
The external context is the same as above.
|
|
|
|
|
|
|
|
|
|
Contexts:
|
|
|
|
|
- Internal context (`this`)
|
|
|
|
|
References the `.prototype` of the constructor.
|
|
|
|
|
- External context (`context`)
|
|
|
|
|
This is the object the instance is called from (`window` or `global` by
|
|
|
|
|
default), i.e. _the thing before the dot_, the same as for function
|
|
|
|
|
constructor and `.__call__(..)`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The value `.__new__(..)`returns is used as the instance and gets linked
|
|
|
|
|
in the prototype chain.
|
|
|
|
|
|
|
|
|
|
This has priority over the callable protocols above, thus the user must
|
|
|
|
|
take care of both the _prototype as function_ and `prototype.__call__(..)`
|
|
|
|
|
handling.
|
|
|
|
|
|
|
|
|
|
|
2019-07-16 23:49:32 +03:00
|
|
|
## Components
|
|
|
|
|
|
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>)
|
|
|
|
|
-> <list>
|
|
|
|
|
```
|
|
|
|
|
|
2020-04-25 01:43:31 +03:00
|
|
|
Get parent method
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
|
|
|
|
parent(<method>, <this>)
|
|
|
|
|
parent(<method>, <name>, <this>)
|
|
|
|
|
-> <parent-method>
|
|
|
|
|
```
|
|
|
|
|
|
2020-04-25 01:43:31 +03:00
|
|
|
Mixin objects into a prototype chain
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
|
|
|
|
mixin(<root>, <object>, ...)
|
|
|
|
|
-> <object>
|
|
|
|
|
```
|
|
|
|
|
|
2020-04-25 01:43:31 +03:00
|
|
|
Mixin contents of objects into one
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
|
|
|
|
mixinFlat(<root>, <object>, ...)
|
|
|
|
|
-> <object>
|
|
|
|
|
```
|
2020-04-25 01:43:31 +03:00
|
|
|
This is like `Object.assign(..)` but copies property objects rather than
|
|
|
|
|
property values.
|
|
|
|
|
|
2019-07-16 23:49:32 +03:00
|
|
|
|
2020-04-25 01:43:31 +03:00
|
|
|
Define an object constructor
|
2019-07-16 23:49:32 +03:00
|
|
|
```
|
2020-04-10 18:30:55 +03:00
|
|
|
Constructor(<name>, <prototype>)
|
|
|
|
|
Constructor(<name>, <class-prototype>, <prototype>)
|
2019-07-16 23:49:32 +03:00
|
|
|
-> <constructor>
|
|
|
|
|
```
|
|
|
|
|
|
2020-04-25 01:52:00 +03:00
|
|
|
Shorthand to `Constructor(..)`
|
|
|
|
|
```
|
|
|
|
|
C(<name>, ..)
|
|
|
|
|
-> <constructor>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Utilities
|
|
|
|
|
|
|
|
|
|
Align text to shortest leading whitespace
|
|
|
|
|
```
|
|
|
|
|
normalizeIndent(<text>)
|
|
|
|
|
normalizeIndent(<text>, <tab-size>)
|
|
|
|
|
-> <text>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This is used to format `.toString(..)` return values for nested functions
|
|
|
|
|
to make source printing in console more pleasant to read.
|
2019-07-16 23:49:32 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
|
|
|
|
[BSD 3-Clause License](./LICENSE)
|
|
|
|
|
|
2019-07-17 00:26:39 +03:00
|
|
|
Copyright (c) 2019, Alex A. Naanou,
|
2019-07-16 23:49:32 +03:00
|
|
|
All rights reserved.
|
|
|
|
|
|
2020-04-25 01:14:00 +03:00
|
|
|
<!-- vim:set ts=4 sw=4 spell : -->
|