mirror of
https://github.com/flynx/object.js.git
synced 2025-10-30 11:00:08 +00:00
added Mixin(..) + some tweaks...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
9b24acc4ee
commit
735ecf65fd
410
object.js
410
object.js
@ -568,178 +568,6 @@ function(a, b){
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
|
||||||
// Mixin utils...
|
|
||||||
|
|
||||||
// Mix a set of methods/props/attrs into an object...
|
|
||||||
//
|
|
||||||
// mixinFlat(base, object, ...)
|
|
||||||
// -> base
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// NOTE: essentially this is just like Object.assign(..) but copies
|
|
||||||
// properties directly rather than copying property values...
|
|
||||||
// NOTE: this will not transfer several the special variables not listed
|
|
||||||
// by Object.keys(..).
|
|
||||||
// This includes things like .__proto__
|
|
||||||
// NOTE: this can and will overwrite attributes...
|
|
||||||
var mixinFlat =
|
|
||||||
module.mixinFlat =
|
|
||||||
function(base, ...objects){
|
|
||||||
return objects
|
|
||||||
.reduce(function(base, cur){
|
|
||||||
Object.keys(cur)
|
|
||||||
.map(function(k){
|
|
||||||
Object.defineProperty(base, k,
|
|
||||||
Object.getOwnPropertyDescriptor(cur, k)) })
|
|
||||||
return base }, base) }
|
|
||||||
|
|
||||||
|
|
||||||
// Mix sets of methods/props/attrs into an object as prototypes...
|
|
||||||
//
|
|
||||||
// mixin(base, object, ..)
|
|
||||||
// -> base
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// This will create a new object per set of methods given and
|
|
||||||
// mixinFlat(..) the method set into this object leaving the
|
|
||||||
// original objects intact.
|
|
||||||
//
|
|
||||||
// base <-- object1_copy <-- .. <-- objectN_copy <- base.__proto__
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// NOTE: this will only mix in non-empty objects...
|
|
||||||
// NOTE: mixing into a constructor will break object creation via new...
|
|
||||||
// Example:
|
|
||||||
// class A {}
|
|
||||||
// class B extends A {}
|
|
||||||
//
|
|
||||||
// mixin(B, {x: 123})
|
|
||||||
//
|
|
||||||
// var b = new B() // will break...
|
|
||||||
//
|
|
||||||
// This does not affect object.Constructor(..) chains...
|
|
||||||
// NOTE: mixin(Object.prototype, ..) will fail because Object.prototype.__proto__
|
|
||||||
// is imutable...
|
|
||||||
var mixin =
|
|
||||||
module.mixin =
|
|
||||||
function(base, ...objects){
|
|
||||||
base.__proto__ = objects
|
|
||||||
.reduce(function(res, cur){
|
|
||||||
return Object.keys(cur).length > 0 ?
|
|
||||||
module.mixinFlat(Object.create(res), cur)
|
|
||||||
: res }, base.__proto__)
|
|
||||||
return base }
|
|
||||||
|
|
||||||
|
|
||||||
// Get matching mixins...
|
|
||||||
//
|
|
||||||
// mixins(base, object[, callback])
|
|
||||||
// mixins(base, list[, callback])
|
|
||||||
// -> list
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// callback(base, obj, parent)
|
|
||||||
// -> STOP
|
|
||||||
// -> undefined
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// NOTE: this will also match base...
|
|
||||||
// NOTE: if base matches directly callback(..) will get undefined as parent
|
|
||||||
// NOTE: for more docs on the callback(..) see sources(..)
|
|
||||||
var mixins =
|
|
||||||
module.mixins =
|
|
||||||
function(base, object, callback){
|
|
||||||
object = object instanceof Array ?
|
|
||||||
object
|
|
||||||
: [object]
|
|
||||||
var res = []
|
|
||||||
var o
|
|
||||||
var parent
|
|
||||||
while(base != null){
|
|
||||||
// match each object...
|
|
||||||
for(var obj of object){
|
|
||||||
if(match(base, obj)){
|
|
||||||
o = callback
|
|
||||||
&& callback(base, obj, parent)
|
|
||||||
// manage results...
|
|
||||||
res.push(
|
|
||||||
(o === undefined || o === module.STOP) ?
|
|
||||||
[base]
|
|
||||||
: o instanceof module.STOP ?
|
|
||||||
o.value
|
|
||||||
: o )
|
|
||||||
if(o === module.STOP
|
|
||||||
|| o instanceof module.STOP){
|
|
||||||
return res.flat() }
|
|
||||||
// match found, no need to test further...
|
|
||||||
break } }
|
|
||||||
parent = base
|
|
||||||
base = base.__proto__ }
|
|
||||||
return res.flat() }
|
|
||||||
|
|
||||||
|
|
||||||
// Check of base has mixin...
|
|
||||||
//
|
|
||||||
// hasMixin(base, mixin)
|
|
||||||
// -> bool
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// NOTE: to test for a flat mixin directly use .matchPartial(base, object)
|
|
||||||
var hasMixin =
|
|
||||||
module.hasMixin =
|
|
||||||
function(base, object){
|
|
||||||
return (
|
|
||||||
// normal mixin...
|
|
||||||
mixins(base, object, function(){ return module.STOP })
|
|
||||||
.length > 0
|
|
||||||
// flat mixin search...
|
|
||||||
|| sources(base, function(p){
|
|
||||||
return matchPartial(p, object) ?
|
|
||||||
module.STOP
|
|
||||||
: [] })
|
|
||||||
.length > 0 )}
|
|
||||||
|
|
||||||
|
|
||||||
// Mix-out sets of methods/props/attrs out of an object prototype chain...
|
|
||||||
//
|
|
||||||
// Mix-out first occurrence of each matching object...
|
|
||||||
// mixout(base, object, ..)
|
|
||||||
// mixout(base, 'first', object, ..)
|
|
||||||
// -> base
|
|
||||||
//
|
|
||||||
// Mix-out all occurrences of each matching object...
|
|
||||||
// mixout(base, 'all', object, ..)
|
|
||||||
// -> base
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// NOTE: this is the opposite to mixin(..)
|
|
||||||
// NOTE: this used mixins(..) / match(..) to find the relevant mixins,
|
|
||||||
// see those for more info...
|
|
||||||
var mixout =
|
|
||||||
module.mixout =
|
|
||||||
function(base, ...objects){
|
|
||||||
var all = objects[0] == 'all' ?
|
|
||||||
!!objects.shift()
|
|
||||||
: objects[0] == 'first' ?
|
|
||||||
!objects.shift()
|
|
||||||
: false
|
|
||||||
var remove = []
|
|
||||||
mixins(base, objects, function(match, obj, parent){
|
|
||||||
parent && remove.push(parent)
|
|
||||||
// when removing the first occurrence, don't check for obj again...
|
|
||||||
all || objects.splice(objects.indexOf(obj), 1) })
|
|
||||||
// NOTE: we are removing on a separate stage so as not to mess with
|
|
||||||
// mixins(..) iterating...
|
|
||||||
remove
|
|
||||||
// XXX not sure why this is needed, needs thought...
|
|
||||||
.reverse()
|
|
||||||
.forEach(function(p){
|
|
||||||
p.__proto__ = p.__proto__.__proto__ })
|
|
||||||
return base }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
// Constructor...
|
// Constructor...
|
||||||
|
|
||||||
@ -789,6 +617,9 @@ function(context, constructor, ...args){
|
|||||||
var _mirror_doc = function(func, target){
|
var _mirror_doc = function(func, target){
|
||||||
Object.defineProperty(func, 'toString', {
|
Object.defineProperty(func, 'toString', {
|
||||||
value: function(...args){
|
value: function(...args){
|
||||||
|
// user-defined .toString...
|
||||||
|
if(target.prototype.toString !== Function.prototype.toString){
|
||||||
|
return target.prototype.toString.call(this, ...args) }
|
||||||
var f = typeof(target.prototype) == 'function' ?
|
var f = typeof(target.prototype) == 'function' ?
|
||||||
target.prototype
|
target.prototype
|
||||||
: target.prototype.__call__
|
: target.prototype.__call__
|
||||||
@ -1059,8 +890,8 @@ function Constructor(name, a, b, c){
|
|||||||
&& obj.__init__(...arguments)
|
&& obj.__init__(...arguments)
|
||||||
return obj }
|
return obj }
|
||||||
|
|
||||||
_constructor.name = name
|
Object.defineProperty(_constructor, 'name', { value: name })
|
||||||
// just in case the browser refuses to change the name, we'll make
|
// just in case the browser/node refuses to change the name, we'll make
|
||||||
// it a different offer ;)
|
// it a different offer ;)
|
||||||
_constructor.name == 'Constructor'
|
_constructor.name == 'Constructor'
|
||||||
// NOTE: this eval(..) should not be a risk as its inputs are
|
// NOTE: this eval(..) should not be a risk as its inputs are
|
||||||
@ -1132,7 +963,8 @@ function Constructor(name, a, b, c){
|
|||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
// For more info see .sources(..) above...
|
// For more info see .sources(..) above...
|
||||||
module.STOP = Constructor('STOP', {
|
module.STOP =
|
||||||
|
Constructor('STOP', {
|
||||||
doc: 'stop iteration.',
|
doc: 'stop iteration.',
|
||||||
__init__: function(value){
|
__init__: function(value){
|
||||||
this.value = value },
|
this.value = value },
|
||||||
@ -1140,6 +972,234 @@ module.STOP = Constructor('STOP', {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// Mixin utils...
|
||||||
|
|
||||||
|
// Mix a set of methods/props/attrs into an object...
|
||||||
|
//
|
||||||
|
// mixinFlat(base, object, ...)
|
||||||
|
// -> base
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// NOTE: essentially this is just like Object.assign(..) but copies
|
||||||
|
// properties directly rather than copying property values...
|
||||||
|
// NOTE: this will not transfer several the special variables not listed
|
||||||
|
// by Object.keys(..).
|
||||||
|
// This includes things like .__proto__
|
||||||
|
// NOTE: this can and will overwrite attributes...
|
||||||
|
var mixinFlat =
|
||||||
|
module.mixinFlat =
|
||||||
|
function(base, ...objects){
|
||||||
|
return objects
|
||||||
|
.reduce(function(base, cur){
|
||||||
|
Object.keys(cur)
|
||||||
|
.map(function(k){
|
||||||
|
Object.defineProperty(base, k,
|
||||||
|
Object.getOwnPropertyDescriptor(cur, k)) })
|
||||||
|
return base }, base) }
|
||||||
|
|
||||||
|
|
||||||
|
// Mix sets of methods/props/attrs into an object as prototypes...
|
||||||
|
//
|
||||||
|
// mixin(base, object, ..)
|
||||||
|
// -> base
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// This will create a new object per set of methods given and
|
||||||
|
// mixinFlat(..) the method set into this object leaving the
|
||||||
|
// original objects intact.
|
||||||
|
//
|
||||||
|
// base <-- object1_copy <-- .. <-- objectN_copy <- base.__proto__
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// NOTE: this will only mix in non-empty objects...
|
||||||
|
// NOTE: mixing into a constructor will break object creation via new...
|
||||||
|
// Example:
|
||||||
|
// class A {}
|
||||||
|
// class B extends A {}
|
||||||
|
//
|
||||||
|
// mixin(B, {x: 123})
|
||||||
|
//
|
||||||
|
// var b = new B() // will break...
|
||||||
|
//
|
||||||
|
// This does not affect object.Constructor(..) chains...
|
||||||
|
// NOTE: mixin(Object.prototype, ..) will fail because Object.prototype.__proto__
|
||||||
|
// is imutable...
|
||||||
|
var mixin =
|
||||||
|
module.mixin =
|
||||||
|
function(base, ...objects){
|
||||||
|
base.__proto__ = objects
|
||||||
|
.reduce(function(res, cur){
|
||||||
|
return Object.keys(cur).length > 0 ?
|
||||||
|
module.mixinFlat(Object.create(res), cur)
|
||||||
|
: res }, base.__proto__)
|
||||||
|
return base }
|
||||||
|
|
||||||
|
|
||||||
|
// Get matching mixins...
|
||||||
|
//
|
||||||
|
// mixins(base, object[, callback])
|
||||||
|
// mixins(base, list[, callback])
|
||||||
|
// -> list
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// callback(base, obj, parent)
|
||||||
|
// -> STOP
|
||||||
|
// -> undefined
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// NOTE: this will also match base...
|
||||||
|
// NOTE: if base matches directly callback(..) will get undefined as parent
|
||||||
|
// NOTE: for more docs on the callback(..) see sources(..)
|
||||||
|
var mixins =
|
||||||
|
module.mixins =
|
||||||
|
function(base, object, callback){
|
||||||
|
object = object instanceof Array ?
|
||||||
|
object
|
||||||
|
: [object]
|
||||||
|
var res = []
|
||||||
|
var o
|
||||||
|
var parent
|
||||||
|
while(base != null){
|
||||||
|
// match each object...
|
||||||
|
for(var obj of object){
|
||||||
|
if(match(base, obj)){
|
||||||
|
o = callback
|
||||||
|
&& callback(base, obj, parent)
|
||||||
|
// manage results...
|
||||||
|
res.push(
|
||||||
|
(o === undefined || o === module.STOP) ?
|
||||||
|
[base]
|
||||||
|
: o instanceof module.STOP ?
|
||||||
|
o.value
|
||||||
|
: o )
|
||||||
|
if(o === module.STOP
|
||||||
|
|| o instanceof module.STOP){
|
||||||
|
return res.flat() }
|
||||||
|
// match found, no need to test further...
|
||||||
|
break } }
|
||||||
|
parent = base
|
||||||
|
base = base.__proto__ }
|
||||||
|
return res.flat() }
|
||||||
|
|
||||||
|
|
||||||
|
// Check of base has mixin...
|
||||||
|
//
|
||||||
|
// hasMixin(base, mixin)
|
||||||
|
// -> bool
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// NOTE: to test for a flat mixin directly use .matchPartial(base, object)
|
||||||
|
var hasMixin =
|
||||||
|
module.hasMixin =
|
||||||
|
function(base, object){
|
||||||
|
return (
|
||||||
|
// normal mixin...
|
||||||
|
mixins(base, object, function(){ return module.STOP })
|
||||||
|
.length > 0
|
||||||
|
// flat mixin search...
|
||||||
|
|| sources(base, function(p){
|
||||||
|
return matchPartial(p, object) ?
|
||||||
|
module.STOP
|
||||||
|
: [] })
|
||||||
|
.length > 0 )}
|
||||||
|
|
||||||
|
|
||||||
|
// Mix-out sets of methods/props/attrs out of an object prototype chain...
|
||||||
|
//
|
||||||
|
// Mix-out first occurrence of each matching object...
|
||||||
|
// mixout(base, object, ..)
|
||||||
|
// mixout(base, 'first', object, ..)
|
||||||
|
// -> base
|
||||||
|
//
|
||||||
|
// Mix-out all occurrences of each matching object...
|
||||||
|
// mixout(base, 'all', object, ..)
|
||||||
|
// -> base
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// NOTE: this is the opposite to mixin(..)
|
||||||
|
// NOTE: this used mixins(..) / match(..) to find the relevant mixins,
|
||||||
|
// see those for more info...
|
||||||
|
var mixout =
|
||||||
|
module.mixout =
|
||||||
|
function(base, ...objects){
|
||||||
|
var all = objects[0] == 'all' ?
|
||||||
|
!!objects.shift()
|
||||||
|
: objects[0] == 'first' ?
|
||||||
|
!objects.shift()
|
||||||
|
: false
|
||||||
|
var remove = []
|
||||||
|
mixins(base, objects, function(match, obj, parent){
|
||||||
|
parent && remove.push(parent)
|
||||||
|
// when removing the first occurrence, don't check for obj again...
|
||||||
|
all || objects.splice(objects.indexOf(obj), 1) })
|
||||||
|
// NOTE: we are removing on a separate stage so as not to mess with
|
||||||
|
// mixins(..) iterating...
|
||||||
|
remove
|
||||||
|
// XXX not sure why this is needed, needs thought...
|
||||||
|
.reverse()
|
||||||
|
.forEach(function(p){
|
||||||
|
p.__proto__ = p.__proto__.__proto__ })
|
||||||
|
return base }
|
||||||
|
|
||||||
|
|
||||||
|
// Mixin wrapper/object...
|
||||||
|
//
|
||||||
|
// Create a new mixin...
|
||||||
|
// Mixin(name, data, ..)
|
||||||
|
// -> mixin
|
||||||
|
//
|
||||||
|
// Apply the mixin to an object...
|
||||||
|
// mixin(obj)
|
||||||
|
// -> obj
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// var BasicMixin = Mixin('BasicMixin', {
|
||||||
|
// ...
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// var o = {
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// BasicMixin(o)
|
||||||
|
//
|
||||||
|
//
|
||||||
|
var Mixin =
|
||||||
|
module.Mixin =
|
||||||
|
Constructor('Mixin', {
|
||||||
|
name: null,
|
||||||
|
data: null,
|
||||||
|
|
||||||
|
mode: 'proto',
|
||||||
|
|
||||||
|
isMixed: function(target){
|
||||||
|
return hasMixin(target, this.data) },
|
||||||
|
mixout: function(target){
|
||||||
|
return mixout(target, this.data) },
|
||||||
|
|
||||||
|
// mixin to target...
|
||||||
|
__call__: function(_, target, mode=this.mode){
|
||||||
|
return mode == 'flat' ?
|
||||||
|
mixinFlat(target, this.data)
|
||||||
|
: mixin(target, this.data) },
|
||||||
|
|
||||||
|
__init__: function(name, ...data){
|
||||||
|
// NOTE: .defineProperty(..) is used because this is a function
|
||||||
|
// and function's .name is not too configurable...
|
||||||
|
// XXX do we need to configure this better???
|
||||||
|
Object.defineProperty(this, 'name', { value: name })
|
||||||
|
this.data = mixinFlat({},
|
||||||
|
...data.map(function(e){
|
||||||
|
return e instanceof Mixin ?
|
||||||
|
e.data
|
||||||
|
: e })) },
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
/**********************************************************************
|
||||||
* vim:set ts=4 sw=4 : */ return module })
|
* vim:set ts=4 sw=4 : */ return module })
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ig-object",
|
"name": "ig-object",
|
||||||
"version": "5.3.0",
|
"version": "5.4.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "object.js",
|
"main": "object.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user