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

feat($injector): support instantiating classes. #12598

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 27 additions & 21 deletions src/auto/injector.js
Original file line number Diff line number Diff line change
Expand Up @@ -801,48 +801,54 @@ function createInjector(modulesToLoad, strictDi) {
}
}

function invoke(fn, self, locals, serviceName) {
if (typeof locals === 'string') {
serviceName = locals;
locals = null;
}

function injectionArgs(fn, locals, serviceName) {
var args = [],
$inject = createInjector.$$annotate(fn, strictDi, serviceName),
length, i,
key;
$inject = createInjector.$$annotate(fn, strictDi, serviceName);

for (i = 0, length = $inject.length; i < length; i++) {
key = $inject[i];
for (var i = 0, length = $inject.length; i < length; i++) {
var key = $inject[i];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why move the declarations of i and key here? Being var declarations they will still be hoisted to the top of the function block.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also we seem to have lost the declaration of the length variable here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

length is delayed as part of the var declaration

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declared, darned Swype

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@petebacondarwin I was moving the code around anyway, and I personally find it easier to declare things as close as possible to their respective use site, hoisting or not. Makes it easier to see what's used where and why, and also makes it easier to refactor code without forgetting to remove declarations.

if (typeof key !== 'string') {
throw $injectorMinErr('itkn',
'Incorrect injection token! Expected service name as string, got {0}', key);
}
args.push(
locals && locals.hasOwnProperty(key)
? locals[key]
: getService(key, serviceName)
);
args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
getService(key, serviceName));
}
return args;
}


function invoke(fn, self, locals, serviceName) {
if (typeof locals === 'string') {
serviceName = locals;
locals = null;
}

var args = injectionArgs(fn, locals, serviceName);
if (isArray(fn)) {
fn = fn[length];
fn = fn[fn.length - 1];
}

// http://jsperf.com/angularjs-invoke-apply-vs-switch
// #5388
return fn.apply(self, args);
}


function instantiate(Type, locals, serviceName) {
// Check if Type is annotated and use just the given function at n-1 as parameter
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
// Object creation: http://jsperf.com/create-constructor/2
var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
var returnedValue = invoke(Type, instance, locals, serviceName);

return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
var args = injectionArgs(Type, locals, serviceName);
// Empty object at position 0 is ignored for invocation with `new`, but required.
args.unshift({});
/*jshint -W058 */ // Applying a constructor without immediate parentheses is the point here.
return new (Function.prototype.bind.apply(ctor, args));
/*jshint +W058 */
}


return {
invoke: invoke,
instantiate: instantiate,
Expand Down
10 changes: 10 additions & 0 deletions test/auto/injectorSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,16 @@ describe('injector', function() {
it('should take args before first arrow', function() {
expect(annotate(eval('a => b => b'))).toEqual(['a']);
});

it('should be possible to instantiate ES6 classes', function() {
// Only Chrome (not even the FF we use) supports ES6 classes.
if (!/chrome/i.test(navigator.userAgent)) return;
providers('a', function() { return 'a-value'; });
var clazz = eval('(class { constructor(a) { this.a = a; } aVal() { return this.a; } })');
var instance = injector.instantiate(clazz);
expect(instance).toEqual({a: 'a-value'});
expect(instance.aVal()).toEqual('a-value');
});
/*jshint +W061 */
});
}
Expand Down