diff options
| author | Misko Hevery | 2010-05-30 19:42:21 -0700 |
|---|---|---|
| committer | Misko Hevery | 2010-05-30 19:42:21 -0700 |
| commit | 2e33e89a77d115ff17f5841ec328b1c1e4228161 (patch) | |
| tree | 22a97d5c70f2e74ffb4dfe789c82545363abed55 | |
| parent | 1aa99c08e9ccd515a333478f00b361f40c622002 (diff) | |
| download | angular.js-2e33e89a77d115ff17f5841ec328b1c1e4228161.tar.bz2 | |
added compiled getterFN for better performance
| -rw-r--r-- | scenario/perf.html | 2 | ||||
| -rw-r--r-- | src/Parser.js | 7 | ||||
| -rw-r--r-- | src/Scope.js | 35 | ||||
| -rw-r--r-- | test/ScopeSpec.js | 21 |
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); + + }); + }); }); |
