一、概述

AngularJS有一经典之处就是依赖注入,对于什么是依赖注入,熟悉spring的同学应该都非常了解了,但,对于前端而言,还是比较新颖的。

依赖注入,简而言之,就是解除硬编码,达到解偶的目的。

下面,我们看看AngularJS中常用的实现方式。

方法一:推断式注入声明,假定参数名称就是依赖的名称。因此,它会在内部调用函数对象的toString()方法,分析并提取出函数参数列表,然后通过$injector将这些参数注入进对象实例。

如下:

//方法一:推断式注入声明,假定参数名称就是依赖的名称。
//因此,它会在内部调用函数对象的toString()方法,分析并提取出函数参数列表,
//然后通过$injector将这些参数注入进对象实例
injector.invoke(function($http, $timeout){
//TODO
});

方法二:行内注入声明,允许我们在函数定义时,直接传入一个参数数组,数组包含了字符串和函数,其中,字符串代表依赖名,函数代表目标函数对象。

如下:

//方法二:行内注入声明,允许我们在函数定义时,直接传入一个参数数组,
//数组包含了字符串和函数,其中,字符串代表依赖名,函数代表目标函数对象。
module.controller('name', ['$http', '$timeout', function($http, $timeout){
//TODO
}]);

看了上述代码,心中有一疑问,这些是怎么实现的呢?

哈哈,下面,我们就来一起模拟一下这些依赖注入方式,从而了解它们。

该篇博客原文地址:http://www.cnblogs.com/giggle/p/5769169.html

二、搭建基本骨架

依赖注入的获取过程就是,通过字段映射,从而获取到相应的方法。

故而,要实现一个基本的依赖注入,我们需要一个存储空间(dependencies),存储所需键值(key/value);一个注册方法(register),用于新增键值对到存储空间中;还有一个就是核心实现方法(resolve),通过相关参数,到存储空间中获得对应的映射结果。

So,基本骨架如下:

var injector = {
dependencies: {},
register: function(key, value){
this.dependencies[key] = value;
return this;
},
resolve: function(){ }
};
三、完善核心方法resolve

从我们搭建的基本骨架中,可以发现,重点其实resolve方法,用于实现我们具体形式的依赖注入需求。

首先,我们来实现,如下形式的依赖注入:推断式注入声明。

如下:

injector.resolve(function(Monkey, Dorie){
Monkey();
Dorie();
});

要实现上述效果,我们可以利用函数的toString()方法,我们可以将函数转换成字符串,从而通过正则表达式获得参数名,即key值。 然后通过key值,在存储空间dependencies找value值,没找到对应值,则报错。

实现如下:

var injector = {
dependencies: {},
register: function(key, value){
this.dependencies[key] = value;
return this;
},
resolve: function(){
var func, deps, args = [], scope = null;
func = arguments[0];
//获取函数的参数名
deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, '').split(',');
scope = arguments[1] || {};
for(var i = 0, len = deps.length; i < len, d = deps[i]; i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
throw new Error('Can\'t find ' + d);
}
}
func.apply(scope, args);
}
};

测试代码,如下:

<!DOCTYPE html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<script>
var injector = {
dependencies: {},
register: function(key, value){
this.dependencies[key] = value;
return this;
},
resolve: function(){
var func, deps, args = [], scope = null;
func = arguments[0];
//获取函数的参数名
deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, '').split(',');
scope = arguments[1] || {};
for(var i = 0, len = deps.length; i < len, d = deps[i]; i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
throw new Error('Can\'t find ' + d);
}
}
func.apply(scope, args);
}
};
//测试代码
injector.register('Monkey', function(){
console.log('Monkey');
}).register('Dorie', function(){
console.log('Dorie');
});
injector.resolve(function(Monkey, Dorie){
Monkey();
Dorie();
console.log('-.-');
});
</script>
</body>
</html>

代码稍长,请自行打开

推断式注入声明,有个缺点,就是不能利用压缩工具压缩,因为我们是通过函数的参数作为依赖的,当我们压缩时,会将参数名改掉,参数名都变了,那肯定扑街咯。

那么下面,我们就看看行内注入声明,它可以弥补这缺点。

实现行内注入声明,如下:

injector.resolve(['Monkey', 'Dorie', function(M, D){
M();
D();
}]);

利用typeof判断arguments[0]的类型可以辨别并获得依赖参数和函数。

实现如下:

var injector = {
dependencies: {},
register: function(key, value){
this.dependencies[key] = value;
return this;
},
resolve: function(){
var firstParams, func, deps = [], scope = null, args = [];
firstParams = arguments[0];
scope = arguments[1] || {};
//获得依赖参数
for(var i = 0, len = firstParams.length; i < len; i++){
var val = firstParams[i],
type = typeof val;
if(type === 'string'){
deps.push(val);
}else if(type === 'function'){
func = val;
}
}
//通过依赖参数,找到关联值
for(i = 0, len = deps.length; i < len, d = deps[i]; i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
throw new Error('Can\'t find ' + d);
}
}
func.apply(scope || {}, args);
}
};

代码稍长,请自行打开

测试代码,如下:

<!DOCTYPE html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<script>
var injector = {
dependencies: {},
register: function(key, value){
this.dependencies[key] = value;
return this;
},
resolve: function(){
var firstParams, func, deps = [], scope = null, args = [];
firstParams = arguments[0];
scope = arguments[1] || {};
//获得依赖参数
for(var i = 0, len = firstParams.length; i < len; i++){
var val = firstParams[i],
type = typeof val;
if(type === 'string'){
deps.push(val);
}else if(type === 'function'){
func = val;
}
}
//通过依赖参数,找到关联值
for(i = 0, len = deps.length; i < len, d = deps[i]; i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
throw new Error('Can\'t find ' + d);
}
}
func.apply(scope || {}, args);
}
};
//测试代码
injector.register('Monkey', function(){
console.log('Monkey');
}).register('Dorie', function(){
console.log('Dorie');
});
injector.resolve(['Monkey','Dorie',function(M, D){
M();
D();
console.log('-.-');
}]);
</script>
</body>
</html>

代码稍长,请自行打开

因为行内注入声明,是通过字符串的形式作为依赖参数,so,压缩也不怕咯。

最后,我们将上面实现的两种方法,整合到一起,就可以为所欲为啦。

那,就合并下吧,如下:

var injector = {
dependencies: {},
register: function(key, value){
this.dependencies[key] = value;
return this;
},
resolve: function(){
var firstParams, func, deps = [], scope = null, args = [];
firstParams = arguments[0];
scope = arguments[1] || {};
//判断哪种形式的注入
if(typeof firstParams === 'function'){
func = firstParams;
deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, '').split(',');
}else{
for(var i = 0, len = firstParams.length; i < len; i++){
var val = firstParams[i],
type = typeof val;
if(type === 'string'){
deps.push(val);
}else if(type === 'function'){
func = val;
}
}
}
//通过依赖参数,找到关联值
for(i = 0, len = deps.length; i < len, d = deps[i]; i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
throw new Error('Can\'t find ' + d);
}
}
func.apply(scope || {}, args);
}
};

代码稍长,请自行打开

四、花絮—RequireJS之依赖注入

依赖注入并非在AngularJS中有,倘若你使用过RequireJS,那么下面这种形式,不会陌生吧:

require(['Monkey', 'Dorie'], function(M, D){
//TODO
});

通过,上面我们一步步的模拟AngularJS依赖注入的实现,想必,看到这,你自己也会豁然开朗,换汤不换药嘛。

模拟实现如下:

var injector = {
dependencies: {},
register: function(key, value){
this.dependencies[key] = value;
return this;
},
resolve: function(deps, func, scope){
var args = [];
for(var i = 0, len = deps.length; i < len, d = deps[i]; i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
throw new Error('Can\'t resolve ' + d);
}
}
func.apply(scope || {}, args);
}
};

测试代码如下:

<!DOCTYPE html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<script>
var injector = {
dependencies: {},
register: function(key, value){
this.dependencies[key] = value;
return this;
},
resolve: function(deps, func, scope){
var args = [];
for(var i = 0, len = deps.length; i < len, d = deps[i]; i++){
if(this.dependencies[d]){
args.push(this.dependencies[d]);
}else{
throw new Error('Can\'t resolve ' + d);
}
}
func.apply(scope || {}, args);
}
};
//测试代码
injector.register('Monkey', function(){
console.log('Monkey');
}).register('Dorie', function(){
console.log('Dorie');
});
injector.resolve(['Monkey', 'Dorie'], function(M, D){
M();
D();
console.log('-.-');
});
</script>
</body>
</html>

代码稍长,请自行打开

五、参考

1、AngularJS应用开发思维之3:依赖注入

2、Dependency injection in JavaScript

模拟AngularJS之依赖注入的更多相关文章

  1. AngularJS DI(依赖注入)实现推测

    AngularJS DI(依赖注入) http://www.cnblogs.com/whitewolf/archive/2012/09/11/2680659.html 回到angularjs:在框架中 ...

  2. AngularJS的依赖注入方式

    在定义controller,module,service,and directive时有两种方式, 方式一: var myModule = angular.module('myApp', []); m ...

  3. AngularJS(15)-依赖注入

    AngularJS 依赖注入 什么是依赖注入 wiki 上的解释是:依赖注入(Dependency Injection,简称DI)是一种软件设计模式,在这种模式下,一个或更多的依赖(或服务)被注入(或 ...

  4. Angularjs之依赖注入

    一个对象通常有三种方式可以获得对其依赖的控制权: 在内部创建依赖: 通过全局变量进行引用: 在需要的地方通过参数进行传递 依赖注入是通过第三种方式实现的.比如: function SomeClass( ...

  5. AngularJS:依赖注入

    ylbtech-AngularJS:依赖注入 1.返回顶部 1. AngularJS 依赖注入 什么是依赖注入 wiki 上的解释是:依赖注入(Dependency Injection,简称DI)是一 ...

  6. 【转】简单模拟angular的依赖注入

    原文:http://www.oschina.net/code/snippet_1181081_35136 代码片段 var angular = function(){}; Object.defineP ...

  7. 我的angularjs源码学习之旅2——依赖注入

    依赖注入起源于实现控制反转的典型框架Spring框架,用来削减计算机程序的耦合问题.简单来说,在定义方法的时候,方法所依赖的对象就被隐性的注入到该方法中,在方法中可以直接使用,而不需要在执行该函数的时 ...

  8. AngularJS(十):依赖注入

    本文也同步发表在我的公众号“我的天空” 依赖注入 依赖注入不是AngularJS独有的概念,而是现代软件开发与架构的范畴,但是在AngularJS中“依赖注入”是其核心思想之一,所以我们专门来学习一下 ...

  9. 细数Javascript技术栈中的四种依赖注入

    作为面向对象编程中实现控制反转(Inversion of Control,下文称IoC)最常见的技术手段之一,依赖注入(Dependency Injection,下文称DI)可谓在OOP编程中大行其道 ...

随机推荐

  1. 旺财速啃H5框架之Bootstrap(五)

    在上一篇<<旺财速啃H5框架之Bootstrap(四)>>做了基本的框架,<<旺财速啃H5框架之Bootstrap(二)>>篇里也大体认识了bootst ...

  2. 关于 Chrome 浏览器中 onresize 事件的 Bug

    我在写插件时用到了 onresize 事件,在反复地测试后发现该事件在 Chrome 及 Opera(内核基本与 Chrome 相同,以下统称 Chrome)浏览器打开时就会执行,这种情况也许不能算作 ...

  3. 一个免费的、跨平台的、开源音频编辑器Audacity

    Audacity 是一个免费的开源程序,用于编辑音频录制.它可在多个平台(windows/linux)上运行.Audacity 基于 GUI,是一个具有多种选项的强大程序.它支持您录制各种类型的声音. ...

  4. Web APi之认证(Authentication)两种实现方式【二】(十三)

    前言 上一节我们详细讲解了认证及其基本信息,这一节我们通过两种不同方式来实现认证,并且分析如何合理的利用这两种方式,文中涉及到的基础知识,请参看上一篇文中,就不再叙述废话. 序言 对于所谓的认证说到底 ...

  5. 第一个移动前端开源项目-dailog

    你还在为手机上没有忙碌光标而发愁吗?你还在抱怨弹出框组件要依赖zepto/jqery吗?你还在纠结是否要自己写一套还是去网上寻找成现成的UI组件吗?YouA为你轻松解决所有烦恼.YouA是我为移动前端 ...

  6. spring注解源码分析--how does autowired works?

    1. 背景 注解可以减少代码的开发量,spring提供了丰富的注解功能.我们可能会被问到,spring的注解到底是什么触发的呢?今天以spring最常使用的一个注解autowired来跟踪代码,进行d ...

  7. iOS从零开始学习直播之3.美颜

      任何一款直播软件都必须进行美颜,不然哪来的那么多美女,所以技术改变世界,不只是说说而已.美颜在采集的时候就得就行,让主播实时看到直播的效果. 1.美颜原理   其实美颜的本质就是美白和磨皮,分别通 ...

  8. Linux下高cpu解决方案

    昨天搞定了一个十万火急的issue,客户抱怨产品升级后系统会变慢和CPU使用率相当高,客户脾气很大,声称不尽快解决这个问题就退货,弄得我们 R&D压力很大,解决这个issue的任务分给了我,客 ...

  9. 免费开源的 .NET 分布式组件库 Exceptionless Foundatio

    前言 在互联网时代,分布式应用.系统变得越来越多,我们在使用 .Net 技术构建分布式系统的时候,需要使用到一些组件或者是助手库来帮助我们提高生产力以及应用程序解耦,但是纵观.Net圈,能够符合要求的 ...

  10. Hello Jexus

    一.关于 CentOS CentOS(Community Enterprise Operating System)是Linux发行版之一,它是来自于Red Hat Enterprise Linux依照 ...