coolriver

1年前
  • 2590

    浏览
  • 2

    评论
  • 0

    收藏

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的模块定义中,其方式也是一种依赖注入。

2 条评论