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 条评论
登录 后参与评论

相关文章

来自专栏IT技术精选文摘

sed、awk——运维必须掌握的两个工具

12360
来自专栏决胜机器学习

设计模式专题(二十四) ——访问者模式

设计模式专题(二十四)——访问者模式 (原创内容,转载请注明来源,谢谢) 一、概述 访问者模式(visitor)表示一个作用于某对象结构中的各元素的操作,它使...

359120
来自专栏熊二哥

Python快速入门

最近在很多地方都可以看到Python的身影,尤其在人工智能等科学领域,其丰富的科学计算等方面类库无比强大。很多身边的哥们也提到Python非常的简洁方便,比如用...

343100
来自专栏北京马哥教育

Python with提前退出:坑与解决方案

? 问题的起源 早些时候使用with实现了一版全局进程锁,希望实现以下效果: ? 全局进程锁本身不用多说,大部分都依靠外部的缓存来实现的,redis上用的是s...

30250
来自专栏从流域到海域

《笨办法学Python》 第12课手记

《笨办法学Python》 第12课手记 本节课接着讲raw_input这个函数,其后面的括号里的字符串可以显示在屏幕上。 原代码如下: age = raw_in...

21370
来自专栏九彩拼盘的叨叨叨

EditorConfig 介绍

当多人共同开发一个项目的时候,往往会出现大家用不同编辑器的情况。就前端开发者来说,有人喜欢 Sublime,有人喜欢 Webstorm , 也有人喜欢 Atom...

9510
来自专栏Java后端技术栈

Java 虚拟机内存区域划分详解(1)

JVM,java virtual machine, 即Java虚拟机,是运行java class文件的程序。

11040
来自专栏CDA数据分析师

不能不懂的 Python 7大功能和特点

在使用Python多年以后,我偶然发现了一些我们过去不知道的功能和特性。一些可以说是非常有用,但却没有充分利用。考虑到这一点,我编辑了一些你应该了解的Pytho...

19780
来自专栏技术专栏

Scala入门与进阶(一)- 初始Scala

13020
来自专栏程序员的知识天地

使用Python这么多年,竟然还有这些实用的功能和特点!

在使用Python多年以后,我偶然发现了一些我们过去不知道的功能和特性。一些可以说是非常有用,但却没有充分利用。考虑到这一点,我编辑了一些你应该了解的Pytho...

10640

扫码关注云+社区

领取腾讯云代金券