dependency injection库 – 重命名注入的值

我想按名称注入lodash,如下所示:

let val = function(lodash){ // lodash will be injected, simply by using require('lodash'); }; 

但是说我想重命名导入,我想要做这样的事情:

 let val = function({lodash:_}){ }; 

要么

 let val = function(lodash as _){ }; 

有没有办法做到这一点与ES6 / ES7 / ES8或TypeScript?

请注意,这个DI框架比只需要('x')做更多的工作…它将尝试注入其他值首先,如果没有其他的存在,那么它将尝试要求的价值。

还要注意,这里的要求是,当你调用val.toString()时,“lodash”将被视为参数名称。 但是在函数体内的运行时会看到_而不是lodash。 这是因为为了注入lodash,我们调用fn.toString()来获取参数名称。

更新

这里有一个指向npm包di-proxy的链接(灵感来自于这个答案),代码覆盖率达到100%,支持memoization以提高性能,与Node.js >=6.0.0兼容。

老答案

我在解决对象解构和Proxy的时候,发现了一个很棒的解决scheme:

 /* MIT License */ /* Copyright 2017 Patrick Roberts */ // dependency injection utility function inject(callbackfn) { const handler = { get(target, name) { /* this is just a demo, swap these two lines for actual injection */ // return require(name); return { name }; } }; const proxy = new Proxy({}, handler); return (...args) => callbackfn.call(this, proxy, ...args); } // usage // wrap function declaration with inject() const val = inject(function ({ lodash: _, 'socket.io': sio, jquery: $, express, fs }, other, args) { // already have access to lodash, no need to even require() here console.log(_); console.log(sio); console.log($); console.log(express); console.log(fs); console.log(other, args); }); // execute wrapped function with automatic injection val('other', 'args'); 
 .as-console-wrapper { max-height: 100% !important; } 

JavaScript中没有支持这种映射的语法。 即使自定义函数签名parsing器被写入来为function({lodash:_}) ...等解构参数提供期望的行为,它也将失败,这是一个主要的缺陷。 处理这个最直接的方法是

 function foo(lodash){ const _ = lodash; ... } 

它显然不适用于像lodash.pick这样的无效variables名称。

DI食谱做这个的一个惯例是提供注释 。 所有描述的注释可以组合在一起。 它们在angular度DI中特别实施。 Angular注入器可以独立使用(包括Node)作为injection-js库。

注释属性

这样函数签名和依赖关系列表就不需要匹配。 这个配方可以在AngularJS中看到。

该属性包含一个DI令牌列表。 它们可以是将被加载require或其他东西的依赖项的名称。

 // may be more convenient when it's a string const ANNOTATION = Symbol(); ... foo[ANNOTATION] = ['lodash']; function foo(_) { ... } bar[ANNOTATION] = ['lodash']; function bar() { // doesn't need a param in signature const _ = arguments[0]; ... } 

而且DI是这样执行的

 const fnArgs = require('fn-args'); const annotation = foo[ANNOTATION] || fnArgs(foo); foo(...annotation.map(depName => require(depName)); 

这种注释configuration是为了方便使用函数定义,因为提供允许在函数签名之上放置注释。

数组注释

函数签名和依赖关系列表不必匹配。 这个配方也可以在AngularJS中看到。

当函数被表示为一个数组时,这意味着它是注释函数,其参数应被视为注释,最后一个函数本身。

 const foo = [ 'lodash', function foo(_) { ... } ]; ... const fn = foo[foo.length - 1]; const annotation = foo.slice(0, foo.length - 1); foo(...annotation.map(depName => require(depName)); 

TypeScripttypes注释

这个配方可以在Angular(2和更高版本)中看到,并依赖于TypeScripttypes。 types可以从构造函数签名中提取并用于DI。 使之成为可能的是Reflect元数据提议和TypeScript自己的emitDecoratorMetadatafunction 。

释放的构造函数types作为相应类的元数据存储,并可以通过Reflect API进行检索以解决依赖关系。 这是基于类的 DI,因为装饰器仅在类上受支持,所以对DI容器来说效果最好:

 import 'core-js/es7/reflect'; abstract class Dep {} function di(target) { /* can be noop to emit metadata */ } @di class Foo { constructor(dep: Dep) { ... } } ... const diContainer = { Dep: require('lodash') }; const annotations = Reflect.getMetadata('design:paramtypes', Foo); new (Foo.bind(Foo, ...annotations.map(dep => diContainer [dep]))(); 

这将产生可行的JS代码,但会创buildtypes问题,因为Lodash对象不是Dep令牌类的实例。 此方法主要对注入到类中的类相关性有效。

对于非课堂DI,需要回退到其他注释。

我已经做了一些可能适合你的事情,但你可以随时改变它,并使用一般的想法。

它是用ES6function编写的,但可以轻松删除它们。

 let di = function() { const argumentsLength = arguments.length; //you must call this func with at least a callback if (argumentsLength === 0) return; //this will be called with odd amount of variables, //pairs of key and assignment, and the callback //means: 1,3,5,7.... amount of args if (argumentsLength%2 === 0) throw "mismatch of args"; //here we will assing the variables to "this" for (key in arguments) { //skip the callback if(key===argumentsLength-1) continue; //skip the "key", it will be used in the next round if(key%2===0) continue; const keyToSet = arguments[key-1]; const valToSet = arguments[key]; this[keyToSet] = valToSet; } arguments[argumentsLength-1].apply(this); } di("name", {a:"IwillBeName"}, "whatever", "IwillBeWhatever", () => { console.log(whatever); console.log(name); }); 

在底线,你可以在这些参数中调用func“di”pass:

 di("_", lodash, callback); 

现在在你callback代码里面,你可以用“_”来引用“lodash”

给出答案,我仍然认为Angular 1.x(和RequireJS)所做的是最高性能的,尽pipe可能不是最简单的:

 let = createSomething('id', ['lodash', function(_){ }]);