From 91bb499e35a072ab32c4263ea89804561daf5954 Mon Sep 17 00:00:00 2001 From: Jason Bedard Date: Mon, 27 Oct 2014 21:35:30 -0700 Subject: [PATCH] perf($interpolate): provide a simplified result for constant expressions --- benchmarks/largetable-bp/main.html | 8 ++++++++ src/ng/interpolate.js | 26 ++++++++++++++++++++++++++ test/ng/interpolateSpec.js | 23 +++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/benchmarks/largetable-bp/main.html b/benchmarks/largetable-bp/main.html index f40353cb61e2..7fd8bdeec468 100644 --- a/benchmarks/largetable-bp/main.html +++ b/benchmarks/largetable-bp/main.html @@ -19,6 +19,7 @@
interpolation + fnInvocation:
ngBind + filter:
interpolation + filter:
+
ngModel:
@@ -77,6 +78,13 @@

interpolation with filter

{{column.i | noop}}:{{column.j | noop}}| +
+

ngModels

+
+ + +
+
diff --git a/src/ng/interpolate.js b/src/ng/interpolate.js index 5a0687cbcbcb..3b2461934643 100644 --- a/src/ng/interpolate.js +++ b/src/ng/interpolate.js @@ -88,6 +88,19 @@ function $InterpolateProvider() { return '\\\\\\' + ch; } + //TODO: this is the same as the constantWatchDelegate in parse.js + function constantWatchDelegate(scope, listener, objectEquality, constantInterp) { + var unwatch; + return unwatch = scope.$watch(function constantInterpolateWatch(scope) { + return constantInterp(scope); + }, function constantInterpolateListener(value, old, scope) { + if (isFunction(listener)) { + listener.apply(this, arguments); + } + unwatch(); + }, objectEquality); + } + /** * @ngdoc service * @name $interpolate @@ -183,6 +196,19 @@ function $InterpolateProvider() { * - `context`: evaluation context for all expressions embedded in the interpolated text */ function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) { + // Provide a quick exit and simplified result function for text with no interpolation + if (!text.length || text.indexOf(startSymbol) === -1) { + var constantInterp; + if (!mustHaveExpression) { + var unescapedText = unescapeText(text); + constantInterp = function constantInterpolationFn() { return unescapedText; }; + constantInterp.exp = text; + constantInterp.expressions = []; + constantInterp.$$watchDelegate = constantWatchDelegate; + } + return constantInterp; + } + allOrNothing = !!allOrNothing; var startIndex, endIndex, diff --git a/test/ng/interpolateSpec.js b/test/ng/interpolateSpec.js index 232244ede492..8124dc6f5695 100644 --- a/test/ng/interpolateSpec.js +++ b/test/ng/interpolateSpec.js @@ -125,6 +125,28 @@ describe('$interpolate', function() { expect($rootScope.$countWatchers()).toBe(0); })); + + it('should stop watching strings with no expressions after first execution', + inject(function($interpolate, $rootScope) { + var spy = jasmine.createSpy(); + $rootScope.$watch($interpolate('foo'), spy); + $rootScope.$digest(); + expect(($rootScope.$$watchers || []).length).toBe(0); + expect(spy).toHaveBeenCalledWith('foo', 'foo', $rootScope); + expect(spy.calls.length).toBe(1); + }) + ); + + it('should stop watching strings with only constant expressions after first execution', + inject(function($interpolate, $rootScope) { + var spy = jasmine.createSpy(); + $rootScope.$watch($interpolate('foo {{42}}'), spy); + $rootScope.$digest(); + expect(($rootScope.$$watchers || []).length).toBe(0); + expect(spy).toHaveBeenCalledWith('foo 42', 'foo 42', $rootScope); + expect(spy.calls.length).toBe(1); + }) + ); }); describe('interpolation escaping', function() { @@ -135,6 +157,7 @@ describe('$interpolate', function() { it('should support escaping interpolation signs', inject(function($interpolate) { + expect($interpolate('\\{\\{')(obj)).toBe('{{'); expect($interpolate('{{foo}} \\{\\{bar\\}\\}')(obj)).toBe('Hello {{bar}}'); expect($interpolate('\\{\\{foo\\}\\} {{bar}}')(obj)).toBe('{{foo}} World'); }));