Adds several methods to Object, Function and Promise that are frequently used
With nodejs:
Create your project using this package.json. After downloaded, run npm install
and start writing your application:
In the browser:
For browser-usage, ITSA has a predefined loaderfiles. Once included, a global ITSA
object with default features is available. For customized loaderfiles, read: Customized build.
<script src="/itsabuild-min.js"></script>
The forEach
method added functional programing style looping through arrays, as did several other methods such as map
, or some
. That functionality is sorely lacking in Object
which has the added complication of having to check via hasOwnProperty
to see if the named member belongs to the object or has been inherited.
This module adds some easy to use methods to Object and a few to Function that allow for a kind of classical inheritance without ES6.
All this functionality is present in several libraries as functions that operate on the affected objects. We think it makes no longer sense to do it this way. The new methods are being added as non-enumerable methods so that they should not show when looping through an object. Besides, when looping through an object, you should always have had to check for hasOwnProperties
which would not list these.
Object has the following new methods. They all check for hasOwnProperty
or equivalent functionality.
Like Array's forEach
it loops through an object and calls the given function, optionally in the given context, passing it the value of the property, the name of the property and a copy of the whole object, a similar order of arguments to forEach
.
{a:1, b:2}.each(function(value, key) {
console.log(key + ': ' value);
});
// Prints
// a: 1
// b: 2
Like Array's some
will loop through an object until the called function returns true. The function will return true if any of the calls returned true (the condition was met) or false if no function returned true.
console.log({a:1, b:2}.some(function(value, key) {
return value === 2;
}));
// Prints true
Like the similarly named method of Array, it will loop through an object returning a new object with the same keys but the values as returned by the function. The function cannot change the keys themselves, the function receives them just for reference. If the function returns undefined
that property will not be included in the resulting object.
console.log({a:1, b:2}.each(function(value, key) {
return value * 2;
});
// Prints
// {a: 2, b: 4}
Returns an array with the names of the properties in the object.
console.log({a: 1, b: 2}.keys());
// prints ['a', 'b']
// Same as:
console.log(Object.keys({a: 1, b: 2}));
Returns the number of keys in the object.
console.log({a: 15, b: 25}.size());
// prints 2
Transforms the object into an array with 'key/value' objects
console.log({country: 'USA', Continent: 'North America'}.toArray());
// prints [{key: 'country', value: 'USA'}, {key: 'Continent', value: 'North America'}]
Returns an array with the values of the properties.
console.log({a: 1, b: 2}.values());
// prints [1, 2]
Returns true if the object has no properties of its own.
console.log({}.isEmpty()); // true
console.log({a:1}.isEmpty()); // false
Returns a shallow clone of the object. Properties that are themselves references will not be traversed, thus, the cloned object would have a reference to the same object as the original. It is handy and fast for objects that are known not to be deep.
If an object has a few properties known to contain shallow objects, it is easy to do a fast slightly deeper clone like this:
var obj = {a: 1, b: 2, deep: {c:3, d:4}};
var newObj = obj.shallowClone();
newObj.deep = obj.deep.shallowClone();
Returns a deep copy of the object. Only handles members of primary types, Dates, Arrays and Objects.
Merges into the object a set of properties taken from another object. Properties with the same name will be preserved unless the second argument is passed as true. The original object is changed. The method is chainable.
var a = {a: 1, b: 2};
a.merge({b:99, c: 3}).merge({a:44, d:4}, true);
console.log(a);
// Prints:
// {a: 44, b:2, c: 3, d: 4}
Compares this object with the reference-object whether they have the same value. Not by reference, but their content as simple types.
Sets the properties of obj
to the instance. This will redefine the object, while remaining the instance.
Merges array
into this array (appended by default).
Empties the Object by deleting all its own properties (also non-enumerable).
(static method)
Returns a new object resulting of merging the properties of the objects passed as its arguments. If several objects have properties with the same name, the first one will prevail.
A useful example is filling up a configuration object with default values.
var init = function (config) {
config = Object.merge(config, defaultConfig);
}
The config
argument can be missing but after calling Object.merge
it will always be an object. In either case the returned object will have the properties in defaultConfig
filling in the missing properties in config
.
(static method)
Returns true when an object passed. Will return false for Array, Functions, RegExp, Date and Error objects.
var a = {};
isObj = Object.isObject(a);
(static method)
Returns a new object with the prototype specified by proto
.
var a = {
b: 10;
};
var proto = {
c: 20;
};
newObj = Object.newProto(a, proto);
String gets extended with the following new methods:
Checks if the string ends with the value specified by test
.
Checks if the string can be parsed into a number when using parseInt()
Checks if the string starts with the value specified by test
.
Performs {placeholder}
substitution on a string.
Returns a ISO-8601 Date-object build by the String's value.
Generated the string without any white-spaces at the start or end.
Generated the string without any white-spaces at the beginning.
Generated the string without any white-spaces at the end.
Validates if the String's value represents a valid emailaddress.
Validates if the String's value represents a valid floated number.
Validates if the String's value represents a hexadecimal color.
Validates if the String's value represents a valid integer number.
Validates if the String's value represents a valid URL.
Array gets extended with the following new methods:
Checks whether an item is inside the Array. Alias for (array.indexOf(item) > -1).
Removes an item from the array.
Returns a deep copy of the Array. Only handles members of primary types, Dates, Arrays and Objects.
Shuffles the items in the Array randomly.
Sets the items of array
to the instance. This will refill the array, while remaining the instance.
Empties the Array by setting its length to zero.
Math gets extended with the following new methods:
Returns the value, while forcing it to be inbetween the specified edges.
Floors a value in the direction to zero.
Ceils a value from zero up.
Parses a stringified object and creates true Date
properties.
Transforms String
-properties into true Date-objects in case they match the Date-syntax.
(static method)
Promise.chainFns could be seen as the chained-version of Promise.all(). There is a big difference though: you need to pass an array of function- or Promise-references, not invoked Promises. These should be references, because Promise.chainFns() will invoke them when the time is ready.
The returnvalue of the functions is irrelevant. But if one of the functions returns a Promise, the chain will wait its execution for this function to be resolved. If one of the items returns a rejected Promise, the whole chain rejects by default, unless the second argument (finishAll) is set true. Preceding functions won't be invoked.
p1 = function() {
return Promise.resolve(5);
};
// note that p2 returns a simple type, not a Promise
p2 = function(amount) {
// amount===5 --> passed through by p1
return 10*amount;
};
p3 = function(amount) {
// amount===50 --> passed through by p2
return Promise.resolve(amount*5);
};
Promise.chainFns([p1, p2, p3]).then(
function(total) {
// total==250;
}
);
(static method)
Promise.finishAll returns a Promise that always fulfills. It is fulfilled when all items are resolved (either fulfilled or rejected).
This is useful for waiting for the resolution of multiple promises, such as reading multiple files in Node.js or making multiple XHR requests in the browser. Because -on the contrary of Promise.all
- finishAll waits until all single Promises are resolved, you can handle all promises, even if some gets rejected.
p1 = IO.send('/sendSMS', smsData1);
p2 = IO.send('/sendSMS', smsData2);
p3 = IO.send('/sendSMS', smsData3);
simulateRejectedP2 = p2.then(function() {
throw new Error('we simulate the IO failed');
});
Promise.finishAll([p1, simulateRejectedP2, p3]).then(
function(response) {
// all SMS is send, either succesfully or with failures
}
);
If you need to examine individual responses, response
has 2 properties: response.fulfilled and response.rejected: both are arrays with the same length: each position hold the returnvalue
, or undefined
if the returnvalue is present in the other array.
(static method)
Promises are meant to hold state. They can be pending or resolved and are supposed to resolve from the inside. The don't have a way to communicate by any handler and can't be resolved from outside without making workarrounds.
Promise.manage(callbackFn
) returns a new Promise that is supposed to be managable from outside. You can pass in one argument: callbackFn. Promise.manage returns a new Promise which has three handlers:
You can invoke promise.callback() which will invoke the original passed-in callbackFn - if any, or the callback which is set at a later time using setCallback()
. The method promise.fulfill() and promise.reject() are meant to resolve the promise from outside, just like deferred can do.
var promise = Promise.manage(
function(msg) {
alert(msg);
}
);
promise.then(
function() {
// promise is fulfilled, no further actions can be taken
}
);
setTimeout(function() {
promise.callback('hey, I\'m still busy');
}, 1000);
setTimeout(function() {
promise.fulfill();
}, 2000);
Note: the thennable (promise.then()) does not have these three methods: the thennable is a different Promise. You shouldn't need it at that point anyway, for the Promise is resolved at that stage.
Every Promise-instance gets a .finally()-method at its prototype. You can call p.finally() at the very end of a chain, even after .catch().
This method will invoke the callback function regardless whether the chain resolves or rejects.
setBodyMask(); // some function which makes a mask visible
p = IO.send('/sendSMS', smsData);
p.then(
function() {
alert('sms is send');
},
function() {
alert('failed to send sms');
}
)
.finally(hideBodyMask);
// hideBodyMask is some function that hides the mask
setBodyMask();
IO.send('/sendSMS', smsData)
.then(
function() {
alert('sms is send');
}
)
.catch(
function(err) {
alert(err.message);
}
)
.finally(hideBodyMask);
Every Promise-instance gets a .thenFulfill()-method at its prototype. It is an alternative to .then in a way that it is fulfilled promise. Should the original promise be rejected, then .thenFulfill is fulfilled (with the rejected reason as argument).
This method is useful if you are in a Promise-chain where you want to get into the fulfilled chain, even if the chain got rejected before. It is comparable with .finally() only now you get a Promise in return which can use inside the chain.
smsToUser1 = IO.send('/sendSMS', smsData1);
smsToUser2 = IO.send('/sendSMS', smsData2);
smsAdministratorConfirmation = IO.send('/sendSMS', smsConfirmation);
simulateRejectedSMS = smsToUser2.then(function() {
throw new Error('we simulate the IO failed');
});
setBodyMask();
smsToUser1
.then(smsToUser1)
.then(simulateRejectedSMS)
.thenFulfill(smsAdministratorConfirmation) // will always be invoked
.finally(hideBodyMask);
Table of Contents