模拟AngularJS之依赖注入
一、概述 |
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>
代码稍长,请自行打开
五、参考 |
2、Dependency injection in JavaScript
模拟AngularJS之依赖注入的更多相关文章
- AngularJS DI(依赖注入)实现推测
AngularJS DI(依赖注入) http://www.cnblogs.com/whitewolf/archive/2012/09/11/2680659.html 回到angularjs:在框架中 ...
- AngularJS的依赖注入方式
在定义controller,module,service,and directive时有两种方式, 方式一: var myModule = angular.module('myApp', []); m ...
- AngularJS(15)-依赖注入
AngularJS 依赖注入 什么是依赖注入 wiki 上的解释是:依赖注入(Dependency Injection,简称DI)是一种软件设计模式,在这种模式下,一个或更多的依赖(或服务)被注入(或 ...
- Angularjs之依赖注入
一个对象通常有三种方式可以获得对其依赖的控制权: 在内部创建依赖: 通过全局变量进行引用: 在需要的地方通过参数进行传递 依赖注入是通过第三种方式实现的.比如: function SomeClass( ...
- AngularJS:依赖注入
ylbtech-AngularJS:依赖注入 1.返回顶部 1. AngularJS 依赖注入 什么是依赖注入 wiki 上的解释是:依赖注入(Dependency Injection,简称DI)是一 ...
- 【转】简单模拟angular的依赖注入
原文:http://www.oschina.net/code/snippet_1181081_35136 代码片段 var angular = function(){}; Object.defineP ...
- 我的angularjs源码学习之旅2——依赖注入
依赖注入起源于实现控制反转的典型框架Spring框架,用来削减计算机程序的耦合问题.简单来说,在定义方法的时候,方法所依赖的对象就被隐性的注入到该方法中,在方法中可以直接使用,而不需要在执行该函数的时 ...
- AngularJS(十):依赖注入
本文也同步发表在我的公众号“我的天空” 依赖注入 依赖注入不是AngularJS独有的概念,而是现代软件开发与架构的范畴,但是在AngularJS中“依赖注入”是其核心思想之一,所以我们专门来学习一下 ...
- 细数Javascript技术栈中的四种依赖注入
作为面向对象编程中实现控制反转(Inversion of Control,下文称IoC)最常见的技术手段之一,依赖注入(Dependency Injection,下文称DI)可谓在OOP编程中大行其道 ...
随机推荐
- In-Memory:内存优化表的事务处理
内存优化表(Memory-Optimized Table,简称MOT)使用乐观策略(optimistic approach)实现事务的并发控制,在读取MOT时,使用多行版本化(Multi-Row ve ...
- .NetCore中的日志(1)日志组件解析
.NetCore中的日志(1)日志组件解析 0x00 问题的产生 日志记录功能在开发中很常用,可以记录程序运行的细节,也可以记录用户的行为.在之前开发时我一般都是用自己写的小工具来记录日志,输出目标包 ...
- 继电器是如何成为CPU的(1)
继电器是如何成为CPU的(1) ——<穿越计算机的迷雾>整理和总结 究竟是如何设计的电路,具有计算和控制的智力? 这一点也不高深.本系列文章从初中学的最简单的电路图说起,看看能不能从最初的 ...
- VisualStudio2013 如何打开之前版本开发的(.vdproj )安装项目
当你的项目使用早于 visualstudio2013 的版本开发并且使用 Visual Studio Installer 制作安装项目时,在升级至 VS2013 后会发现新安装项目无法打开, VS20 ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统 (源码购买说明)
系列目录 升级日志 !!!重大版本更新:于2016-12-20日完成了系统的结构重构并合并简化了T4(这是一次重要的更新,不需要修改现有功能的代码),代码总行数比上个版本又少了1/3.更新了代码生成器 ...
- 如何利用ansible callback插件对执行结果进行解析
最近在写一个批量巡检工具,利用ansible将脚本推到各个机器上执行,然后将执行的结果以json格式返回来. 如下所示: # ansible node2 -m script -a /root/pyth ...
- Anders Hejlsberg 技术理想架构开发传奇
Anders Hejlsberg(安德斯-海森博格) 坐在自己的办公室,双眼直直的盯着前方.他要做一个决定,决定自己未来的命运和理想.这是1996年一个普通的下午,几个小时前,他刚与比尔-盖茨结束了 ...
- 原生javascript 固定表头原理与源码
我在工作中需要固定表头这个功能,我不想去找,没意思.于是就写了一个,我写的是angularjs 自定义指令 起了个 "fix-header" ,有人叫 "freeze- ...
- 基于DFA敏感词查询的算法简析
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 项目中需要对敏感词做一个过滤,首先有几个方案可以选择: a.直 ...
- 【JavaScript】innerHTML、innerText和outerHTML的用法区别
用法: <div id="test"> <span style="color:red">test1</span> tes ...