major semantic change, should not break anything -- really odd...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-05-03 03:03:18 +03:00
parent 4822e39ed7
commit b57759d40f
3 changed files with 142 additions and 63 deletions

View File

@ -25,10 +25,13 @@ Disadvantages compared to the `class` syntax:
- Slightly more complicated calling of `parent` (_super_) methods
There are some other limitations to this currently, for more info see
the [relevant section](#limitations).
## Installation
```bash
```shell
$ npm install ig-object
```
@ -59,9 +62,9 @@ we simply need to _link_ the prototypes of two constructors via `.__proto__`,
`Object.create(..)` or other means.
```javascript
var B = object.Constructor('B', {__proto__: A.prototype})
var B = object.Constructor('B', { __extends__: A })
var C = object.Constructor('C', Object.create(B.prototype))
var C = object.Constructor('C', B, {})
```
Now we can test this...
@ -96,7 +99,7 @@ var Base = object.Constructor('Base', {
var Item = object.Constructor('Item', {
// inherit from Base...
__proto__: Base.prototype,
__extends__: Base,
__init__: function(){
// call the "super" method...
@ -106,6 +109,9 @@ var Item = object.Constructor('Item', {
},
})
var SubItem = object.Constructor('SubItem', Item, {
// ...
})
```
@ -152,6 +158,14 @@ If the prototype is explicitly defined as a function then it is the
user's responsibility to call `.__call__(..)` method.
**Notes:**
- the two approaches (_function_ vs. `.__call__(..)`) will produce
slightly different results, the difference is in `.prototype`, in the
first case it is a _function_ while in the second an object with a
`.__call__(..)` method.
(this may change in the future)
## Advanced usage
@ -198,60 +212,50 @@ handling.
### Extending the constructor
The `constructor.__proto__` should be callable, _object.js_ will by design
make no effort to either maintain nor test for this.
```javascript
var D = object.Constructor('D',
object.mixinFlat(function(){}, {
constructor_attr: 'some value',
var C = object.Constructor('C',
// this will get mixed into the constructor C...
{
constructor_attr: 123,
constructorMethod: function(){
// ...
},
// ...
}),
{
instance_attr: 'some other value',
}, {
instanceMethod: function(){
// get constructor data...
var x = this.constructor.constructor_attr
// ...
},
// ...
})
```
Keeping the class prototype a function is not necessary, but not doing
so will break the `D instanceof Function` test.
Here is another less strict approach but here `D.__proto__` will not be
callable:
And the same thing while extending...
```javascript
var D = object.Constructor('D',
var D = object.Constructor('D',
// this will get mixed into C(..)...
{
__proto__: Function,
__extends__: C,
// ...
},
{
}, {
// ...
})
```
Passing a simple object as a constructor prototype will work too, but
will neither pass the `D instanceof Function` test nor be callable and
thus is not recommended.
Note that `.__extends__` can be written in either block, this is done
for convenience and to keep it as close as possible to the definition top.
### Inheriting from native constructor objects
```javascript
var myArray = object.Constructor('myArray', Array, {
__proto__: Array.prototype,
var myArray = object.Constructor('myArray', {
__extends__: Array,
// ...
})
@ -271,8 +275,8 @@ Extending `.constructor(..)` is not necessary in most cases as
replacement.
```javascript
var myArray = object.Constructor('myArray', Array, {
__proto__: Array.prototype,
var myArray = object.Constructor('myArray', {
__extends__: Array,
__new__: function(context, ...args){
var obj = Reflect.construct(myArray.__proto__, args, myArray)
@ -363,7 +367,8 @@ Define an object constructor
```
Constructor(<name>)
Constructor(<name>, <prototype>)
Constructor(<name>, <class-prototype>, <prototype>)
Constructor(<name>, <parent-constructor>, <prototype>)
Constructor(<name>, <constructor-mixin>, <prototype>)
-> <constructor>
```
@ -375,6 +380,29 @@ C(<name>, ..)
```
## Limitations
### Can not mix unrelated native types directly
At this point we can't mix native types, i.e. it is not possible to make
a callable `Array`...
For example this will produce a broken instance:
```javascript
var CallablaArray = object.Constructor('CallablaArray', Array, function(){ .. })
```
This will produce a broken instance in a different way:
```javascript
var CallablaArray = object.Constructor('CallablaArray', Array, {
__call__: function(){ .. },
})
```
Some of this is due to how _object.js_ is currently implemented, this
needs further investigation...
## Utilities
Align text to shortest leading whitespace

109
object.js
View File

@ -322,6 +322,14 @@ function(root, ...objects){
// unneccessary restrictions both on the "class" object and on the
// instance...
//
// XXX the following are not the same:
// var C = Constructor('C', function(){ .. })
// and
// var C2 = Constructor('C2', { __call__: function(){ .. } })
// the difference is in C.prototype vs. C2.prototype, the first
// being a function while the second is an object with a call
// method...
// Q: should the two cases produce the same result???
// XXX Q: should the context (this) in .__new__(..) be _constructor or
// .prototype???
// ... .prototype seems to be needed more often but through it we
@ -373,12 +381,8 @@ function(context, constructor, ...args){
: Reflect.construct(Object, [], constructor)
// link to prototype chain, if not done already...
if(obj.__proto__ !== constructor.prototype){
obj.__proto__ = constructor.prototype
Object.defineProperty(obj, 'constructor', {
value: constructor,
enumerable: false,
}) }
obj.__proto__ !== constructor.prototype
&& (obj.__proto__ = constructor.prototype)
return obj }
@ -389,12 +393,13 @@ function(context, constructor, ...args){
// Constructor(name, proto)
// -> constructor
//
// Make a constructor with a prototype (object/function) and a class
// prototype...
// Constructor(name, class-proto, proto)
// Make a constructor with a prototype and a constructor prototype...
// Constructor(name, constructor-mixin, proto)
// -> constructor
//
// Make a constructor with prototype extending parent-constructor...
// Constructor(name, parent-constructor, proto)
// -> constructor
// NOTE: the <class-proto> defines a set of class methods and
// attributes.
//
//
// The resulting constructor can produce objects in one of these ways:
@ -432,6 +437,16 @@ function(context, constructor, ...args){
//
//
//
// Special attributes:
// .__extends__
// Shorthand to define define the prototype constructor.
// Constructor('X', {__extends__: Y})
// is the same as:
// Constructor('X', Y, {})
// This can be defined on either the prototype or the constructor
// mixin but not on both.
//
//
// Special methods (constructor):
//
// Handle uninitialized instance construction
@ -469,13 +484,9 @@ function(context, constructor, ...args){
// // NOTE: new is optional...
// var A = new Constructor('A')
//
// // NOTE: in a prototype chain the prototypes are "inherited"
// // NOTE: JS has no classes and the prototype is just another
// // object, the only difference is that it's used by the
// // constructor to link other objects i.e. "instances" to...
// var B = Constructor('B', {__proto__: A.prototype})
// var B = Constructor('B', { __extends__: A })
//
// var C = Constructor('C', Objec.create(B.prototype))
// var C = Constructor('C', B, {})
//
// var c = C()
//
@ -510,6 +521,9 @@ function(context, constructor, ...args){
// NOTE: to disable .__rawinstance__(..) handling set it to false in the
// class prototype...
//
// XXX BUG:
// // this does not make a callable array...
// X = Constructor('X', Array, function(){})
// XXX revise .toString(..) definition test...
var Constructor =
module.Constructor =
@ -518,9 +532,43 @@ module.C =
function Constructor(name, a, b){
var proto = b == null ? a : b
proto = proto || {}
var cls_proto = b == null ? b : a
var constructor_mixin = b == null ? b : a
var constructor_proto
// the actual constructor...
// handle:
// Constructor(name, constructor, { .. })
if(constructor_mixin instanceof Function){
constructor_proto = constructor_mixin
constructor_mixin = null
proto.__proto__ === ({}).__proto__
&& (proto.__proto__ = constructor_proto.prototype)
// handle:
// Constructor(name, { .. })
// Constructor(name, { .. }, { .. })
} else {
// handle .__extends__
a = Object.hasOwnProperty.call(proto, '__extends__')
&& proto.__extends__
b = constructor_mixin != null
&& Object.hasOwnProperty.call(constructor_mixin, '__extends__')
&& constructor_mixin.__extends__
// sanity check...
if(!!a && !!b){
throw new Error('Constructor(..): '
+'only one of prototype.__extends__ or constructor.__extends__ '
+'can exist.') }
constructor_proto = !!a ? a : b
// cleanup...
if(!!b){
constructor_mixin = mixinFlat({}, constructor_mixin)
delete constructor_mixin.__extends__ }
!!constructor_proto
&& (proto.__proto__ = constructor_proto.prototype)
}
// the constructor base...
var _constructor = function Constructor(){
// create raw instance...
var obj = _constructor.__rawinstance__ ?
@ -543,8 +591,8 @@ function Constructor(name, a, b){
// set .toString(..)...
// NOTE: do this only if .toString(..) is not defined by user...
// XXX revise this test...
;((cls_proto || {}).toString === Function.toString
|| (cls_proto || {}).toString === ({}).toString)
;((constructor_mixin || {}).toString === Function.toString
|| (constructor_mixin || {}).toString === ({}).toString)
&& Object.defineProperty(_constructor, 'toString', {
value: function(){
var args = proto.__init__ ?
@ -561,21 +609,24 @@ function Constructor(name, a, b){
return `${this.name}(${args})${normalizeIndent(code)}` },
enumerable: false,
})
_constructor.__proto__ = cls_proto === undefined ?
_constructor.__proto__
: cls_proto
_constructor.prototype = proto
// set generic raw instance constructor...
_constructor.__rawinstance__ instanceof Function
|| (_constructor.__rawinstance__ =
function(context, ...args){
return makeRawInstance(context, this, ...args) })
!!constructor_proto
&& (_constructor.__proto__ = constructor_proto)
_constructor.prototype = proto
// NOTE: this is intentionally last, this enables the user to override
// any of the system methods...
// NOTE: place the non-overridable definitions after this...
!!constructor_mixin
&& mixinFlat(
_constructor,
constructor_mixin)
// set constructor.prototype.constructor
Object.defineProperty(_constructor.prototype, 'constructor', {
value: _constructor,
enumerable: false,
})
_constructor.prototype.constructor = _constructor
return _constructor }

View File

@ -1,6 +1,6 @@
{
"name": "ig-object",
"version": "2.7.2",
"version": "3.0.0",
"description": "",
"main": "object.js",
"scripts": {