Angular算是将后端开发工程化引入前端的先驱之一,而Dependency injection依赖注入(后面简称为DI)又是Angular内部运做的核心功能,因此要深刻理解Angular有必要先理解这一核心概念。前端
在软件工程中,依赖注入是实现控制反转的一种软件设计模式,一个依赖是一个被其余对象(client)调用的对象(服务),注入则是将被依赖的对象(service)实例传递给依赖对象(client)的行为。将 被依赖的对象传给依赖者,而不须要依赖者本身去建立或查找所需对象是DI的基本原则。 依赖注入容许程序设计听从依赖倒置原则(简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就下降了客户与实现模块间的耦合) 调用者(client)只需知道服务的接口,具体服务的查找和建立由注入者(injector)负责处理并提供给client,这样就分离了服务和调用者的依赖,符合低耦合的程序设计原则。git
从维基百科解释可知, DI中包含三个角色,调用者(client), 服务(service)和注入者 (injector),下面开始介绍本文的主题 Angular的依赖注入。github
先看看下面这段 hello,world代码 (注意:设置了严格模式或压缩混淆代码后 下面的代码不能正常工做,后面有解释)编程
angular.module('myApp', []) .controller('Ctl', function ($scope, $log) { $scope.name = 'leonwgc'; $log.log('hello,world'); });
上面这段代码就用到了angular的依赖注入,代码首先建立了一个myApp模块,而后在此模块中建立了Ctl控制器,建立控制器函数的第二个参数则是控制器的构造函数, 构造函数声明了对$scope和$log服务的依赖。 当构造函数执行时, 便可得到$scope和$log服务实例,进行操做。 从咱们前面对DI的了解,$scope和$log是由注入器injector 提供,知道了injector的存在,咱们直接从angular的源码中将其找出,以下:后端
function createInternalInjector(cache, factory) { // 中间一段略去... // 调用client function invoke(fn, self, locals, serviceName) { if (typeof locals === 'string') { serviceName = locals; locals = null; } var args = [], // 查询依赖 $inject = createInjector.$$annotate(fn, strictDi, serviceName), length, i, key; // 中间一段略去... // 遍历$inject数组调用getService获取服务.... //开始执行client , args则是依赖的所有服务,injector都为咱们建立好了 return fn.apply(self, args); } // 中间一段略去... // 这里返回公开的injector对象 return { // 执行DI方法,好比上面的控制器函数 // invoke方法首先就是调用annotate取得依赖 // 而后调用get取得服务 // 若是缓存中没有服务,get内部调用instantiate建立服务并缓存 // 最后利用function.apply传入依赖并执行 invoke: invoke, // 实例化(建立)服务 instantiate: instantiate, // 获取服务(若是缓存中有,直接从缓存拿,没有则调用instantiate建立并放入缓存,下次直接从缓存拿) get: getService, // 得到依赖服务 annotate: createInjector.$$annotate, // 检查缓存中是否包含服务 has: function(name) { return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); } }; }
源码中查询依赖的源码以下:设计模式
function annotate(fn, strictDi, name) { var $inject, fnText, argDecl, last; if (typeof fn === 'function') { // 若是咱们直接给函数添加了$inject依赖 // 则直接返回依赖,后面不作处理 if (!($inject = fn.$inject)) { $inject = []; if (fn.length) { if (strictDi) { if (!isString(name) || !name) { name = fn.name || anonFn(fn); } throw $injectorMinErr('strictdi', '{0} is not using explicit annotation...', name); } // 针对直接在构造函数中使用服务的状况 // 使用function.toString() 而后正则匹配出依赖的对象 // 因此上面例子若是混淆了代码就呵呵了 // 最后存入$inject数组 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); }); }); } //给构造函数添加$inject属性 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; }
看了上面的源码片断和解释,想必你们对angular的依赖注入有了总体的认识。数组
下面是另外两种推荐的声明依赖的方式缓存
angular.module('myApp', []) .controller('Ctl', ['$scope', '$log', function ($scope, $log) { $scope.name = 'leonwgc'; $log.log('hello,world'); }]);
angular.module('myApp', []) .controller('Ctl', Ctrl); function Ctrl($scope, $log) { $scope.name = 'leonwgc'; $log.log('hello,world'); } // 给构造函数添加$inject属性, // $inject是一个数组,元素是依赖的服务名. Ctrl.$inject = ["$scope", "$log"];
Angular引入了大量后端开发的概念,而前端同窗可能还不熟悉,望本文能有所帮助。app