aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMisko Hevery2010-05-30 19:42:21 -0700
committerMisko Hevery2010-05-30 19:42:21 -0700
commit2e33e89a77d115ff17f5841ec328b1c1e4228161 (patch)
tree22a97d5c70f2e74ffb4dfe789c82545363abed55
parent1aa99c08e9ccd515a333478f00b361f40c622002 (diff)
downloadangular.js-2e33e89a77d115ff17f5841ec328b1c1e4228161.tar.bz2
added compiled getterFN for better performance
-rw-r--r--scenario/perf.html2
-rw-r--r--src/Parser.js7
-rw-r--r--src/Scope.js35
-rw-r--r--test/ScopeSpec.js21
4 files changed, 60 insertions, 5 deletions
diff --git a/scenario/perf.html b/scenario/perf.html
index 1b0e40b4..e4edc00f 100644
--- a/scenario/perf.html
+++ b/scenario/perf.html
@@ -25,7 +25,7 @@
<hr/>
<ul>
<li ng-repeat="item in items.$filter('').$orderBy('name')">
- {{item.name}} {{item.parts.join(', ')}}
+ {{item.name}} <a href="#{{item.name}}">{{item.parts.join(', ')}}</a>
</li>
</ul>
</body>
diff --git a/src/Parser.js b/src/Parser.js
index cfb72c72..df270792 100644
--- a/src/Parser.js
+++ b/src/Parser.js
@@ -151,9 +151,7 @@ Lexer.prototype = {
}
var fn = Lexer.OPERATORS[ident];
if (!fn) {
- fn = function(self){
- return getter(self, ident);
- };
+ fn = getterFn(ident);
fn.isAssignable = ident;
}
this.tokens.push({index:start, text:ident, fn:fn});
@@ -563,8 +561,9 @@ Parser.prototype = {
fieldAccess: function(object) {
var field = this.expect().text;
+ var getter = getterFn(field);
var fn = function (self){
- return getter(object(self), field);
+ return getter(object(self));
};
fn.isAssignable = field;
return fn;
diff --git a/src/Scope.js b/src/Scope.js
index bed0ff6d..1c223130 100644
--- a/src/Scope.js
+++ b/src/Scope.js
@@ -43,6 +43,41 @@ function setter(instance, path, value){
return value;
}
+///////////////////////////////////
+
+var getterFnCache = {};
+function getterFn(path){
+ var fn = getterFnCache[path];
+ if (fn) return fn;
+
+ var code = 'function (self){\n';
+ code += ' var last, fn, type;\n';
+ foreach(path.split('.'), function(key) {
+ code += ' if(!self) return self;\n';
+ code += ' last = self;\n';
+ code += ' self = self.' + key + ';\n';
+ code += ' if(typeof self == "function") \n';
+ code += ' self = function(){ return last.'+key+'.apply(last, arguments); };\n';
+ if (key.charAt(0) == '$') {
+ // special code for super-imposed functions
+ var name = key.substr(1);
+ code += ' if(!self) {\n';
+ code += ' type = angular.Global.typeOf(last);\n';
+ code += ' fn = (angular[type.charAt(0).toUpperCase() + type.substring(1)]||{})["' + name + '"];\n';
+ code += ' if (fn)\n';
+ code += ' self = function(){ return fn.apply(last, [last].concat(slice.call(arguments, 0, arguments.length))); };\n';
+ code += ' }\n';
+ }
+ });
+ code += ' return self;\n}';
+ fn = eval('(' + code + ')');
+ fn.toString = function(){ return code; };
+
+ return getterFnCache[path] = fn;
+}
+
+///////////////////////////////////
+
var compileCache = {};
function expressionCompile(exp){
if (isFunction(exp)) return exp;
diff --git a/test/ScopeSpec.js b/test/ScopeSpec.js
index a3b6d9ae..d93400e5 100644
--- a/test/ScopeSpec.js
+++ b/test/ScopeSpec.js
@@ -157,4 +157,25 @@ describe('scope/model', function(){
}
});
});
+
+ describe('getterFn', function(){
+ it('should get chain', function(){
+ expect(getterFn('a.b')(undefined)).toEqual(undefined);
+ expect(getterFn('a.b')({})).toEqual(undefined);
+ expect(getterFn('a.b')({a:null})).toEqual(undefined);
+ expect(getterFn('a.b')({a:{}})).toEqual(undefined);
+ expect(getterFn('a.b')({a:{b:null}})).toEqual(null);
+ expect(getterFn('a.b')({a:{b:0}})).toEqual(0);
+ expect(getterFn('a.b')({a:{b:'abc'}})).toEqual('abc');
+ });
+
+ it('should map type method on top of expression', function(){
+ expect(getterFn('a.$filter')({a:[]})('')).toEqual([]);
+ });
+
+ it('should bind function this', function(){
+ expect(getterFn('a')({a:function($){return this.b + $;}, b:1})(2)).toEqual(3);
+
+ });
+ });
});