Immutable data
Examples using javaScript
by Ben Bakhar
benb@codeoasis.com
15 minutes talk
Immutable object
In object-oriented and functional programming, an immutable object is an object whose state cannot be modified after it is created (wikipedia)
Some arguments in favor of immutability
- simpler application development
- Thread safe
- No defensive copying ( " You must program defensively with the assumption that clients of your class will do their best to destroy its invariants." )
- Simple change detection techniques
Immutable persistent data collections for Javascript which increase efficiency and simplicity.
let list = Immutable.List([1, 2, 3]);
let changed = list.push(4);
list.toString(); // List [ 1, 2, 3 ]
changed.toString(); // List [ 1, 2, 3, 4 ]
list === changed // false
Each change on the collection results with a new object created
compared to mutable Javascript list
let list = [1, 2, 3];
list.push(4);
console.log(list); // [1, 2, 3, 4]
AngularJS data-binding
$scope.collection = generateHugeCollection();
$scope.$watchCollection('collection', function (val) {
// do some stuff with the changed value
});
- register a watcher to watch 'collection'
- evaluate at least once on each digest loop
- compare current value (reference) to previous one, cheap.
- equality check, expensive.
Drawback: Although we gain a faster $watch there’s a big overhead of creating a new data structure once we add or remove items
Summary
- Fast change detection
- Slow insertions and deletions
Can we take the best of both worlds?
Goals:
- reduce $watch collection complexity from O(n) to O(1)
- overcome the overhead of creation the new data structure on change
Ideas?
// ...
for (key in newValue) {
if (newValue.hasOwnProperty(key)) {
newLength++;
newItem = newValue[key];
oldItem = oldValue[key];
if (key in oldValue) {
bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
if (!bothNaN && (oldItem !== newItem)) {
changeDetected++;
oldValue[key] = newItem;
}
} else {
oldLength++;
oldValue[key] = newItem;
changeDetected++;
}
}
}
// ...
AngularJS $watchCollection implementation:
Use $watchCollection(expr, fn) but reduce its running time. This means that we need to decrease the elements over which $watchCollection iterates.
- reduce the keys over which AngularJS’ dirty checker iterates => keep a control flag which handles collection state instead of creating a new reference
Suggestion
Implementation
function defineProperty(obj, name, descriptor) {
'use strict';
Object.defineProperty(obj, name, descriptor);
}
function defineMethod(obj, name, method) {
'use strict';
defineProperty(obj, name, {
enumerable: false,
value: method
});
}
function VersionableMap(data) {
this._version = 0;
defineProperty(this, '_data', {
value: data || {},
enumerable: false
});
}
defineMethod(VersionableMap.prototype, 'set',
function (key, value) {
this._data[key] = value;
this._updateVersion();
});
defineMethod(VersionableMap.prototype, 'get',
function (key) {
return this._data[key];
});
defineMethod(VersionableMap.prototype, 'remove',
function (key) {
var value = this._data[key];
if (this._data[key] !== undefined) {
delete this._data[key];
this._updateVersion();
}
return value;
});
defineMethod(VersionableMap.prototype, 'keys',
function () {
return Object.keys(this._data);
});
Results
Immutable data
By Ben Bakhar
Immutable data
- 1,357