我们大家近几年都接触过一个前端框架叫做 angularjs,里面对它对描述非常简单,MVW(Model-View-Whatever)、模块化、自动化双向数据绑定、语义化标签、依赖注入,每一个标签都非常都高大上,就跟我们看名片一样,某某集团公司总经理/中国xxx理事会成员/世界xxx组织干事, 看起来不明觉厉 好高大上都样子 但是到底是特么什么玩应 我们还是很迷糊都,就觉得很厉害都样子
最近正好无聊 跟大家吹牛逼 动不动就是源码啦 底层了,博客 github地址啦, 让我怀疑这个社会大家都技术都到达了好几层楼楼都高度,然后一看~尼玛 全特么是你吵我 我抄你都文章 , 为了以后能来个小十三 也偶尔写写自己对框架底层和源码对编写。
这里首先讲讲什么是依赖注入,先上图为证
现实世界中,当我们要买一件或几件商品时候,首先需要的是金钱,当没有金钱的时候会向父母索取,那么我们最后实现的目的 也就是买东西的多少和价钱 取决于父母给予我们的多少.
程序同样如此,我们要实现一个功能,如以加密格式对数据进行加密,就要使用md5这个插件或其他加密工具, 在这里 程序的结果 和 加密算法 就是一个依赖关系,只有有加密算法 我们才能又一个加密的数据。
那依赖我们简单知道了,什么是注入呢? 还是看代码,以angular为例
<script> var person = {name:"黎明",age:18} window.onload = function() { alert(person.name+"说我今年"+person.age) } </script>
在上面代码中,alert程序需要依赖与person对象,但是在程序运行完以后person对象就没有存在的价值了,在这个时候,person还是会继续暂居在内存中,这时候我们就有两种处理方式 1.等待浏览器或程序关闭,自动清除内存, 2 手动删除程序
我们换一种编写方式,将person对象只有在程序需要的时候再将其创建,当引用完成再对它删除
1 <script> 2 3 4 5 var createPerson = function() { 6 var person = {name:"黎明",age:18} 7 return person; 8 } 9 10 11 window.onload = function() { 12 13 var obj = createPerson(); 14 15 alert(obj.name+"说我今年"+obj.age) 16 17 } 18 19 20 </script>
在这里,创建person对函数为createPerson,如果程序没有运行就是一个函数表达式而已,占据很少对内存效果,在window.onload中 我们需要使用调用createPerson创建对象,在使用完成后,函数内部自动进行GC销毁,这个过程就是注入,当然有人会说我看到对是显示声明创建对象 没有注入啊。 如果我们把 创建var obj = createPerson() 的过程 以自动加载的方式表示出来,并提供缓存/队列/单例 等功能 那么就是一个注入等过程了 ,因为有依赖 所以我们要调用,在调用等过程又不想显示声明,想自动化处理 所以提供了注入等思想概念。
我们首先以angularjs等思维方式简单编写一个依赖注入程序
var angular = function() {
}
angular.module = function() {
return this;
}
//其实更建议使用prototype编写,但是怕麻烦 大家看不懂就放弃了
angular.$httpProvider = function() {
}
angular.controller = function(name,fn) {
fn();
console.info("控制器结束");
}
var app = angular.module("a");
app.controller("indexCtrl",function() {
console.info("运行控制器");
});
上面是以我个人理解对ng简单编写的一个小函数,在上面中,我们很容易看到 controller运行流程是: 1.运行控制器 2.控制器结束
我们接下来完善$httpProvider服务,并在controller中进行引用
angular.$httpProvider = function() {
//ajax封装 => 代码意思意思就好 不要较真
var ajax = function(obj) {
var xhr = new XMLHttpRequest();
xhr.open(obj.method,obj.uri,obj.async);
xhr.onreadystatechange = function() {
if(xhr.readyState==4 && xhr.status == 200) {
var data = JSON.parse(xhr.responseText);
obj.callBack(xhr.responseText);
}
}
xhr.send();
}
this.$http = ajax;
}
app.controller("indexCtrl", function() {
console.info(this, this.$httpProvider , "打印数据" );
})
在最终打印我们可以看到 结果是 ( window对象,undefined , '打印数据' ),也就是说 this指针不能直接指向app,那有的小伙伴说了,我们可以直接使用app.$httpProvider 进行调用,不是说不可以,但是如果大家接触过设计模式和面向对象的编程方式就知道是不可取的,而且 我们要尽量做到让程序智能化,也就是说 进行引用的时候 让程序内部去进行调用,我们只需要使用现成的就好了,我们继续更改代码
//错误代码
angular.controller = function(name,fn) {
fn();
console.info("控制器结束");
}
//正确代码
angular.controller = function(name,fn) {
var http = angular.$httpProvider(); //步骤1 调用依赖的 ajax
fn(http); //步骤2 注入进ajax
}
app.controller("indexCtrl",function($httpProvider) {
$httpProvider.get({
method:"get",
uri:"https://www.baidu.com"
})
});
那有些小伙伴看到这里又要炸锅了,什么破玩应,为啥在angular.controller里直接就创建ajax对象了,不应该是我注入什么创建什么么,所以我们需要在 上面代码的步骤1中 将 传入进来的函数进行拆分截取获取对应的服务名称
angular.controller = function(name,fn) {
var fnStr = fn.toString(); //将调用controller传入进来的匿名函数转化为字符串,通过正则 获取里面传入的参数名称
//为了简单我们直接索引获取 ()内部的参数
var startI = fnStr.indexOf('(')+1,lastI = fnStr.indexOf(')');
var params = fnStr.substring(startI,lastI).split(',');
//params 就是我们传入进去的服务名称,很神奇吧 其实这里我们要获取的是它字符串
//接下来就是通过特定的函数去寻找到对应的服务,这里面在ng中成为注入器 inject
var injectArr = [];
params.forEach(function(vl) {
//循环调用每一个注入器需要的实例对象
var injectObj = angular[vl]()
injectArr.push(injectObj) //临时保存数组中 后期注入进去
});
//apply替换参数,因为不是确定有多少个参数传递进去 以为数组 apply的方式传值
fn.apply(this,injectArr)
}
上面代码看起来虽然突然间多了一点,但是还是可以看明白多,也就是说 我们传入进去多函数 获取参数名称,根据参数名称寻找到对应到服务去创建,然后将服务到实例对象传如匿名函数中运行就好了。这样就简单多了,我们重新梳理下理解多逻辑。
当然,我这里面只是写了一个非常简单到依赖注入关系,并没有对单例/缓存/队列做出处理,一个完整对依赖注入还是要考虑到这些到。
既然东西我们写完了,我们在回首下依赖注入到标准化定义:
依赖注入: 将程序创建到过程交给框架进行处理,管理程序对声明周期和模型,自动注入到需要使用到另一个程序中到过程,成为依赖注入。
我们可以看到一个最大到特点,依赖注入这种程序到编写和理解方式对我们对代码初期造成了极大对复杂度,但是如果程序越来越大,特别是呈几何倍对增长时,依赖注入管理程序带来对好处与所损耗对性能来说是不值一提对。
本来就是做个随笔 大家简单清除这个流程就好了,我们稍微不填点代码 将注入对对象缓存在对象中,这样我们就好理解为什么在ng对程序中服务只创建一次(其实叫注册),为什么服务没有对应对会报错了
var angular = function() {
}
angular.module = function() {
return this;
}
angular.$httpProvider = function() {
//服务只创建一次 单例模式
console.info("初始化$http服务");
var ajax = function(obj) {
var xhr = new XMLHttpRequest();
xhr.open(obj.method,obj.uri,obj.async);
xhr.onreadystatechange = function() {
if(xhr.readyState==4 && xhr.status == 200) {
var data = JSON.parse(xhr.responseText);
obj.callBack(xhr.responseText);
}
}
xhr.send();
}
return {
get:ajax,
post:ajax,
test:"测试"
}
}
angular.cacheProvider = {}
angular.controller = function(name,fn) {
var fnStr = fn.toString();
var startI = fnStr.indexOf('(')+1,lastI = fnStr.indexOf(')');
var params = fnStr.substring(startI,lastI).split(',');
var injectArr = [];
params.forEach(function(vl) {
var injectObj;
//看看是否存在 不存在创建 存在直接走缓存
if(angular.cacheProvider[vl]) {
injectObj = angular.cacheProvider[vl];
} else {//没有创建过 新创建
try {
injectObj = angular[vl]()
angular.cacheProvider[vl] = injectObj
}catch(e) {
console.error("没有映射到对服务名称");
return;
}
}
injectArr.push(injectObj)
});
fn.apply(this,injectArr)
}
var app = angular.module("a");
var fn = function($httpProvider) {
console.info("运行控制器",$httpProvider,$httpProvider.test);
}
app.controller("indexCtrl",fn);
app.controller("secondCtrl",fn);