js依赖注入初探

本文作者:IMWeb coolriver 原文出处:IMWeb社区 未经同意,禁止转载

前言:一个题目

之前在codewars上做在线题目时遇到这样一个问题:实现一个依赖注入的“注射器”。当时对依赖注入这一概念还不是很理解,只是根据题目的要求初步认识了依赖注入。题目的要求如下:

/**
 * Constructor DependencyInjector
 * @param {Object} - object with dependencies
 */
var DI = function (dependency) {
  this.dependency = dependency;
};

// Should return new function with resolved dependencies
DI.prototype.inject = function (func) {
  // Your code goes here
}

题目先简单地创建了一个依赖注入器的构造函数,题目挑战的方法是在inject函数中编写代码,实现依赖注入。评价挑战成功与否的测试如下:

// 要注入的依赖
var deps = {
  'dep1': function () {return 'this is dep1';},
  'dep2': function () {return 'this is dep2';},
  'dep3': function () {return 'this is dep3';},
  'dep4': function () {return 'this is dep4';}
};

// 新建一个“注射器”
var di = new DI(deps);

// 注射
var myFunc = di.inject(function (dep3, dep1, dep2) {
  return [dep1(), dep2(), dep3()].join(' -> ');
});

// 测试
Test.assertEquals(myFunc(), 'this is dep1 -> this is dep2 -> this is dep3');

先来看一下我之前实现的方案:

/**
 * Constructor DependencyInjector
 * @param {Object} - object with dependencies
 */
var DI = function (dependency) {
  this.dependency = dependency;
};

// Should return new function with resolved dependencies
DI.prototype.inject = function (func) {
  // Your code goes here
  var dependency = this.dependency;

  //返回的函数
  return function(){
    var funArr = [],
        newParm = [];

    // 解析函数参数
    var parmsArr = func.toString().replace(/^function(?:\s|\r|\n)*\(([^\)]*)\)(?:.|\r|\n)*\{(?:.|\r|\n)*\}$/,"$1").split(/\s*,\s*/);
    console.log(parmsArr);
    var obj = {};

    //根据参数查找依赖
    for (var key in dependency){
      if (parmsArr.indexOf(key) >= 0){
        obj[key] = dependency[key];
        newParm.push(key);
      }
    }

    // 待注入的依赖函数定义
    for (var key in obj){
      //console.log("var "+key+" = " + obj[key].toString()+";");
      funArr.push("var "+key+" = " + obj[key].toString()+";");
    }

      // 注入依赖函数定义
    eval("var newfunc="+func.toString().replace(/^(function(?:.|\r|\n)*\{)((?:.|\r|\n)*)(\})$/,"$1"+funArr.join('')+"$2$3"));
    console.log(newfunc.toString());

    // 执行注入依赖后的函数
    return newfunc.apply(obj,newParm);
  }
}

这是较早的时候写的代码,不是很优雅,暴力地将依赖函数的定义注入到函数体中。下面看看codewars上面的大神们写的,根据网站对solution排序的最佳方案:

/**
 * Constructor DependencyInjector
 * @param {Object} - object with dependencies
 */
var DI = function (dependency) {
  this.dependency = dependency;
};

// Should return new function with resolved dependencies
DI.prototype.inject = function (func) {

  var deps = /^[^(]+\(([^)]+)/.exec(func.toString());

 //  构建参数绑定数组
  deps = deps ? deps[1]
    .split(/\s?,\s?/)
    .map(function (dep) {
      return this.dependency[dep];
    }.bind(this)) : [];

  // 通过apply将依赖参数传入函数
  return function () {
    return func.apply(this, deps);
  };

}

看完上面的方案才发现我之前的方案是多么麻烦。。。

依赖注入是什么?

在解决上面是上的问题后,回过头来想:依赖注入是啥?其实通过题目的描述以及测试代码容易理解到,依赖注入可以动态地为函数添加依赖。依赖注入在强类型语言中,如JAVA,比较常见,是一种解藕的方式。 对于如果解释和理解依赖注入,在看了一些“百科”和代码后仍然不是很清晰。后来找到了stackoverflow上的一个问题:如何向一个5岁的小孩解释依赖注入。被认同得最多的答案原文如下:

When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy doesn't want you to have. You might even be looking for something we don't even have or which has expired. What you should be doing is stating a need, "I need something to drink with lunch," and then we will make sure you have something when you sit down to eat.

基本的意思是:熊孩子不会自己弄吃的,或者吃到不该吃的,比如这样:

熊孩子只需要告诉家长“我饿了,要吃东西”,家长知道了就给熊孩子准备适合他吃的:

在js中依赖注入的概念不像java中被经常提到,主要原因是在js中很容易就实现了这种动态依赖。最简单的例子:bind函数。js可以通过bind,apply,call等函数可以很方便地控制函数的参数和this变量,所以简单地依赖注入在很多情况下已经被不知不觉地使用。在AMD的模块定义中,其方式也是一种依赖注入。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏可能是东半球最正规的API社区

扯点儿高性能(一):CGI篇【搞附近】

CGI是一种协议,并不是一种具体的代码程序。上古时代的PHP程序就是靠CGI协议与HTTP服务器比如Apache协作完成。最开始那会儿Web站点的出现一般都是纯...

7100
来自专栏前端资源

PhpStorm表单提交时获取不到post数据的解决方法

初学php,用echo $_POST["variable"]和var_dump($_POST) 都获取不到post数据。

9300
来自专栏无道编程

Windows下的Tomcat 安装与配置环境变量

地址:https://tomcat.apache.org/download-90.cgi

8600
来自专栏无道编程

Tampermonkey 本地开发【使用$.ajax】 http被禁止解决方法

在本地开发Tampermonkey(油猴)脚本,其中需要和本地服务器交互,但是运行的网站是https,本地服务器是http,请求被谷歌浏览器禁止了。

7300
来自专栏无道编程

Windows下Aria2一键启动器【用以下载百度网盘】详细教程

Aria2 下载整合一键工具是由吾爱论坛一位网友制作的,因为纯aria2是命令行,对于普通人员来说不方便使用。于是这个一键启动就有很多人制作了,这里只推荐这一款...

8800
来自专栏无道编程

Laravel旧域名永久301跳转方法【带旧参数】

有一个旧域名xxxx.misiyu.cn 是以前临时用的,然后目前启用正式域名了,想要把旧域名(xxxx.misiyu.cn )跳转到正式域名,并且是要附带域名...

7800
来自专栏无道编程

Laravel6.0发布了!你会使用这个版本吗?

早在前一个月就有听说Laravel6.0版本要出来了,其实我个人是比较期待的。对于我们个人开发者来说,开发一个小项目最好是使用一个著名开源的框架,这会节约大量成...

9700
来自专栏无道编程

Laravel 解决跨域[COS]问题【附CSRF问题】

越发觉得发博客是一种好的习惯,因为自己经历过这种坑,影响深刻。并且所附上的解决办法是真实有效的。没办法,哪些csdn之流的,转载来转载去,不能说没用,但很多都失...

5600
来自专栏无道编程

(2019)[前端]面试题[1]:小知识点大集合

答:不区分,(HTML, CSS都不区分,但为了更好的可读性和团队协作,一般都小写,而在XHTML 中元素名称和属性是必须小写的。)

7400
来自专栏无道编程

PHP本地查询IP地址归属地【非接口】

为工具站做了个查询IP地址的工具,不想用网络上的接口,因为接口有各种不稳定因素。就想在本地实现一个。

13000

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励