Angular算是将后端开发工程化引入前端的先驱之一,而Dependency injection依赖注入(后面简称为DI)又是Angular内部运作的核心功能,所以要深入理解Angular有必要先理解这一核心概念。

维基百科对依赖注入的解释

在软件工程中,依赖注入是实现控制反转的一种软件设计模式,一个依赖是一个被其他对象(client)调用的对象(服务),注入则是将被依赖的对象(service)实例传递给依赖对象(client)的行为。将 被依赖的对象传给依赖者,而不需要依赖者自己去创建或查找所需对象是DI的基本原则。 依赖注入允许程序设计遵从依赖倒置原则(简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合) 调用者(client)只需知道服务的接口,具体服务的查找和创建由注入者(injector)负责处理并提供给client,这样就分离了服务和调用者的依赖,符合低耦合的程序设计原则。

依赖注入中的角色

从维基百科解释可知, DI中包含三个角色,调用者(client), 服务(service)和注入者 (injector),下面开始介绍本文的主题 Angular的依赖注入。

Angular依赖注入分析

先看看下面这段 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的依赖注入有了整体的认识。

下面是另外两种推荐的声明依赖的方式

1. 数组注释 (推荐), js压缩混淆不会有影响。

angular.module('myApp', [])
.controller('Ctl', ['$scope', '$log', function ($scope, $log) {
$scope.name = 'leonwgc';
$log.log('hello,world');
}]);

2.$inject 属性 ,js压缩混淆不会有影响

angular.module('myApp', [])
.controller('Ctl', Ctrl); function Ctrl($scope, $log) {
$scope.name = 'leonwgc';
$log.log('hello,world');
} // 给构造函数添加$inject属性,
// $inject是一个数组,元素是依赖的服务名.
Ctrl.$inject = ["$scope", "$log"];

Angular引入了大量后端开发的概念,而前端同学可能还不熟悉,望本文能有所帮助。

Angular依赖注入详解的更多相关文章

  1. angularjs MVC、模块化、依赖注入详解

    一.MVC <!doctype html> <html ng-app> <head> <meta charset="utf-8"> ...

  2. spring 之 IOC 依赖注入详解

    当我们对一个javaBean进行实例化时,在原本的情况下我们会选择新建一个接口,然后进行实例化,为了进一步降低耦合度我们还会使用工厂模式进行封装. 例: 当我们想要去造,Chinese.America ...

  3. DI:依赖注入详解

    DI(依赖注入) 依赖注入的理解: 一般写程序的时候service层都需要用到dao层,所以一般都是在service层里面new  dao ,而现在利用依赖注入的方式,直接把dao给了service层 ...

  4. 一、.Net Core 依赖注入详解及Autofac使用

    .NET中的依赖注入实际上帮助我们解耦了我们的代码,是控制反转和依赖反转原则的具体实现. .Net Core的依赖注入的好处: 1. application 更稳定,容易维护和演化: 2. 实现细节的 ...

  5. 30行代码让你理解angular依赖注入:angular 依赖注入原理

    依赖注入(Dependency Injection,简称DI)是像C#,java等典型的面向对象语言框架设计原则控制反转的一种典型的一种实现方式,angular把它引入到js中,介绍angular依赖 ...

  6. angular 依赖注入原理

    依赖注入(Dependency Injection,简称DI)是像C#,java等典型的面向对象语言框架设计原则控制反转的一种典型的一种实现方式,angular把它引入到js中,介绍angular依赖 ...

  7. WPF依赖属性详解

    WPF依赖属性详解 WPF 依赖属性 英文译为 Dependency Properties,是WPF引入的一种新类型的属性,在WPF中有着极为广泛的应用,在WPF中对于WPF Dependency P ...

  8. angular 自定义指令详解 Directive

    在angular中,Directive,自定义指令的学习,可以更好的理解angular指令的原理,当angular的指令不能满足你的需求的时候,嘿嘿,你就可以来看看这篇文章,自定义自己的指令,可以满足 ...

  9. [译] 关于 Angular 依赖注入你需要知道的

    如果你之前没有深入了解 Angular 依赖注入系统,那你现在可能认为 Angular 程序内的根注入器包含所有合并的服务提供商,每一个组件都有它自己的注入器,延迟加载模块有它自己的注入器. 但是,仅 ...

随机推荐

  1. Java环境设置

    win7/win8下JDK环境变量设置方法 首先需要到官网上下载JDK这款软件,本人下载的是jdk-7u40-windows-i586版本,安装完成显示jdk1.7.0_67. 其次选择安装路径.本人 ...

  2. Ring0打开其他设备对象三种方式整理

    1.通过ZwCreateFile打开其他设备的Handle,此函数只能得到句柄.ZwCreateFile与NtCreateFile区别在于NtCreateFile更加底层,ZwCreateFile是基 ...

  3. 简单明了区分escape、encodeURI和encodeURIComponent

    一.前言 讲这3个方法区别的文章太多了,但是大部分写的都很绕.本文试图从实践角度去讲这3个方法. 二.escape和它们不是同一类 简单来说,escape是对字符串(string)进行编码(而另外两种 ...

  4. ABP理论学习之多租户

    返回总目录 本篇目录 什么是多租户 ABP中的多租户 什么是多租户 维基百科:"软件多租户是指一种软件架构,在这种软件架构中,软件的一个实例运行在服务器上并且为多个租户服务".一个 ...

  5. Redis系列(五)-Opserver的监控

    阅读目录: 基本介绍 使用配置 部署实例 面板属性 基本介绍 Opserver是Stack Exchange的一个开源监控系统,基于Net.MVC开发,所以Net程序员可以轻松基于它二次开发.它主要监 ...

  6. 使用vbs脚本进行批量编码转换

    使用vbs脚本进行批量编码转换 最近需要使用SourceInsight查看分析在Linux系统下开发的项目代码,我们知道Linux系统中文本文件默认编码格式是UTF-8,而Windows中文系统中的默 ...

  7. DDD领域驱动设计之领域服务

    1.DDD领域驱动设计实践篇之如何提取模型 2.DDD领域驱动设计之聚合.实体.值对象 3.DDD领域驱动设计之领域基础设施层 什么是领域服务,DDD书中是说,有些类或者方法,放实体A也不好,放实体B ...

  8. Qcon会议之所见所想

    作为普通码农一枚,Qcon是俺参与过的最高级的技术大会了.大会共历时三天,因为俺第二天就得赶火车休个五一大长假,所以只参加了第一天4/25号的会议(其他俩天自然有其他同事会去观摩),不过第一天的会议有 ...

  9. ASP.NET Web API自身对CORS的支持:从实例开始

    在<通过扩展让ASP.NET Web API支持W3C的CORS规范>中我们通过自定义的HttpMessageHandler为ASP.NET Web API赋予了跨域资源共享的能力,具体来 ...

  10. JS.中文乱码,Jsp\Servlet端的解决办法

    JS.中文乱码,Jsp\Servlet端的解决办法 2010-03-08 15:18:21|  分类: Extjs |  标签:encodeuricomponent  乱码  urldecoder   ...