Optimize $watch and $parse for frozen/immutable objects #6391
Description
I see a lot of people having problems with too many $watch expressions. I have seen solutions such as bindonce, but that solution brings a lot of complexity on its own.
I suggest that AngularJS integrates and encourage a separation between mutable and immutable data, and detect frozen objects in $watch expressions. This has testability, complexity and speed advantages.
No new APIs or syntax is required for this.
Immutable data in Ecmascript 5.1 can be created using Object.freeze(). When a $watch expression is evaluated, and all constituent objects are frozen, then the result of the expression is a constant.
Current vs new watch expressions:
function watch(epression, scope) {
return $parse(expression).bind(scope);
}
function transmogrifyingWatch(expression, scope, onConstant) {
var f = $parse(expression);
// initial invariant. when it is broken, we do something
var inv = function() { return !scope.isFrozen(); };
return function() {
if (!inv()) {
// slow path, leftmost properties have changed their frozen status
f = .. new optimized function taking into consideration that the invariant is broken
inv = new invariant
if (f is constant) {
onConstant(f.apply(scope));
}
}
return f.apply(scope);
};
}
Of course, the invariant can be part of the f
function as well by integrating it into the current $parse
.
For all current code, this only adds a simple test for scope.isFrozen in the evaluation function, but it opens up for much more performant UIs by having watches that automatically learn about immutable data.
The onConstant callback can be used to remote a $watch from the digest cycle. Thus properly designed programs would be able to have thousands of $watches as long as they use immutable data to back them.