Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

初识JavaScript依赖注入 #26

Open
Aaaaaaaty opened this issue Nov 2, 2017 · 6 comments
Open

初识JavaScript依赖注入 #26

Aaaaaaaty opened this issue Nov 2, 2017 · 6 comments

Comments

@Aaaaaaaty
Copy link
Owner

Aaaaaaaty commented Nov 2, 2017

写在最前

本次分享一下我学习到的有关依赖注入的梳理与总结。试图生动形象得解释出来其内部的思想与实现流程。

欢迎关注我的博客,不定期更新中——

什么是依赖注入

第一次听到这个说法是在angular的时候,我们都知道angular内部大量使用了依赖注入。虽然我到现在也没玩过:),不过这并不影响我们来探究一下它。

首先试图形象的说明一下(个人观点、有问题欢迎指正):有那么一群人,这群人的职业是程序员。他们除了工作不想费力气去做别的事。除了上班剩下的只有买吃的和买格子衫。具体吃什么和格子衫什么样子他们并不关心。那么也许我们可以提供一个公共服务,来专门为程序员提供吃的和格子衫。程序员不需要关心我们怎么做吃的和去哪里买格子衫,他们只需要告诉我们他们需要就可以了,买好之后我们自然会给他们送到。这样就可以避免每个程序员还要花费心思独自的去吃东西和买格子衫,省去了大把时间就可以更好的投入到工作中了。

刚刚那段说法可以抽象为下面这张简易示意图:
image
按照上面图的流程中我们可以知道我们需要实现这么几件事:

  • 提供一个服务容器
  • 为目标函数注册需要的依赖
  • 获取目标函数注册的依赖项
  • 通过依赖项来查询对应服务
  • 将获取的依赖项传入目标函数

提供一个服务容器

//假装提供一些服务
var services = { 
    A: () => {console.log(1)}, 
    B: () => {console.log(2)},
    C: () => {console.log(3)}
} 

为目标函数注册需要的依赖

// 目标函数 
function Service(A, B) { 
    A()
    B()
} 

目前的注册方式采用在形参的方式来传递,我们不需要关心A、B是怎么实现的,我们只需要知道这些代表着吃的和格子衫就可以了:)

获取目标函数注册的依赖项

// 获取func的参数列表(依赖列表) 
getFuncParams = function (func) { 
    var matches = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m); 
    if (matches && matches.length > 1) 
        return matches[1].replace(/\s+/, '').split(','); 
    return []; 
}

实现原理为将传入的目标函数进行正则匹配,匹配出形参。这其中的关键点在于这段正则表达式:

/^function\s*[^\(]*\(\s*([^\)]*)\)/m

其中\(\s*([^\)]*
通过括号来提取匹配到function后面参数括号的内部内容,也就是可以得到参数字符串。这里面是运用了括号的提取数据的规则来获取的信息,规则如下:

var regex = /(\d{4})-(\d{2})-(\d{2})/;
var string = "2017-06-12";
console.log( string.match(regex) ); 
// => ["2017-06-12", "2017", "06", "12", index: 0, input: "2017-06-12"]

结果数组中第一个元素为匹配结果,之后为括号内的数据,由此我们便可知道,这段正则通过括号的使用,获取到了整个形参作为一个字符串,之后再通过split进行拆分就得到了我们想要的结果。

通过依赖项来查询对应服务

//简易实现
setFuncParams = function (params) { 
    for (var i in params) { 
        params[i] = services[params[i]]; 
    } 
    return params; 
}; //依次对应服务中的项进行查找返回结果。

将获取的依赖项传入目标函数

// 注射器 
function Activitor(func, scope) { 
   return () => {
       func.apply(scope || {}, setFuncParams(getFuncParams(func)));
   } 
} 
// 实例化Service并调用方法 
var service = Activitor(Service); 
service();//1 2

小结

至此我们便完整地实现了一个很简单的依赖注入的模式,源码在这里。非常简单,同时也没有做很多的判断。不过核心的思路还是梳理了出来。自己闷头琢磨了半天,有不对的地方欢迎斧正~ PS:下面的几篇参考资料写的都很好,其中颜海镜老师的JavaScript里的依赖注入很有深意,拜读了很久,也分享给大家。

参考资料

最后

惯例po作者的博客,不定时更新中——
有问题欢迎在issues下交流。

@nikolausliu
Copy link

不穿格子衫的程序员正在角落瑟瑟发抖:)

@yanlee26
Copy link

yanlee26 commented Nov 3, 2017

可以顺带讲解一下ng或vue的依赖注入机制吗?

@Aaaaaaaty
Copy link
Owner Author

@nikolausliu 我其实也不穿哈哈哈

@Aaaaaaaty
Copy link
Owner Author

@yanlee26 我其实推荐了这篇文章 http://yanhaijing.com/javascript/2014/01/24/dependency-injection-in-javascript/ 这个里面就是模仿了ng的依赖注入的形式,虽然不是其内部源码讲解但是思路还是有的

@JackZong
Copy link

JackZong commented Jun 7, 2018

nice

@BigKongfuPanda
Copy link

BigKongfuPanda commented Dec 4, 2018

楼主,我个人觉得你这篇文章里面的代码有些地方不太好。

  1. 不应该用 for in 来遍历数组。在 通过依赖项来查询对应服务 中,参数 params 为数组对吧,for in 循环数组的下标之后,下标 i 就从 number 类型变为了 string 类型了,这是使用 for in 来遍历数组的弊端,所以应该避免这么操作。
//简易实现
setFuncParams = function (params) { 
    for (var i in params) { 
        params[i] = services[params[i]]; 
    } 
    return params; 
}; /
  1. 感觉直接将一个变量的类型从基本类型变为引用类型,不太好。比如上面的代码中,params[i] 本来是函数名,为字符串,而你在进行 params[i] = services[params[i]]; 之后,将 params[i] 变为了 function ,这个地方,你完全可以在定义一个新的数组来存放 services[params[i]] 的值。

以上只是个人的看法哈。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants