mirror of
				https://github.com/flynx/object.js.git
				synced 2025-10-30 11:00: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
					
				
							
								
								
									
										90
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								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', | ||||
| 	// 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