A minimalistic javascript constant object library. It utilises Object.freeze
to prevent
mutations, however it does deep-freeze objects making them truly constant.
The constant object is identical (obj === constant(obj)
), so it does in fact freeze the original
objects. Accessing properties can therefore be done in the standard JS way (obj.prop
or obj[propname]
).
There are also convenience access methods and mutation methods supported making handling of constant object hierarchies easy.
In general this library shares common tree branches in hierarchies. This goes so far as to return the same object from alteration methods that do not actually modify the object where this is easily detected.
npm install --save constant.js
npm test
This library has 100% test coverage. Pull requests are always welcome, however they will only be accepted if the test-coverage remains at 100%.
Converts an object to an constant version.
Parses a JSON string into an constant object.
Tests if an object has been made constant.
Tests if an object is an Array and has been made constant.
Tests if an object is a plain Object and has been made constant.
Tests if an object is a Date and has been made constant.
Tests if an object is a RegExp and has been made constant.
These methods a available on all constant objects whether they be plain Objects, Arrays or Dates. All prototype methods remain available, but using modifying methods (if they have not been implemented here) will throw in strict mode or not have an effect in non-strict mode.
This method performs a deep mutation on an object by calling one of its child's methods.
Example:
var obj = constant({ a: { b: [ new Date(0) ] } });
var res = obj.mutate([ 'a', 'b', '0' ], 'setFullYear', 1900);
JSON.stringify(res); // { "a": { "b": [ "1900-01-01T00:00:00.000Z"] } }
This method gets a value deep inside an object hierarchy.
Example:
var obj = constant({ a: { b: { c: 'value' } } });
obj.get('a', 'b', 'c'); // 'value'
This method sets a value deep inside an object hierarchy and returns a new constant object containing the change. The original constant remains unchanged. This method ensures that all values that remain unchanged will be reused. If the "change" isn't actually a change the return value will be the same object.
Example:
var obj = constant({ a: { b: { c: 'value' } }, d: { other: 'branch' } });
var chg = obj.set('a', 'b', 'c', 'new-value');
chg.d === obj.d; // true
chg.a.b.c; // 'new-value'
obj.a.b.c; // 'value'
chg = obj.set('a', 'b', 'c', 'value');
chg === obj; // true
Creates a shallow mutable version of the object. It's children in the hierarchy remain constant.
Example:
var obj = constant({ a: { b: { c: 'value' } }, d: { other: 'branch' } });
var mut = obj.mutable();
constant.isObject(mut); // false
constant.isObject(mut.a); // true
constant.isObject(mut.d); // true
mut.f = 'hallo';
mut.f; // 'hallo';
Creates a deep mutable version of the object. This means that the entire hierarchy will be mutable.
It will retain cyclical integrity provided there is the global Map
builtin.
If that is not the case, then a call on a cyclical constant object will loop indefinitely!
Only use cyclical structures if you have a modern environment!
Example:
var obj = constant({ a: { b: { c: 'value' } }, d: { other: 'branch' } });
var mut = obj.mutable();
constant.isObject(mut); // false
constant.isObject(mut.a); // false
constant.isObject(mut.d); // false
mut.f = 'hallo';
mut.f; // 'hallo';
Similar to Object.assign
in that it merges object into a constant object. It then returns a new constant object where
the properties of the argument objects have been merged in.
var obj = constant({ a: 'test', b: { c: true } });
var mut = obj.assign({ a: 'hallo', d: { e: 5 } });
constant.isObject(mut); // true
constant.isObject(mut.b); // true
constant.isObject(mut.d); // true
obj.b === mut.b; // true
JSON.stringify(mut); // {"a":"hallo","b":{"c":true},"d":{"e":5}}
Works just like Array.prototype.map
except on an object and that it returns a new constant object and
allows removal of an element by returning undefined
.
Example:
var obj = constant({ a: 1, b: 2, c: 3, d: 4, e: 5 });
mut = obj.transform(function(val, key) {
return key === 'b' ? undefined : (val * 2);
});
constant.isObject(mut); // true
JSON.stringify(mut); // {"a": 2,"c":6,"d":8,"e":10}
mut = obj.transform(function(val) {
return val;
});
obj === mut; //true
All methods available trough Array.prototype
are available on constant Array objects as well except that
modifying methods have been replaced with ones returning a new constant Array object instead.
Just like the regular Array.prototype.splice
method except that rather than returning the
deleted elements, It returns a new constant Array with the changes applied. Also inserted
elements will become constant.
Example:
var arr = constant([1, 2, 3, 4, 5]);
var mut = arr.splice(1,3, 'element');
constant.isArray(mut); // true
JSON.stringify(mut); // '[ 1, "element", 5 ]'
Just like the regular Array.prototype.concat
method except that the resulting array is constant.
Example:
var arr = constant([1, 2, 3 ]);
var mut = arr.concat([ 4, 5, 6 ]);
constant.isArray(mut); // true
JSON.stringify(mut); // '[ 1, 2, 3, 4, 5, 6 ]'
Just like the regular Array.prototype.reverse
method except that the resulting array is constant.
Example:
var arr = constant([1, 2, 3, 4, 5, 6 ]);
var mut = arr.reverse();
constant.isArray(mut); // true
JSON.stringify(mut); // '[ 6, 5, 4, 3, 2, 1 ]'
Just like the regular Array.prototype.sort
method except that the resulting array is constant.
Example:
var arr = constant([ 6, 5, 4, 3, 2, 1 ]);
var mut = arr.reverse();
constant.isArray(mut); // true
JSON.stringify(mut); // '[1, 2, 3, 4, 5, 6 ]'
Inserts a new element at a specified index. If element isn't constant it will be on insertion.
Example:
var arr = constant([ 1, 2, 4, 5 ]);
var mut = arr.insert(2, 3);
constant.isArray(mut); // true
JSON.stringify(mut); // '[1, 2, 3, 4, 5 ]'
Removes an element from an array and returns the constant changed array.
Example:
var arr = constant([ 1, 2, 3, 4, 5 ]);
var mut = arr.delete(2);
constant.isArray(mut); // true
JSON.stringify(mut); // '[1, 2, 4, 5 ]'
Removes the first element of an array and returns the changed constant array.
Example:
var arr = constant([ 1, 2, 3, 4, 5 ]);
var mut = arr.shift();
constant.isArray(mut); // true
JSON.stringify(mut); // '[ 2, 3, 4, 5 ]'
Adds an element at the beginning of an array. Returns a new constant array with that element.
Example:
var arr = constant([ 1, 2, 3 ]);
var mut = arr.unshift(0);
constant.isArray(mut); // true
JSON.stringify(mut); // '[ 0, 1, 2, 3 ]'
Removes the last element of an array and returns the changed constant array.
Example:
var arr = constant([ 1, 2, 3, 4, 5 ]);
var mut = arr.pop();
constant.isArray(mut); // true
JSON.stringify(mut); // '[ 1, 2, 3, 4 ]'
Adds an element at the end of an array. Returns a new constant array with that element.
Example:
var arr = constant([ 1, 2, 3 ]);
var mut = arr.push(4);
constant.isArray(mut); // true
JSON.stringify(mut); // '[ 1, 2, 3, 4 ]'
Works just like Array.prototype.map
except that it returns a new constant array and allows removal of an element by returning undefined
.
Example:
var arr = constant([ 0, 1, 2, 3, 4 ]);
var mut = arr.transform(function(val) {
return val ? (val * 2) : undefined;
});
constant.isArray(mut); // true
JSON.stringify(mut); // '[ 2, 4, 6, 8 ]'
.first
ist the first element in an array and .last
is the last element in an array.
Example:
var arr = constant([ 1, 2, 3]);
arr.first; // 1
arr.last; // 3
All methods available trough Date.prototype
are available on constant Date objects as well except that
modifying methods have been replaced with ones returning a new constant Date object instead.
Works just like Date.prototype.setDate
except that rather than modifying the date, it returns a new constant date with the change applied.
Works just like Date.prototype.setFullYear
except that rather than modifying the date, it returns a new constant date with the change applied.
Works just like Date.prototype.setHours
except that rather than modifying the date, it returns a new constant date with the change applied.
Works just like Date.prototype.setMilliseconds
except that rather than modifying the date, it returns
a new constant date with the change applied.
Works just like Date.prototype.setMinutes
except that rather than modifying the date, it returns a new constant
date with the change applied.
Works just like Date.prototype.setMonth
except that rather than modifying the date, it returns a new constant
date with the change applied.
Works just like Date.prototype.setSeconds
except that rather than modifying the date, it returns a new constant date with the change applied.
Works just like Date.prototype.setTime
except that rather than modifying the date, it returns a new constant date with the change applied.
Works just like Date.prototype.setUTCDate
except that rather than modifying the date, it returns a new constant date with the change applied.
Works just like Date.prototype.setUTCFullYear
except that rather than modifying the date, it returns a new
constant date with the change applied.
Works just like Date.prototype.setUTCHours
except that rather than modifying the date, it returns a new constant date with the change applied.
Works just like Date.prototype.setUTCMilliseconds
except that rather than modifying the date, it returns a new constant date with the change applied.
Works just like Date.prototype.setUTCMinutes
except that rather than modifying the date, it returns a new constant date with the change applied.
Works just like Date.prototype.setUTCMonth
except that rather than modifying the date, it returns a new constant date with the change applied.
Works just like Date.prototype.setUTCSeconds
except that rather than modifying the date, it returns a new constant date with the change applied.
Works just like Date.prototype.setYear
except that rather than modifying the date, it returns a new constant date with the change applied.
Works just like Map.prototype.set
except it returns a contant Map instance that is the altered map.
Works just like Map.prototype.delete
except it returns a constant Map instance that is the altered map.
Works just like Map.prototype.clear
except it returns a constant Map instance that is empty.
Works just like Set.prototype.add
except it returns a constant Map instance that is empty.
Works just like Set.prototype.delete
except it returns a constant Map instance that is empty.
Gets the value of an element of a Set by its index. This utilizes the @@iterator
so for large sets this will be expensive depending on the value of idx
.
Works just like Set.prototype.clear
except it returns a constant Map instance that is empty.
Some object types require more information to properly handle. This can be done by placing a property on the prototype.
function MyClass() {}
MyClass.prototype[constant.COPY] = function() {
var copy = new MyClass();
copy.setValue(this.getValue());
return copy;
};
MyClass.prototype[constant.COPY].proto = {};
MyClass.prototype[constant.COPY].proto.setValue = function(val) {
var mut = this.mutable();
mut.setValue(val);
return constant(mut);
};
MyClass.isConstant = function(obj) {
return constant.is(obj) && (obj[constant.COPY] === MyClass.prototype[constant.COPY]);
};
In this fashion it is possible to support any arbitrary object as a constant object and add custom methods for getting mutated versions of them.