
大家好!今天,我将尝试在纯JavaScript中试验依赖项注入。那些不知道游戏是什么以及如何烹饪的人,我邀请您熟悉一下。好吧,那些知道的人将有机会发表重要而有用的评论。所以,我们开车...
依赖注入
DI是被设计以减少一种架构模式的的连通性的系统实体-组件,模块,类。少连接(不要与混淆连通),越容易改变这些非常的实体,添加新的和考验他们。通常,加号是加号,但让我们看看是否确实如此。
没有DI:
class Engine {...};
class ElectroEngine {...};
class Transmission {...};
class Chassis {...};
class TestChassis {...};
class Car {
constructor() {
this.engine = new Engine();
this.transmission = new Transmission();
this.chassis = new Chassis();
}
}
class ElectroCar {
constructor() {
this.engine = new ElectroEngine();
this.transmission = new Transmission();
this.chassis = new Chassis();
}
}
class TestCar {
constructor() {
this.engine = new Engine();
this.transmission = new Transmission();
this.chassis = new TestChassis ();
}
}
const car = new Car();
const electroCar = new ElectroCar();
const testCar = new TestCar();
使用DI:
class Engine{...};
class ElectroEngine {...};
class TestEngine {...};
class Transmission {...};
class TestTransmission {...};
class Chassis {...};
class SportChassis {...};
class TestChassis {...};
class Car {
constructor(engine, transmission, chassis) {
this.engine = engine;
this.transmission = transmission;
this.chassis = chassis;
}
}
const petrolCar = new Car(new Engine(), new Transmission(), new Chassis());
const sportCar = new Car(new Engine(), new Transmission(), new SportChassis());
const electroCar = new Car(new ElectroEngine(), new Transmission(), new Chassis());
const testCar = new Car(new TestEngine(), new TestTransmission(), new TestChassis());
在没有DI的第一个示例中,我们的Car类与特定的类相关联,因此,例如要创建electroCar,您必须创建一个单独的ElectroCar类。在该实施例中,存在“硬”实现依赖性,即 对特定类实例的依赖。
— DI, Car. . ! — . , "" — .
DI . "" — , , . , ? , . :
class Engine{
constructor(candles, pistons, oil) {….}
};
class Chassis{
constructor(doors, hood, trunk) {….}
};
const petrolCar = new Car(
new Engine(new Candles(), new Pistons(), new Oil() ),
new Transmission(…..),
new Chassis(new Doors, new Hood(), new Trunk())
);
. , . , , .
Inversion of Control
"" DI- — Inversion of Control (IoC). , — , . DI, IoC , . - , . :
class Engine{...};
class Transmission{...};
class Chassis{…}
class Car {
constructor(engine: Engine, transmission: Transmission, chassis: Chassis) {}
}
const car = new Car();
car.engine instanceof Engine;
— new Car(). — , a .
DI-in-JS
JS. , DI . "".
, , , , . . .
constructor(engine = Engine, transmission = Transmission, chassis = Chassis)
:
constructor(engine: Engine, transmission: Transmission, chassis: Chassis)
, . IoC «» , . , ?
, Reflection. , — .
, JS:
function reflectionMetaInfo(a) { console.log(a); }
reflectionMetaInfo.name ;
reflectionMetaInfo.length ;
reflectionMetaInfo.toString();
arguments;
, toString(). . , , . () , . , .
const constructorSignature = classFunc
.toString()
.replace(/\s|['"]/g, '')
.replace(/.*(constructor\((?:\w+=\w+?,?)+\)).*/g, '$1')
.match(/\((.*)\)/)[1]
.split(',')
.map(item => item.split('='));
constructorSignature
, , . , , . . IoC — .
:
function Injectable(classFunc, options) {
const
depsRegistry = Injectable.depsRegistry || (Injectable.depsRegistry = {}),
className = classFunc.name,
factories = options && options.factories;
if (factories) {
Object.keys(factories).forEach(factoryName => {
depsRegistry[factoryName] = factories[factoryName];
})
}
const depDescriptions = classFunc.toString()
.replace(/\s/g, '')
.match(/constructor\((.*)[^(]\){/)[1]
.replace(/"|'/g, '')
.split(',')
.map(item => item.split('='));
const injectableClassFunc = function(...args) {
const instance = new classFunc(...args);
depDescriptions.forEach(depDescription => {
const
depFieldName = depDescription[0],
depDesc = depDescription[1];
if (instance[depFieldName]) return;
try {
instance[depFieldName] = new depsRegistry[depDesc]();
} catch (err) {
instance[depFieldName] = depDesc;
}
});
return instance;
}
return depsRegistry[classFunc.name] = injectableClassFunc;
}
class CustomComponent {
constructor(name = "Custom Component") {
this.name = name;
}
sayName() {
alert(this.name);
}
}
const Button = Injectable(
class Button extends CustomComponent {
constructor(name = 'Button') {
super(name);
}
}
)
const Popup = Injectable(
class Popup extends CustomComponent {
constructor(
confirmButton = 'confirmButtonFactory',
closeButton = Button,
name = 'NoticePopup'
) {
super(name);
}
},
{
factories: {
confirmButtonFactory: function() { return new Button('Confirm Button') }
}
}
);
const Panel = Injectable(
class Panel extends CustomComponent {
constructor(
closeButton = 'closeButtonFactory',
popup = Popup,
name = 'Head Panel'
) {
super(name);
}
},
{
factories: {
closeButtonFactory: function() { return new Button('Close Button') }
}
}
);
const customPanel = new Panel();
, , . , . — Injectable, IoC. :
- ;
- ;
- ;
- ;
- ;
, try-catch .
:
- . . , , , - .
- . , , . - option.factories, .
.
:
function inject(context, ...deps) {
const
depsRegistry = inject.depsRegistry || (inject.depsRegistry = {}),
className = context.constructor.name;
let depsNames = depsRegistry[className];
if (!depsNames) {
depsNames
= depsRegistry[className]
= context.constructor
.toString()
.replace(/\s|['"]/g, '')
.replace(/.*(inject\((?:\w+,?)+\)).*/g, '$1')
.replace(/inject\((.*)\)/, '$1')
.split(',');
depsNames.shift();
}
deps.forEach((dep, index) => {
const depName = depsNames[index];
try {
context[depName] = new dep();
} catch (err) {
context[depName] = dep;
}
});
return context;
}
class Component {
constructor(name = 'Component') {
inject(this, name);
}
showName() {
alert(this.name);
}
}
class Button extends Component {
constructor(name = 'Component') {
super();
inject(this, name);
}
disable() {
alert(`button ${this.name} is disabled`);
}
enable() {
alert(`button ${this.name} is enabled`);
}
}
class PopupComponent extends Component {
show() {
alert(`show ${this.name} popup`);
}
hide() {
alert(`hide ${this.name} popup`);
}
}
class TopPopup extends PopupComponent {
constructor(
popupButton = Button,
name = 'Top Popup'
) {
super();
inject(this, popupButton, name);
this.popupButton.name = 'TopPopup Button';
}
}
class BottomPopup extends PopupComponent {
constructor(
popupButton = function() { return new Button('BottomPopup Button') },
name = 'Bottom Popup'
) {
super();
inject(this, popupButton, name);
}
}
class Panel extends Component {
constructor(
name = 'Panel',
popup1 = TopPopup,
popup2 = BottomPopup,
buttonClose = function() { return new Button('Close Button') }
) {
super();
inject(this, name, popup1, popup2, buttonClose);
}
}
const panel = new Panel('Panel 1');
. . . , inject, .
inject :
- (this)
- — context.constructor.
- .
- , — inject.depsRegistry
- — context
— . — Inject , . , .
:
class Injectable {
constructor(...dependensies) {
const
depsRegistry = Injectable.depsRegistry || (Injectable.depsRegistry = {}),
className = this.constructor.name;
let depNames = depsRegistry[className];
if (!depNames) {
depNames = this.constructor
.toString()
.replace(/\s|['"]/g, '')
.replace(/.*(super\((?:\w+,?)+\)).*/g, '$1')
.replace(/super\((.*)\)/, '$1')
.split(',');
}
dependensies.forEach((dependense, index) => {
const depName = depNames[index];
try {
this[depName] = new dependense();
} catch (err) {
this[depName] = dependense;
}
})
}
}
class Component extends Injectable {
showName() {
alert(this.name);
}
}
class Button extends Component {
constructor(name = 'button') {
super(name);
}
disable() {
alert(`button ${this.name} is disabled`);
}
enable() {
alert(`button ${this.name} is enabled`);
}
}
class PopupComponent extends Component {
show() {
alert(`show ${this.name} popup`);
}
hide() {
alert(`hide ${this.name} popup`);
}
}
class TopPopup extends PopupComponent {
constructor(
popupButton = Button,
name = 'Top Popup'
) {
super(popupButton, name);
this.popupButton.name = 'TopPopup Button';
}
}
class BottomPopup extends PopupComponent {
constructor(
popupButton = function() { return new Button('BottomPopup Button') },
name = 'Bottom Popup'
) {
super(popupButton, name);
}
}
class Panel extends Component {
constructor(
name = 'Panel',
popup1 = TopPopup,
popup2 = BottomPopup,
buttonClose = function() { return new Button('Close Button') }
) {
super(name, popup1, popup2, buttonClose);
}
}
const panel = new Panel('Panel 1');
Injectable. super , .
:

我认为,最后一个-第3个选项最适合DI和IoC定义,因此对客户端代码而言,实现机制是最隐蔽的。
好,仅此而已。我希望这是有趣和有益的。回见了各位!