
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
ig-object
Advanced tools
_object.js_ is a set of tools and abstractions to create and manage constructors, objects and prototype chains in idiomatic JavaScript.
object.js is a set of tools and abstractions to create and manage constructors, objects and prototype chains in idiomatic JavaScript.
This is an alternative to the ES6 class syntax in JavaScript and provides
several advantages:
.__new__(..) and .__init__(..) methods),.__call__(..)),new is optional,Disadvantages compared to the class syntax:
parent (super) methods.Note that the produced constructors and objects are functionally identical (almost) to the ones produced via ES6 classes and are interchangeable with them.
Here is a basic comparison:
|
object.js
|
ES6
|
STOP / STOP(..)ASIS(..)Constructor(..) / C(..)create(..) / Constructor.create(..)sources(..) / Constructor.sources(..)entries(..) / Constructor.entries(..)values(..) / Constructor.values(..)parent(..) / Constructor.parent(..)parentProperty(..) / Constructor.parentProperty(..)parentCall(..) / Constructor.parentCall(..)parentOf(..) / childOf(..) / related(..) and Constructor.*(..) variantsRawInstance(..)Mixin(..)<mixin>(..)<mixin>.mode<mixin>.mixout(..)<mixin>.isMixed(..)mixin(..) / Mixin.mixin(..)mixinFlat(..) / Mixin.mixinFlat(..)mixout(..) / Mixin.mixout(..)mixins(..) / Mixin.mixins(..)hasMixin(..) / Mixin.hasMixin(..)$ npm install ig-object
Or just download and drop object.js into your code.
Include the code, this is compatible with both node's and
RequireJS' require(..)
var object = require('ig-object')
Create a basic constructor...
// NOTE: new is optional here...
var A = new object.Constructor('A', {})
var B = object.Constructor('B', A, {})
var C = object.Constructor('C', B, {})
Now we can test this...
var c = C() // or new C()
c instanceof C // -> true
c instanceof B // -> true
c instanceof A // -> true
Note:
object.Constructor('X', A) the second argument is used as the
prototype, to use A as a parent constructor add an empty object
as a third argument, i.e. 'object.Constructor('X', A, {})'Constructor(..) / C(..) for more info)//
// Base <--- Item <--- SubItem
//
var Base = object.Constructor('Base', {
proto_attr: 'prototype attr value',
get prop(){
return 'propery value' },
method: function(){
console.log('Base.method()') },
// initializer...
__init__: function(){
this.instance_attr = 'instance'
},
})
var Item = object.Constructor('Item', Base, {
method: function(){
// ...
// call the "super" method...
return object.parentCall(Item.prototype, 'method', this, ...arguments)
},
__init__: function(...args){
// call the "super" method...
object.parentCall(this.__init__, this, ...args)
this.item_attr = 'instance attribute value'
},
})
var SubItem = object.Constructor('SubItem', Item, {
// ...
})
var Action = object.Constructor('Action',
// constructor as a function...
function(context, ...args){
// return the instance...
return this
})
// a more flexible approach...
//
// 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.
var Action2 = object.Constructor('Action2', {
__call__: function(context, ...args){
// call the callable parent...
return object.parentCall(Action2.prototype, '__call__', this, ...arguments)
},
})
var action = Action()
var action2 = new Action2()
// the instances are now functions...
action()
action2()
In the above cases both the function constructor and the .__call__(..)
method receive a context argument in addition to this context, those
represent the two contexts relevant to the callable instance:
this)context)
This is the object the instance is called from, i.e. the call context
(window or global by default)If the prototype is explicitly defined as a function then it is the
user's responsibility to call .__call__(..) method.
When calling the parent passing '__call__' will get the parent in both
the function and .__call__(..) implementations, but extra care must be
taken in passing the reference prototype to .parentCall(..), the instance
is implemented as a proxy function that will pass the arguments to the
implementation (i.e. this.constructor.prototype(..)) so this proxy
function as well as the .constructor.prototype(..) are valid implementations
and both will be retrieved by sources(this, '__call__'),
values(this, '__call__') and by extension parent(this, '__call__')
and friends, so this is another reason not to use this in the general
case.
Notes:
The two approaches (function vs. .__call__(..)) will produce
functionally identical but structurally different constructors/objects,
the difference is in .prototype -- what is defined as the prototype
is the prototype (POLS), so we get:
.prototype is that exact function object,.__call__(..) -> .prototype is the object with the .__call__(..)
method.The instance in both cases is a function wrapper that will proxy the call to the corresponding implementation. (this may change in the future)
Making an object callable does not guarantee that <obj> instanceof Function
will be true, though typeof(<obj>) == 'function'will always work.
To satisfy the instanceof Function test the prototype tree must be
rooted in Function.
Prototype-based mixin...
var utilityMixin = {
utility: function(){
// ...
},
}
var Base = object.Constructor('Base')
// normal instance prototype chain:
// b -> Base.prototype -> ..
//
var b = Base()
// mixin directly into the instance...
//
// now the prototype chain looks like this:
// b -> mixinFlat({}, utilityMixin) -> Base.prototype -> ..
//
object.mixin(b, utilityMixin)
.mixin(..) will copy the contents of utilityMixin into the prototype
chain between b and b.__proto__.
We can also remove the mixin...
o.mixout(b, utilityMixin)
The mixed-in data is removed iff a matching
object is found in the chain with the same attributes as utilityMixin and
with each attribute matching identity with the corresponding attribute in
the mixin.
Constructor-based mixin...
var UtilityMixin = function(parent){
return object.Constructor(parent.name + '+utils', parent, utilityMixin) }
var Mixed = object.Constructor('Mixed', UtilityMixin(Base), {
// ...
})
var m = Mixed()
Notes:
.mixin(..) into constructors directly, use
.mixinFlat(..) instead.var LowLevel = object.Constructor('LowLevel', {
__new__: function(context, ...args){
return {}
},
})
The value .__new__(..) returns is used as the instance and gets linked
to the prototype chain by the calling constructor's .__rawinstance__(..),
the constructor then will call .__init__(..) if defined.
Note that .__init__(..) is called by the constructor and not by
RawInstance(..) or .__rawinstance__(..).
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:
this).prototype of the constructor.context)
This is the object the instance is called from, i.e. the call context
(window or global by default), the same as for function constructor
and .__call__(..).This has priority over the callable protocols above, thus the user must
take care of both the function constructor and prototype.__call__(..)
handling.
var C = object.Constructor('C', {
// this will get mixed into the constructor C...
constructor_attr: 123,
constructorMethod: function(){
// ...
},
// ...
}, {
instanceMethod: function(){
// get constructor data...
var x = this.constructor.constructor_attr
// ...
},
// ...
})
And the same thing while extending...
var D = object.Constructor('D', C, {
// ...
}, {
// ...
})
var myArray = object.Constructor('myArray', Array, {
// ...
})
All special methods and protocols defined by object.js except for
.__new__(..) will work here without change.
For details on .__new__(..) and native .constructor(..) interaction
see: Extending native .constructor(..)
.constructor(..)Extending .constructor(..) is not necessary in most cases as
.__init__(..) will do everything generally needed, except for instance
replacement.
var myArray = object.Constructor('myArray', Array, {
__new__: function(context, ...args){
var obj = Reflect.construct(myArray.__proto__, args, myArray)
// ...
return obj
},
})
<object>.__new__(..)Create new instance object.
<object>.__new__(<context>, ..)
-> <instance>
This is called in the context of <constructor> as at time of call
no instance exists yet.
<context> is the outer context of the call, i.e. the object from which
<constructor> was referenced before it was called.
Any value returned by .__new__(..) will be integrated into the prototype
chain of <object>, if this is not desired then wrap it in
object.ASIS(..) before returning, but note that this will
not prevent <object>.__init__(..) from being called. The ASIS(..)-wrapped
value will be unwrapped before being returned by the constructor.
For more info see:
<object>.__init__(..)Initialize the instance.
<object>.__init__(..)
Return value is ignored.
<object>.__call__(..)Call the object.
<object>.__call__(<context>, ..)
-> <result>
This is called in the context of <object>.
<context> is the outer context of the call, i.e. the object from which
<object> was referenced before it was called.
For more info see: Callable instances
Note that all of the following are generic and will work on any relevant JavaScript object.
For example, this will happily create a normal native array object
['a', 'b', 'c']:
var l = object.RawInstance(null, Array, 'a', 'b', 'c')
STOP / STOP(..)Used in sources(..),
values(..) and
mixins(..)
to stop the search before it reaches the top of
the prototype chain.
ASIS(..)Can be used in .__new__(..) to wrap the returned object to
prevent changing it's prototype by RawInstance().
Constructor(..) / C(..)Define an object constructor
Constructor(<name>)
Constructor(<name>, <prototype>)
Constructor(<name>, <parent-constructor>, <prototype>)
Constructor(<name>, <parent-constructor>, <constructor-mixin>, <prototype>)
Constructor(<name>, <constructor-mixin>, <prototype>)
-> <constructor>
Constructor(..) essentially does the following:
.name and .toString(..) for introspection,.__rawinstance__(..) wrapper to RawInstance(..).__proto__, .prototype and .prototype.constructor,The resulting constructor function when called will:
.__rawinstance__(..) if defined or RawInstance(..)
to create an instance,.__init__(..) if present.Note that Constructor(<name>, <prototype>) is intentionally set as default
instead of having the parent-constructor as the last argument, this is
done for two reasons:
Constructor(<name>, <prototype>) is more common than
empty inheritance.Shorthand to Constructor(..)
C(<name>, ..)
-> <constructor>
Constructor(..) / C(..) and their products can be called with and without
new.
create(..) / Constructor.create(..)Create a new object from the given
create(<base>)
-> <obj>
For functions we can set .name
create(<name>, <base-func>)
-> <func>
This is similar to Object.create(..) but handles callables correctly, i.e. if
<base> is a callable then <obj> will also be callable.
<obj> respects the call protocol, and will call <obj>.__call__(..) if defined.
sources(..) / Constructor.sources(..)Iterate the sources for attribute
sources(<object>)
sources(<object>, <name>)
-> <iterator>
If no name is given iterate through all the parents.
Special case: get callable implementations
sources(<object>, '__call__')
-> <iterator>
This will iterate the callable implementations regardless of the actual
implementation details, i.e. both function prototype or .__call__(..)
methods will be matched.
entries(..) / Constructor.entries(..)Iterate <soruce>-<value> pairs for attribute in the prototype chain.
entries(<object>, <name>)
-> <iterator>
Iterate property descriptors for attribute in prototype chain
entries(<object>, <name>, true)
-> <iterator>
Special case: get callable implementations
entries(<object>, '__call__')
-> <iterator>
This will yield the callable objects themselves or the value of .__call__.
values(..) / Constructor.values(..)Iterate values for attribute in prototype chain
values(<object>, <name>)
-> <iterator>
Iterate property descriptors for attribute in prototype chain
values(<object>, <name>, true)
-> <list>
Special case: get callable implementations
values(<object>, '__call__')
-> <list>
This will yield the callable objects themselves or the value of .__call__.
See sources(..) for docs on callback(..)
and special cases.
parent(..) / Constructor.parent(..)Get parent attribute value or method
parent(<prototype>, <name>)
-> <parent-value>
-> undefined
It is recommended to use the relative<constructor>.prototype as
<prototype> and in turn not recommended to use this or this.__proto__
as they will not provide the appropriate reference point in the prototype
chain for the current method and may result in infinite recursion.
For access to parent methods the following special case is better.
parent(<method>, <this>)
-> <parent-method>
-> undefined
Edge case: The parent(<method>, ..) has one potential pitfall -- in
the rare case where a prototype chain contains two or more references
to the same method under the same name, parent(..) can't distinguish
between these references and will always return the second one.
Special case: get the parent callable implementation
parent(<prototype>, '__call__')
-> <parent-value>
-> undefined
See sources(..) for more info on the
special case.
parentProperty(..) / Constructor.parentProperty(..)Get parent property descriptor
parentProperty(<prototype>, <name>)
-> <prop-descriptor>
-> undefined
parentCall(..) / Constructor.parentCall(..)Get parent method and call it
parentCall(<prototype>, <name>, <this>)
-> <result>
-> undefined
parentCall(<method>, <this>)
-> <result>
-> undefined
Special case: call the parent callable implementation
parentCall(<prototype>, '__call__', <this>)
-> <result>
-> undefined
See parent(..) and
sources(..) for more details.
parentOf(..) / childOf(..) / related(..) and Constructor.*(..) variantsTest if a is parent of b and/or vice-versa.
parentOf(<parent>, <child>)
-> <bool>
childOf(<child>, <parent>)
-> <bool>
related(<a>, <b>)
-> <bool>
These are similar to instanceof but will test if the two objects are in the
same prototype chain and in case of parentOf(..)/childOf(..) in what order.
RawInstance(..)Make a raw (un-initialized) instance
RawInstance(<context>, <constructor>, ..)
-> <object>
RawInstance(..) will do the following:
.__new__(..) if defined, or.__call__(..) is defined, create a
wrapper function, or.__proto__ has a .__rawinstance__(..) use it
to create an instance, or.__proto__ is a function (constructor) use it
to create an instance, or{}.Un-initialized means this will not call .__init__(..)
RawInstance(..) can be called with and without new.
Mixin(..)Create a mixin wrapper.
Mixin(<name>, <obj>, ..)
-> <mixin>
This will create a more convenient <mixin> object.
The following two are the same
var mixin = {
// ...
}
var obj = mixinFlat({
// ...
}, mixin)
and
var mixin = Mixin('mixin', {
// ...
})
var obj = mixin('flat', {
// ...
})
The former approach is better suited for inline mixing in, where one could
use Object.assign(..) while the later is more convenient for working with
library and reusable mixin object as it is more readable and more centralized.
This also makes combining mixins simpler
var A = Mixin('A', {
// ...
})
var B = Mixin('B', {
// ...
})
// this is a combination of A and B...
var C = Mixin('C', A, B, {
// NOTE: this "block" is optional...
// ...
})
Note that for multiple mixins used in Mixin(..) as well as in
mixin(..)/mixinFlat(..),
mixins from right to left, e.g. in the above example B will overwrite
intersecting data in A, ... etc.
<mixin>(..)Mixin into <target> as a prototype
<mixin>(<target>)
<mixin>('proto', <target>)
-> <target>
Mixin into <target> directly (flatly)
<mixin>('flat', <target>)
-> <target>
These are similar to using mixin(..) or
mixinFlat(..) respectively.
<mixin>.modeSets the default mode for <mixin>(..).
Can be:
protomixin(..)flatmixinFlat(..)<mixin>.mixout(..)Remove <mixin> from <target>
<mixin>.mixout(<target>)
-> <target>
This is the same as mixout(..)
<mixin>.isMixed(..)Check if <mixin> is mixed into <target>
<mixin>.isMixed(<target>)
-> <bool>
This is the same as hasMixin(..)
mixin(..) / Mixin.mixin(..)Mixin objects into a prototype chain
mixin(<base>, <object>, ..)
-> <base>
This will link the base .__proto__ to the last mixin in chain,
keeping the prototype visibility the same.
This will copy the content of each input object without touching the objects themselves, making them fully reusable.
It is not recommended to .mixin(..) into constructors directly, use
.mixinFlat(..) instead.
mixinFlat(..) / Mixin.mixinFlat(..)Mixin contents of objects into one base object
mixinFlat(<base>, <object>, ..)
-> <base>
This is like Object.assign(..) but copies property descriptors rather
than property values.
Also like Object.assign(..) this will overwrite attribute values in
<base>.
mixout(..) / Mixin.mixout(..)Remove the first match matching input mixin from base of base
mixout(<base>, <object>, ..)
mixout(<base>, 'first', <object>, ..)
-> <base>
Remove all occurrences of each matching input mixin from base
mixout(<base>, 'all', <object>, ..)
-> <base>
This is the opposite of mixin(..)
mixins(..) / Mixin.mixins(..)Get matching mixins
mixins(<base>, <object>)
mixins(<base>, [<object>, ..])
mixins(<base>, <object>, <callback>)
mixins(<base>, [<object>, ..], <callback>)
-> list
callback(<match>, <object>, <parent>)
-> STOP
-> undefined
-> <value>
See sources(..) for docs on callback(..)
hasMixin(..) / Mixin.hasMixin(..)Check if base object has mixin
hasMixin(<base>, <mixin>)
-> <bool>
deepKeys(..) / Constructor.deepKeys(..)deepKeys(<obj>)
-> <keys>
deepKeys(<obj>, <stop>)
-> <keys>
This is like Object.keys(..) but will get the keys from the whole
prototype chain or until <stop> if given.
match(..) / Constructor.match(..)Test if the two objects match in attributes and attribute values
match(<base>, <obj>)
-> <bool>
This relies on first level object structure to match the input object, for a successful match one of the following must apply:
or:
typeof matches and,Non-strict match
match(<base., <obj>, true)
-> <bool>
Like the default case but uses equality instead of identity to match values.
matchPartial(..) / Constructor.matchPartial(..)matchPartial(<base>, <obj>)
-> <bool>
// non-strict version...
matchPartial(<base>, <obj>, true)
-> <bool>
Like .match(..) but will check for a partial match, i.e. when obj is
a non-strict subset of base.
At this point we can't mix native types, for example it is not possible
to make a callable Array object...
This is not possible in current JavaScript implementations directly as most builtin objects rely on "hidden" mechanics and there is no way to combine or inherit them.
To illustrate:
// produces an Array that looks like a function but does not act like one...
var a = Reflect.construct(Array, [], Function)
// creates a function that looks like an array...
var b = Reflect.construct(Function, [], Array)
So these will produce partially broken instances:
var A = object.Constructor('A', Array, function(){ .. })
var B = object.Constructor('B', Array, {
__call__: function(){ .. },
})
Essentially this issue and the inability to implement it without emulation, shows the side-effects of two "features" in JavaScript:
Still, this is worth some thought.
For more info see the source...
Copyright (c) 2016-2021, Alex A. Naanou,
All rights reserved.
FAQs
_object.js_ is a set of tools and abstractions to create and manage constructors, objects and prototype chains in idiomatic JavaScript.
The npm package ig-object receives a total of 3 weekly downloads. As such, ig-object popularity was classified as not popular.
We found that ig-object demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.