Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 864b259

Browse files
kseamonIgorMinar
authored andcommitted
perf($parse) use a faster path when the number of path parts is low
Use a faster path when the number of path tokens is low (ie the common case). This results in a better than 19x improvement in the time spent in $parse and produces output that is about the same speed in chrome and substantially faster in firefox. http://jsperf.com/angularjs-parse-getter/6 Closes #5359
1 parent f3a796e commit 864b259

File tree

2 files changed

+44
-17
lines changed

2 files changed

+44
-17
lines changed

src/ng/parse.js

+43-17
Original file line numberDiff line numberDiff line change
@@ -891,19 +891,19 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
891891
? function cspSafeGetter(scope, locals) {
892892
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
893893

894-
if (pathVal === null || pathVal === undefined) return pathVal;
894+
if (pathVal == null) return pathVal;
895895
pathVal = pathVal[key0];
896896

897-
if (!key1 || pathVal === null || pathVal === undefined) return pathVal;
897+
if (!key1 || pathVal == null) return pathVal;
898898
pathVal = pathVal[key1];
899899

900-
if (!key2 || pathVal === null || pathVal === undefined) return pathVal;
900+
if (!key2 || pathVal == null) return pathVal;
901901
pathVal = pathVal[key2];
902902

903-
if (!key3 || pathVal === null || pathVal === undefined) return pathVal;
903+
if (!key3 || pathVal == null) return pathVal;
904904
pathVal = pathVal[key3];
905905

906-
if (!key4 || pathVal === null || pathVal === undefined) return pathVal;
906+
if (!key4 || pathVal == null) return pathVal;
907907
pathVal = pathVal[key4];
908908

909909
return pathVal;
@@ -912,7 +912,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
912912
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope,
913913
promise;
914914

915-
if (pathVal === null || pathVal === undefined) return pathVal;
915+
if (pathVal == null) return pathVal;
916916

917917
pathVal = pathVal[key0];
918918
if (pathVal && pathVal.then) {
@@ -924,7 +924,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
924924
}
925925
pathVal = pathVal.$$v;
926926
}
927-
if (!key1 || pathVal === null || pathVal === undefined) return pathVal;
927+
if (!key1 || pathVal == null) return pathVal;
928928

929929
pathVal = pathVal[key1];
930930
if (pathVal && pathVal.then) {
@@ -936,7 +936,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
936936
}
937937
pathVal = pathVal.$$v;
938938
}
939-
if (!key2 || pathVal === null || pathVal === undefined) return pathVal;
939+
if (!key2 || pathVal == null) return pathVal;
940940

941941
pathVal = pathVal[key2];
942942
if (pathVal && pathVal.then) {
@@ -948,7 +948,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
948948
}
949949
pathVal = pathVal.$$v;
950950
}
951-
if (!key3 || pathVal === null || pathVal === undefined) return pathVal;
951+
if (!key3 || pathVal == null) return pathVal;
952952

953953
pathVal = pathVal[key3];
954954
if (pathVal && pathVal.then) {
@@ -960,7 +960,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
960960
}
961961
pathVal = pathVal.$$v;
962962
}
963-
if (!key4 || pathVal === null || pathVal === undefined) return pathVal;
963+
if (!key4 || pathVal == null) return pathVal;
964964

965965
pathVal = pathVal[key4];
966966
if (pathVal && pathVal.then) {
@@ -976,6 +976,27 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
976976
};
977977
}
978978

979+
function simpleGetterFn1(key0, fullExp) {
980+
ensureSafeMemberName(key0, fullExp);
981+
982+
return function simpleGetterFn1(scope, locals) {
983+
if (scope == null) return scope;
984+
return ((locals && locals.hasOwnProperty(key0)) ? locals : scope)[key0];
985+
};
986+
}
987+
988+
function simpleGetterFn2(key0, key1, fullExp) {
989+
ensureSafeMemberName(key0, fullExp);
990+
ensureSafeMemberName(key1, fullExp);
991+
992+
return function simpleGetterFn2(scope, locals) {
993+
if (scope == null) return scope;
994+
scope = ((locals && locals.hasOwnProperty(key0)) ? locals : scope)[key0];
995+
996+
return scope == null ? scope : scope[key1];
997+
};
998+
}
999+
9791000
function getterFn(path, options, fullExp) {
9801001
// Check whether the cache has this getter already.
9811002
// We can use hasOwnProperty directly on the cache because we ensure,
@@ -988,7 +1009,13 @@ function getterFn(path, options, fullExp) {
9881009
pathKeysLength = pathKeys.length,
9891010
fn;
9901011

991-
if (options.csp) {
1012+
// When we have only 1 or 2 tokens, use optimized special case closures.
1013+
// http://jsperf.com/angularjs-parse-getter/6
1014+
if (!options.unwrapPromises && pathKeysLength === 1) {
1015+
fn = simpleGetterFn1(pathKeys[0], fullExp);
1016+
} else if (!options.unwrapPromises && pathKeysLength === 2) {
1017+
fn = simpleGetterFn2(pathKeys[0], pathKeys[1], fullExp);
1018+
} else if (options.csp) {
9921019
if (pathKeysLength < 6) {
9931020
fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp,
9941021
options);
@@ -1006,11 +1033,10 @@ function getterFn(path, options, fullExp) {
10061033
};
10071034
}
10081035
} else {
1009-
var code = 'var l, fn, p;\n';
1036+
var code = 'var p;\n';
10101037
forEach(pathKeys, function(key, index) {
10111038
ensureSafeMemberName(key, fullExp);
1012-
code += 'if(s === null || s === undefined) return s;\n' +
1013-
'l=s;\n' +
1039+
code += 'if(s == null) return s;\n' +
10141040
's='+ (index
10151041
// we simply dereference 's' on any .dot notation
10161042
? 's'
@@ -1033,10 +1059,10 @@ function getterFn(path, options, fullExp) {
10331059
/* jshint -W054 */
10341060
var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning
10351061
/* jshint +W054 */
1036-
evaledFnGetter.toString = function() { return code; };
1037-
fn = function(scope, locals) {
1062+
evaledFnGetter.toString = valueFn(code);
1063+
fn = options.unwrapPromises ? function(scope, locals) {
10381064
return evaledFnGetter(scope, locals, promiseWarning);
1039-
};
1065+
} : evaledFnGetter;
10401066
}
10411067

10421068
// Only cache the value if it's not going to mess up the cache object

test/ng/parseSpec.js

+1
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,7 @@ describe('parser', function() {
906906
expect($parse('a.b')({a: {b: 0}}, {a: {b:1}})).toEqual(1);
907907
expect($parse('a.b')({a: null}, {a: {b:1}})).toEqual(1);
908908
expect($parse('a.b')({a: {b: 0}}, {a: null})).toEqual(undefined);
909+
expect($parse('a.b.c')({a: null}, {a: {b: {c: 1}}})).toEqual(1);
909910
}));
910911
});
911912

0 commit comments

Comments
 (0)