angular的依赖注入

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);

});

});

}

上海领思教育科技有限公司是一家致力于高素质软件开发人才培养的公司,一方面解决企业招不到优秀人才 的困扰,同时为优秀的大学毕业生提供改变命运的机遇。公司自成立以来,一直坚持采用“好老师+好学生+ 好学习气氛”的培养模式,已经培养了一批又一批的IT人才。

上海领思期待您的加盟。

地址:上海市浦东新区临港新城水芸路300号501室

电话:021-58010107

网址:http://www.lingsiedu.cn

简历投递:hr@lingsiedu.cn

//给构造函数添加$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"];

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • 在Angular中创建一个对象时,需要依赖另一个对象,这是代码层的一种依赖关系,当这种依赖被声明后,Angular...
    阿振_sc阅读 2,721评论 0 6
  • Angular算是将后端开发工程化引入前端的先驱之一,而Dependency injection依赖注入(后面简称...
    点融黑帮阅读 2,248评论 0 5
  • Angular面试题 一、ng-show/ng-hide与ng-if的区别? 第一点区别是,ng-if在后面表达式...
    w_zhuan阅读 5,515评论 0 26
  • 1、angularjs的几大特性是什么? 双向数据绑定、依赖注入、模板、指令、MVC/MVVM 2、列举几种常见的...
    2e9a10d418ab阅读 1,254评论 0 10
  • 1、angularjs的几大特性是什么? 双向数据绑定、依赖注入、模板、指令、MVC/MVVM 2、列举几种常见的...
    秀才JaneBook阅读 1,538评论 0 22