mirror of
https://github.com/flynx/object.js.git
synced 2025-10-29 18:40:08 +00:00
major semantic change, should not break anything -- really odd...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
4822e39ed7
commit
b57759d40f
94
README.md
94
README.md
@ -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
109
object.js
@ -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 }
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ig-object",
|
||||
"version": "2.7.2",
|
||||
"version": "3.0.0",
|
||||
"description": "",
|
||||
"main": "object.js",
|
||||
"scripts": {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user