diff --git a/src/auto/injector.js b/src/auto/injector.js index 189421da8338..4c3b7a322d0b 100644 --- a/src/auto/injector.js +++ b/src/auto/injector.js @@ -795,31 +795,33 @@ 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]; 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 @@ -827,16 +829,18 @@ function createInjector(modulesToLoad, strictDi) { 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({}); + return new (Function.bind.apply(ctor, args)); } + return { invoke: invoke, instantiate: instantiate, diff --git a/test/auto/injectorSpec.js b/test/auto/injectorSpec.js index 94ee892146cf..289875332301 100644 --- a/test/auto/injectorSpec.js +++ b/test/auto/injectorSpec.js @@ -245,12 +245,24 @@ describe('injector', function() { // Only Chrome and Firefox support this syntax. if (/chrome|firefox/i.test(navigator.userAgent)) { - it('should be possible to annotate functions that are declared using ES6 syntax', function() { - /*jshint -W061 */ - // The function is generated using `eval` as just having the ES6 syntax can break some browsers. - expect(annotate(eval('({ fn(x) { return; } })').fn)).toEqual(['x']); - /*jshint +W061 */ + /*jshint -W061 */ + describe('ES6 syntax', function() { + // Functions and classes are generated using `eval` as just having the ES6 syntax can break + // some browsers. + it('should be possible to annotate ES6 methods', + function() { expect(annotate(eval('({ fn(x) { return; } })').fn)).toEqual(['x']); }); + + 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 */ }