Last active
January 4, 2016 13:58
-
-
Save kykyev/8630948 to your computer and use it in GitHub Desktop.
Extracted out angular.js dependency injection.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (function() { | |
| var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; | |
| var FN_ARG_SPLIT = /,/; | |
| var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; | |
| var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; | |
| function annotate(fn) { | |
| var $inject, | |
| fnText, | |
| argDecl, | |
| last; | |
| if (typeof fn == 'function') { | |
| if (!($inject = fn.$inject)) { | |
| $inject = []; | |
| if (fn.length) { | |
| fnText = fn.toString().replace(STRIP_COMMENTS, ''); | |
| argDecl = fnText.match(FN_ARGS); | |
| forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ | |
| arg.replace(FN_ARG, function(all, underscore, name){ | |
| $inject.push(name); | |
| }); | |
| }); | |
| } | |
| fn.$inject = $inject; | |
| } | |
| } else if (isArray(fn)) { | |
| last = fn.length - 1; | |
| assertArgFn(fn[last], 'fn'); | |
| $inject = fn.slice(0, last); | |
| } else { | |
| assertArgFn(fn, 'fn', true); | |
| } | |
| return $inject; | |
| } | |
| function assertNotHasOwnProperty(name, context) { | |
| if (name === 'hasOwnProperty') { | |
| throw new Error("hasOwnProperty is not a valid name"); | |
| } | |
| } | |
| function isObject(value){ | |
| return value != null && typeof value === 'object'; | |
| } | |
| function isFunction(value){ | |
| return typeof value === 'function'; | |
| } | |
| function isArray(value) { | |
| return toString.call(value) === '[object Array]'; | |
| } | |
| function reverseParams(iteratorFn) { | |
| return function(value, key) { iteratorFn(key, value); }; | |
| } | |
| function forEach(obj, iterator, context) { | |
| var key; | |
| if (obj) { | |
| if (isFunction(obj)){ | |
| for (key in obj) { | |
| // Need to check if hasOwnProperty exists, | |
| // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function | |
| if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) { | |
| iterator.call(context, obj[key], key); | |
| } | |
| } | |
| } else if (obj.forEach && obj.forEach !== forEach) { | |
| obj.forEach(iterator, context); | |
| } else if (isArrayLike(obj)) { | |
| for (key = 0; key < obj.length; key++) | |
| iterator.call(context, obj[key], key); | |
| } else { | |
| for (key in obj) { | |
| if (obj.hasOwnProperty(key)) { | |
| iterator.call(context, obj[key], key); | |
| } | |
| } | |
| } | |
| } | |
| return obj; | |
| } | |
| function createInjector() { | |
| var INSTANTIATING = {}, | |
| providerSuffix = 'Provider', | |
| path = [], | |
| providerCache = { | |
| $provide: { | |
| provider: supportObject(provider), | |
| factory: supportObject(factory), | |
| service: supportObject(service), | |
| value: supportObject(value), | |
| constant: supportObject(constant), | |
| decorator: decorator | |
| } | |
| }, | |
| providerInjector = (providerCache.$injector = | |
| createInternalInjector(providerCache, function() { | |
| throw new Error("Unknown provider: " + path.join(' <- ')); | |
| })), | |
| instanceCache = {}, | |
| instanceInjector = (instanceCache.$injector = | |
| createInternalInjector(instanceCache, function(servicename) { | |
| var provider = providerInjector.get(servicename + providerSuffix); | |
| return instanceInjector.invoke(provider.$get, provider); | |
| })); | |
| return { | |
| sInjector: instanceInjector, | |
| pInjector: providerInjector | |
| }; | |
| //////////////////////////////////// | |
| // $provider | |
| //////////////////////////////////// | |
| function supportObject(delegate) { | |
| return function(key, value) { | |
| if (isObject(key)) { | |
| forEach(key, reverseParams(delegate)); | |
| } else { | |
| return delegate(key, value); | |
| } | |
| }; | |
| } | |
| function provider(name, provider_) { | |
| assertNotHasOwnProperty(name, 'service'); | |
| if (isFunction(provider_) || isArray(provider_)) { | |
| provider_ = providerInjector.instantiate(provider_); | |
| } | |
| if (!provider_.$get) { | |
| throw new Error("Provider must define $get factory method."); | |
| } | |
| return providerCache[name + providerSuffix] = provider_; | |
| } | |
| function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } | |
| function service(name, constructor) { | |
| return factory(name, ['$injector', function($injector) { | |
| return $injector.instantiate(constructor); | |
| }]); | |
| } | |
| function value(name, val) { return factory(name, valueFn(val)); } | |
| function constant(name, value) { | |
| assertNotHasOwnProperty(name, 'constant'); | |
| providerCache[name] = value; | |
| instanceCache[name] = value; | |
| } | |
| function decorator(serviceName, decorFn) { | |
| var origProvider = providerInjector.get(serviceName + providerSuffix), | |
| orig$get = origProvider.$get; | |
| origProvider.$get = function() { | |
| var origInstance = instanceInjector.invoke(orig$get, origProvider); | |
| return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); | |
| }; | |
| } | |
| //////////////////////////////////// | |
| // internal Injector | |
| //////////////////////////////////// | |
| function createInternalInjector(cache, factory) { | |
| function getService(serviceName) { | |
| if (cache.hasOwnProperty(serviceName)) { | |
| if (cache[serviceName] === INSTANTIATING) { | |
| throw new Error("Circular dependency found: " + path.join(' <- ')); | |
| } | |
| return cache[serviceName]; | |
| } else { | |
| try { | |
| path.unshift(serviceName); | |
| cache[serviceName] = INSTANTIATING; | |
| return cache[serviceName] = factory(serviceName); | |
| } catch (err) { | |
| if (cache[serviceName] === INSTANTIATING) { | |
| delete cache[serviceName]; | |
| } | |
| throw err; | |
| } finally { | |
| path.shift(); | |
| } | |
| } | |
| } | |
| function invoke(fn, self, locals){ | |
| var args = [], | |
| $inject = annotate(fn), | |
| length, i, | |
| key; | |
| for(i = 0, length = $inject.length; i < length; i++) { | |
| key = $inject[i]; | |
| if (typeof key !== 'string') { | |
| throw new Error("Incorrect injection token! Expected service name as string"); | |
| } | |
| args.push( | |
| locals && locals.hasOwnProperty(key) | |
| ? locals[key] | |
| : getService(key) | |
| ); | |
| } | |
| if (!fn.$inject) { | |
| // this means that we must be an array. | |
| fn = fn[length]; | |
| } | |
| // http://jsperf.com/angularjs-invoke-apply-vs-switch | |
| // #5388 | |
| return fn.apply(self, args); | |
| } | |
| function instantiate(Type, locals) { | |
| var Constructor = function() {}, | |
| instance, returnedValue; | |
| // 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) {}]); | |
| Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; | |
| instance = new Constructor(); | |
| returnedValue = invoke(Type, instance, locals); | |
| return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance; | |
| } | |
| return { | |
| invoke: invoke, | |
| instantiate: instantiate, | |
| get: getService, | |
| annotate: annotate, | |
| has: function(name) { | |
| return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); | |
| } | |
| }; | |
| } | |
| } | |
| window['createInjector'] = createInjector; | |
| })(window); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Angular's DI</title> | |
| </head> | |
| <body> | |
| <script src="/javascript/di.js"></script> | |
| <script src="/javascript/test.js"></script> | |
| </body> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var Foo = function($shell) { | |
| $shell.log('Hello'); | |
| }; | |
| var ShellProviderFn = function() { | |
| this.$get = function() { | |
| return { | |
| magic_symbol: '@', | |
| log: function(what) { | |
| console.log(this.magic_symbol + what); | |
| } | |
| }; | |
| }; | |
| }; | |
| var inj = createInjector(); | |
| var providerInjector = inj.pInjector; | |
| var serviceInjector = inj.sInjector; | |
| providerInjector.invoke(function($provide) { | |
| $provide.provider('$shell', ShellProviderFn); | |
| }); | |
| serviceInjector.invoke(Foo); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment