node-plus (or just plus) is a toolbox for nodejs that adds a bunch of functionalities to make node programming easier.
plus is dual licensed under the MIT or GPL Version 2 licenses.
plus is heavily tested internally (more than 160 unique assertions in unit tests) but bugs undoubtedly exist! Don't hesitate to report them and make pull requests if you feel like helping.
Ever dreamed you could use arguments.forEach and cringed at the acrobatics involved? plus makes all the standard Array methods available to Array-like objects. It also provides two separate means to mark objects or even entire classes as Array-like.
plus also introduces the flatten methods for Arrays and Array-like objects, a super useful deep forEach/map kind of method.
Furthermore, when it makes sense, plus provides an implementation of Array methods for non-Array-like objects. So you can use forEach, map, filter and more onto plain objects!
Never understood why typeof new String() returns "object"? Fear not, plus's typeOf( new String() ) will return "string" just like jQuery.type() does!
Arguments object are recognized as "arguments", Regular Expression object as "regexp", etc.
plus adds extend, deepExtend and hiddenExtend for all your objects merging needs. Dealing with objects has never been easier.
With Array-like objects getting so much love, you'll be happy to know you can use isArrayLike() to determine if your object will behave like an Array or not: very useful when a function parameter can be an Array, an Arguments object or any other kind of Array-like object!
Just use npm: npm install plus
As soon as possible in your project just put require( "plus" );
.
Since plus will declare the global function typeOf
and augment Object
and its prototype, it will "propagate" to the rest of your project. Some people may see this as undesirable and, for any other purpose, I would agree, but it's quite unavoidable given the very nature of what plus does.
Object.prototype.extend( obj1, ..., objN )
will add the properties of
obj1
toobjN
onto the current object. In case of properties already present, they will be overwritten by the newest value.
Object.prototype.deepExtend( obj1, ..., objN )
same as
extend
except that the process is recursive when values of overwritten properties are objects.
Object.prototype.flatten( [ false, ] [ callback ] )
flattens an Array or Array-like object. When called on a non-Array-like object, return the object in an Array:
[ [ 1, 2 ], 3, [ [ 4 ], [ 5 ] ] ].flatten()
will return[ 1, 2, 3, 4, 5 ]
{}.flatten()
will return[ {} ]
if
callback
is given it acts as a data filter, the returned value is used to fill the resulting array. If an exception is thrown, the value is simply skipped and left out of the resulting array. For instance, the following code will return[ 4, 8 ]
:[ [ 1, 2 ], 3, [ [ 4 ], [ 5 ] ] ].flatten(function( value ) { if ( value % 2 ) { throw "odd"; } return 2 * value; });
if
false
is given as first argument, then no array is constructed (flatten
will returnundefined
) butcallback
will be called nonetheless:var tmp = 0; [ [ 1, 2 ], 3 ].flatten(function( value ) { tmp += value; }); console.log( tmp ); // outputs 6
Object.prototype.hiddenExtend( obj1, ..., objN )
same as
extend
except the added properties won't be iterable using afor..in
statement.
Object.prototype.isArrayLike()
returns true if an Object is an Array or an Array-like Object, false otherwise.
typeOf( obj )
returns the type of
obj
. This is equivalent to thetypeof
operator for non-Objects but will return the correct type for instantiated values. For instance,typeOf( new String() )
will return"string"
not"object"
.
The following table shows for which type of Object each Array method is implemented:
Method | Array-like Objects | Objects |
---|---|---|
concat |
YES | NO |
every |
YES | YES |
filter |
YES | YES |
flatten |
YES | YES |
forEach |
YES | YES |
indexOf |
YES | YES |
join |
YES | YES |
lastIndexOf |
YES | YES |
map |
YES | YES |
pop |
YES | NO |
push |
YES | NO |
reduce |
YES | YES |
reduceRight |
YES | YES |
reverse |
YES | YES |
shift |
YES | NO |
slice |
YES | NO |
splice |
YES | NO |
some |
YES | YES |
sort |
YES | YES |
unshift |
YES | NO |
For those methods rewritten for Plain Objects, the algorithm is as much of a direct translation as possible with the notable exception of sort
which, while sorting according to the value, will keep the key/value correspondances of the object. For instance, { "a": 2, "b": 1 }.sort();
will change the object into { "b": 1, "a": 2 }
.
Please note that, because of a BUG (Google devs can argue all they want, three years and more than a hundred comments later, it is still a bug in V8), the engine does not ensure consistent ordering of properties in Objects when one or more keys are numbers (or parsable as numbers, which is even worse). As such, some of the methods will probably not work as you would expect if you use numbers as keys. Let's pray the V8 devs get back to their senses one day. In the meantime, don't use numbers as keys in non-Array-like objects.
The simplest way to tag a Class as Array-like is to add a __array_like__
boolean property set to true to its prototype. We recommand using Object.defineProperty
so that the property is not iterable using a for...in
statement:
Object.defineProperty( MyConstructor.prototype, "__array_like__", { "value": true } );
If your code is not prototype-based (but rather uses Plain Objects created in closures), you can create the property onto the Object itself:
Object.defineProperty( myPlainObject, "__array_like__", { "value": true } );
If you're using Objects from a third-party library and can't (or don't want to) change the library's code, look up what value typeOf
returns for this type of Objects and add an entry on the Object.arrayTypes
map:
Object.arrayTypes[ "thetype" ] = true;
plus will take care of the rest from there! ;)