AngularJs依赖注入原理
什么是依赖注入?将一个或多个依赖(或服务)注入到一个独立的对象中。
在angular中,provider、factory、service、value、constant可以用于注入,控制器和工厂方法(指令、服务、过滤器)可接受注入。
当应用(app)引导启动时,angular生成一个注入器,用于寻找并注入app中所有引入的服务(例如,$http、$route)。
下面是依赖注入的简单实现:
var DI = { /** * 保存能够被注入的服务 */ providerCache: {}, /** * 注册一个新的服务时,以key: value形式保存在providerCache map中 * @param key * @param value */ register: function (key, value) { this.providerCache[key] = value; }, /** * 实现依赖注入 * @param fn * @param self * @returns {*} */ inject: function (fn, self) { var $inject = this.annotate(fn), //获得函数的参数(被注入的对象key值) args = []; //遍历providerCache获得所有注入的对象,用一个数组记录 for (var i = 0, len = $inject.length; i < len; i++) { args.push(this.providerCache[$inject[i]]); } if (isArray(fn)) { fn = fn[len]; } //注入 return fn.apply(self, args); }, /** * 提取函数的参数 * @param fn * @returns {Array} */ annotate: function (fn) { var fnString = fn.toString(), args = [], FUNC_ARGS = /^function\s*[^(]*\(\s*([^)]*)\s*\)/m, FUNC_ARG_SPLIT = /,\s*/; if (isFunction(fn)) { args = fnString.match(FUNC_ARGS)[1].split(FUNC_ARG_SPLIT); } else if (isArray(fn)) { args = fn.slice(0, fn.length - 1); } return args; } } function isFunction(fn) { return typeof fn === 'function'; } function isArray(arr) { return Object.prototype.toString.call(arr) === '[object Array]'; }
可运行例子:
/** * provider定义方法 * @param name * @param fn */ function registerProvider(name, fn) { var obj = DI.inject(fn); DI.register(name, obj); } /** * controller定义方法 * @param name * @param fn */ function registerController(name, fn) { DI.inject(fn); } registerProvider('provider1', function () { return { provider1: 'foo' } }) registerProvider('provider2', function (provider1) { return { provider2: provider1.provider1 + ' bar' } }) registerController('controller', ['provider2', function (provider2) { console.log(provider2.provider2); }])
AngularJs中,注入器是在angular启动时生成的,源码中,angular启动函数(bootstrap)中,代码:
var injector = createInjector(modules);
生成了一个注入器,注入器的具体实现可查看源码createInjector函数。