Skip to content

初识 JavaScript 依赖注入

https://github.com/Aaaaaaaty/Blog/issues/26

什么是依赖注入

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

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

刚刚那段说法可以抽象为下面这张简易示意图:

按照上面图的流程中我们可以知道我们需要实现这么几件事:

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

提供一个服务容器

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

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

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

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

javascript
// 获取 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 [];
};
// 获取 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 [];
};

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

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

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

javascript
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"]
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 进行拆分就得到了我们想要的结果。

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

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

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

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

小结

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

参考资料

最后编辑时间:

Version 4.0 (framework-1.0.0-rc.20)