Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c04530c

Browse files
committed
Refactor injector to have invoke method for speed reasons
1 parent f2cede8 commit c04530c

11 files changed

+183
-116
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
<a name="0.9.16"><a/>
22
# <angular/> 0.9.16 weather-control (in-progress) #
33

4+
### Breaking changes
5+
- $service now has $service.invoke for method injection ($service(self, fn) no longer works)
6+
- injection name inference no longer supports method curry and linking functions. Both must be
7+
explicitly specified using $inject property.
48

59

610

docs/src/gen-docs.js

100644100755
File mode changed.

src/AngularPublic.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ angularService('$browser', function($log){
2424
}, {$inject:['$log']});
2525

2626
extend(angular, {
27+
'annotate': annotate,
2728
'element': jqLite,
2829
'compile': compile,
2930
'scope': createScope,

src/Compiler.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Template.prototype = {
3434
forEach(this.inits, function(fn) {
3535
queue.push(function() {
3636
childScope.$tryEval(function(){
37-
return childScope.$service(fn, childScope, element);
37+
return childScope.$service.invoke(childScope, fn, [element]);
3838
}, element);
3939
});
4040
});
@@ -49,9 +49,11 @@ Template.prototype = {
4949
},
5050

5151

52-
addInit:function(init) {
53-
if (init) {
54-
this.inits.push(init);
52+
addInit:function(linkingFn) {
53+
if (linkingFn) {
54+
if (!linkingFn.$inject)
55+
linkingFn.$inject = [];
56+
this.inits.push(linkingFn);
5557
}
5658
},
5759

src/Injector.js

Lines changed: 100 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,81 +4,114 @@
44
* @function
55
*
66
* @description
7-
* Creates an inject function that can be used for dependency injection.
8-
* (See {@link guide.di dependency injection})
7+
* Creates an injector function that can be used for retrieving services as well as for
8+
* dependency injection (see {@link guide.di dependency injection}).
99
*
10-
* The inject function can be used for retrieving service instances or for calling any function
11-
* which has the $inject property so that the services can be automatically provided. Angular
12-
* creates an injection function automatically for the root scope and it is available as
13-
* {@link angular.scope.$service $service}.
10+
* Angular creates an injector automatically for the root scope and it is available as the
11+
* {@link angular.scope.$service $service} property. Creation of the injector automatically creates
12+
* all of the `$eager` {@link angular.service services}.
1413
*
15-
* @param {Object=} [providerScope={}] provider's `this`
16-
* @param {Object.<string, function()>=} [providers=angular.service] Map of provider (factory)
17-
* function.
18-
* @param {Object.<string, function()>=} [cache={}] Place where instances are saved for reuse. Can
19-
* also be used to override services speciafied by `providers` (useful in tests).
20-
* @returns
21-
* {function()} Injector function: `function(value, scope, args...)`:
14+
* @param {Object=} [factoryScope={}] `this` for the service factory function.
15+
* @param {Object.<string, function()>=} [factories=angular.service] Map of service factory
16+
* functions.
17+
* @param {Object.<string, function()>=} [instanceCache={}] Place where instances of services are
18+
* saved for reuse. Can also be used to override services specified by `serviceFactory`
19+
* (useful in tests).
20+
* @returns {function()} Injector function:
2221
*
23-
* * `value` - `{string|array|function}`
24-
* * `scope(optional=rootScope)` - optional function "`this`" when `value` is type `function`.
25-
* * `args(optional)` - optional set of arguments to pass to function after injection arguments.
26-
* (also known as curry arguments or currying).
22+
* * `injector(serviceName)`:
23+
* * `serviceName` - `{string=}` - name of the service to retrieve.
2724
*
28-
* #Return value of `function(value, scope, args...)`
29-
* The injector function return value depended on the type of `value` argument:
30-
*
31-
* * `string`: return an instance for the injection key.
32-
* * `array` of keys: returns an array of instances for those keys. (see `string` above.)
33-
* * `function`: look at `$inject` property of function to determine instances to inject
34-
* and then call the function with instances and `scope`. Any additional arguments
35-
* (`args`) are appended to the function arguments.
36-
* * `none`: initialize eager providers.
25+
* The injector function also has these properties:
3726
*
27+
* * an `invoke` property which can be used to invoke methods with dependency-injected arguments.
28+
* `injector.invoke(self, fn, curryArgs)`
29+
* * `self` - "`this`" to be used when invoking the function.
30+
* * `fn` - the function to be invoked. The function may have the `$inject` property which
31+
* lists the set of arguments which should be auto injected
32+
* (see {@link guide.di dependency injection}).
33+
* * `curryArgs(array)` - optional array of arguments to pass to function invocation after the
34+
* injection arguments (also known as curry arguments or currying).
35+
* * an `eager` property which is used to initialize the eager services.
36+
* `injector.eager()`
3837
*/
39-
function createInjector(providerScope, providers, cache) {
40-
providers = providers || angularService;
41-
cache = cache || {};
42-
providerScope = providerScope || {};
43-
return function inject(value, scope, args){
44-
var returnValue, provider;
45-
if (isString(value)) {
46-
if (!(value in cache)) {
47-
provider = providers[value];
48-
if (!provider) throw "Unknown provider for '"+value+"'.";
49-
cache[value] = inject(provider, providerScope);
50-
}
51-
returnValue = cache[value];
52-
} else if (isArray(value)) {
53-
returnValue = [];
54-
forEach(value, function(name) {
55-
returnValue.push(inject(name));
56-
});
57-
} else if (isFunction(value)) {
58-
returnValue = inject(injectionArgs(value));
59-
returnValue = value.apply(scope, concat(returnValue, arguments, 2));
60-
} else if (isObject(value)) {
61-
forEach(providers, function(provider, name){
62-
if (provider.$eager)
63-
inject(name);
38+
function createInjector(factoryScope, factories, instanceCache) {
39+
factories = factories || angularService;
40+
instanceCache = instanceCache || {};
41+
factoryScope = factoryScope || {};
42+
injector.invoke = invoke;
6443

65-
if (provider.$creation)
66-
throw new Error("Failed to register service '" + name +
67-
"': $creation property is unsupported. Use $eager:true or see release notes.");
68-
});
69-
} else {
70-
returnValue = inject(providerScope);
44+
injector.eager = function(){
45+
forEach(factories, function(factory, name){
46+
if (factory.$eager)
47+
injector(name);
48+
49+
if (factory.$creation)
50+
throw new Error("Failed to register service '" + name +
51+
"': $creation property is unsupported. Use $eager:true or see release notes.");
52+
});
53+
};
54+
return injector;
55+
56+
function injector(value){
57+
if (!(value in instanceCache)) {
58+
var factory = factories[value];
59+
if (!factory) throw Error("Unknown provider for '"+value+"'.");
60+
instanceCache[value] = invoke(factoryScope, factory);
7161
}
72-
return returnValue;
62+
return instanceCache[value];
7363
};
74-
}
7564

76-
function injectService(services, fn) {
77-
return extend(fn, {$inject:services});
65+
function invoke(self, fn, args){
66+
args = args || [];
67+
var injectNames = injectionArgs(fn);
68+
var i = injectNames.length;
69+
while(i--) {
70+
args.unshift(injector(injectNames[i]));
71+
}
72+
return fn.apply(self, args);
73+
}
7874
}
7975

80-
function injectUpdateView(fn) {
81-
return injectService(['$updateView'], fn);
76+
/**
77+
* @ngdoc function
78+
* @name angular.annotate
79+
* @function
80+
*
81+
* @description
82+
* Annotate the function with injection arguments. This is equivalent to setting the `$inject`
83+
* property as described in {@link guide.di dependency injection}.
84+
*
85+
* <pre>
86+
* var MyController = angular.annotate('$location', function($location){ ... });
87+
* </pre>
88+
*
89+
* is the same as
90+
*
91+
* <pre>
92+
* var MyController = function($location){ ... };
93+
* MyController.$inject = ['$location'];
94+
* </pre>
95+
*
96+
* @param {String|Array} serviceName... zero or more service names to inject into the
97+
* `annotatedFunction`.
98+
* @param {function} annotatedFunction function to annotate with `$inject`
99+
* functions.
100+
* @returns {function} `annotatedFunction`
101+
*/
102+
function annotate(services, fn) {
103+
if (services instanceof Array) {
104+
fn.$inject = services;
105+
return fn;
106+
} else {
107+
var i = 0,
108+
length = arguments.length - 1, // last one is the destination function
109+
$inject = arguments[length].$inject = [];
110+
for (; i < length; i++) {
111+
$inject.push(arguments[i]);
112+
}
113+
return arguments[length]; // return the last one
114+
}
82115
}
83116

84117
function angularServiceInject(name, fn, inject, eager) {
@@ -89,12 +122,12 @@ function angularServiceInject(name, fn, inject, eager) {
89122
/**
90123
* @returns the $inject property of function. If not found the
91124
* the $inject is computed by looking at the toString of function and
92-
* extracting all arguments which start with $ or end with _ as the
125+
* extracting all arguments which and assuming that they are the
93126
* injection names.
94127
*/
95128
var FN_ARGS = /^function\s*[^\(]*\(([^\)]*)\)/;
96129
var FN_ARG_SPLIT = /,/;
97-
var FN_ARG = /^\s*(((\$?).+?)(_?))\s*$/;
130+
var FN_ARG = /^\s*(.+?)\s*$/;
98131
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
99132
function injectionArgs(fn) {
100133
assertArgFn(fn);
@@ -103,12 +136,8 @@ function injectionArgs(fn) {
103136
var fnText = fn.toString().replace(STRIP_COMMENTS, '');
104137
var argDecl = fnText.match(FN_ARGS);
105138
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
106-
arg.replace(FN_ARG, function(all, name, injectName, $, _){
107-
assertArg(args, name, 'after non-injectable arg');
108-
if ($ || _)
109-
args.push(injectName);
110-
else
111-
args = null; // once we reach an argument which is not injectable then ignore
139+
arg.replace(FN_ARG, function(all, name){
140+
args.push(name);
112141
});
113142
});
114143
}

src/Scope.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ function createScope(parent, providers, instanceCache) {
581581
forEach(Class.prototype, function(fn, name){
582582
instance[name] = bind(instance, fn);
583583
});
584-
instance.$service.apply(instance, concat([Class, instance], arguments, 1));
584+
instance.$service.invoke(instance, Class, slice.call(arguments, 1, arguments.length));
585585

586586
//TODO: backwards compatibility hack, remove when we don't depend on init methods
587587
if (isFunction(Class.prototype.init)) {
@@ -634,7 +634,7 @@ function createScope(parent, providers, instanceCache) {
634634
* @param {string} serviceId String ID of the service to return.
635635
* @returns {*} Value, object or function returned by the service factory function if any.
636636
*/
637-
(instance.$service = createInjector(instance, providers, instanceCache))();
637+
instance.$service = createInjector(instance, providers, instanceCache);
638638
}
639639

640640
$log = instance.$service('$log');

src/apis.js

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,16 @@ var angularFunction = {
790790
}
791791
};
792792

793+
/**
794+
* Computes a hash of an 'obj'.
795+
* Hash of a:
796+
* string is string
797+
* number is number as string
798+
* object is either call $hashKey function on object or assign unique hashKey id.
799+
*
800+
* @param obj
801+
* @returns {String} hash string such that the same input will have the same hash string
802+
*/
793803
function hashKey(obj) {
794804
var objType = typeof obj;
795805
var key = obj;
@@ -805,17 +815,37 @@ function hashKey(obj) {
805815
return objType + ':' + key;
806816
}
807817

818+
/**
819+
* HashMap which can use objects as keys
820+
*/
808821
function HashMap(){}
809822
HashMap.prototype = {
823+
/**
824+
* Store key value pair
825+
* @param key key to store can be any type
826+
* @param value value to store can be any type
827+
* @returns old value if any
828+
*/
810829
put: function(key, value) {
811830
var _key = hashKey(key);
812831
var oldValue = this[_key];
813832
this[_key] = value;
814833
return oldValue;
815834
},
835+
836+
/**
837+
* @param key
838+
* @returns the value for the key
839+
*/
816840
get: function(key) {
817841
return this[hashKey(key)];
818842
},
843+
844+
/**
845+
* Remove the key/value pair
846+
* @param key
847+
* @returns value associated with key before it was removed
848+
*/
819849
remove: function(key) {
820850
var _key = hashKey(key);
821851
var value = this[_key];
@@ -836,8 +866,6 @@ defineApi('Array', [angularGlobal, angularCollection, angularArray]);
836866
defineApi('Object', [angularGlobal, angularCollection, angularObject]);
837867
defineApi('String', [angularGlobal, angularString]);
838868
defineApi('Date', [angularGlobal, angularDate]);
839-
// TODO: enable and document this API
840-
//defineApi('HashMap', [HashMap]);
841869
//IE bug
842-
angular['Date']['toString'] = angularDate['toString'];
870+
angular.Date.toString = angularDate.toString;
843871
defineApi('Function', [angularGlobal, angularCollection, angularFunction]);

src/directives.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ angularDirective("ng:bind-attr", function(expression){
466466
* TODO: maybe we should consider allowing users to control event propagation in the future.
467467
*/
468468
angularDirective("ng:click", function(expression, element){
469-
return injectUpdateView(function($updateView, element){
469+
return inject('$updateView', function($updateView, element){
470470
var self = this;
471471
element.bind('click', function(event){
472472
self.$tryEval(expression, element);
@@ -516,7 +516,7 @@ angularDirective("ng:click", function(expression, element){
516516
</doc:example>
517517
*/
518518
angularDirective("ng:submit", function(expression, element) {
519-
return injectUpdateView(function($updateView, element) {
519+
return inject('$updateView', function($updateView, element) {
520520
var self = this;
521521
element.bind('submit', function(event) {
522522
self.$tryEval(expression, element);

src/widgets.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ function radioInit(model, view, element) {
495495
</doc:example>
496496
*/
497497
function inputWidget(events, modelAccessor, viewAccessor, initFn, textBox) {
498-
return injectService(['$updateView', '$defer'], function($updateView, $defer, element) {
498+
return annotate('$updateView', '$defer', function($updateView, $defer, element) {
499499
var scope = this,
500500
model = modelAccessor(scope, element),
501501
view = viewAccessor(scope, element),
@@ -837,16 +837,16 @@ angularWidget('a', function() {
837837
*
838838
* @description
839839
* The `ng:repeat` widget instantiates a template once per item from a collection. The collection is
840-
* enumerated with the `ng:repeat-index` attribute, starting from 0. Each template instance gets
841-
* its own scope, where the given loop variable is set to the current collection item, and `$index`
840+
* enumerated with the `ng:repeat-index` attribute, starting from 0. Each template instance gets
841+
* its own scope, where the given loop variable is set to the current collection item, and `$index`
842842
* is set to the item index or key.
843843
*
844844
* Special properties are exposed on the local scope of each template instance, including:
845845
*
846846
* * `$index` – `{number}` – iterator offset of the repeated element (0..length-1)
847-
* * `$position` – `{string}` – position of the repeated element in the iterator. One of:
847+
* * `$position` – `{string}` – position of the repeated element in the iterator. One of:
848848
* * `'first'`,
849-
* * `'middle'`
849+
* * `'middle'`
850850
* * `'last'`
851851
*
852852
* Note: Although `ng:repeat` looks like a directive, it is actually an attribute widget.
@@ -1062,7 +1062,7 @@ angularWidget('ng:view', function(element) {
10621062

10631063
if (!element[0]['ng:compiled']) {
10641064
element[0]['ng:compiled'] = true;
1065-
return injectService(['$xhr.cache', '$route'], function($xhr, $route, element){
1065+
return annotate('$xhr.cache', '$route', function($xhr, $route, element){
10661066
var parentScope = this,
10671067
childScope;
10681068

0 commit comments

Comments
 (0)