Подтвердить что ты не робот

Инъекция зависимостей JavaScript

Я новичок в JavaScript. Интересно, как инъекция зависимостей внедряется в JavaScript? Я искал в Интернете, но ничего не мог найти.

4b9b3361

Ответ 1

var Injector = {
   dependencies: {},
   add : function(qualifier, obj){
      this.dependencies[qualifier] = obj; 
   },
   get : function(func){
      var obj = new func;
      var dependencies = this.resolveDependencies(func);
      func.apply(obj, dependencies);
      return obj;
   },
   resolveDependencies : function(func) {
      var args = this.getArguments(func);
      var dependencies = [];
      for ( var i = 0; i < args.length; i++) {
         dependencies.push(this.dependencies[args[i]]);
      }
      return dependencies;
   },
   getArguments : function(func) {
      //This regex is from require.js
      var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
      var args = func.toString().match(FN_ARGS)[1].split(',');
      return args;
   }
};

В первую очередь нам нужна конфигурация для обеспечения необходимых зависимостей с квалификаторами. Для этого мы определяем набор зависимостей как зависимости в классе Injector. Мы используем набор зависимостей как наш контейнер, который позаботится о наших объектных экземплярах, сопоставленных с квалификаторами. Чтобы добавить новый экземпляр с квалификатором к набору зависимостей, мы определяем метод добавления. После этого мы определяем метод get для извлечения нашего экземпляра. В этом методе мы сначала находим массив аргументов, а затем сопоставляем эти аргументы зависимостям. После этого мы просто строим объект с нашими зависимостями и возвращаем его. Дополнительную информацию и примеры см. В в моем блоге.

Ответ 2

Вы можете использовать AngularJS в качестве примера. Хорошо ли это, вы должны решить для себя. Я написал неделю назад статью о деинфицировании инъекции зависимостей в AngularJS. Здесь вы можете прочитать код из статьи:

// The following simplified code is partly taken from the AngularJS source code:
// https://github.com/angular/angular.js/blob/master/src/auto/injector.js#L63

function inject(fn, variablesToInject) {
    var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
    var FN_ARG_SPLIT = /,/;
    var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
    var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;

    if (typeof fn === 'function' && fn.length) {
        var fnText = fn.toString(); // getting the source code of the function
        fnText = fnText.replace(STRIP_COMMENTS, ''); // stripping comments like function(/*string*/ a) {}

        var matches = fnText.match(FN_ARGS); // finding arguments
        var argNames = matches[1].split(FN_ARG_SPLIT); // finding each argument name

        var newArgs = [];
        for (var i = 0, l = argNames.length; i < l; i++) {
            var argName = argNames[i].trim();

            if (!variablesToInject.hasOwnProperty(argName)) {
                // the argument cannot be injected
                throw new Error("Unknown argument: '" + argName + "'. This cannot be injected.");
            }

            newArgs.push(variablesToInject[argName]);
        }

        fn.apply(window, newArgs);
    }
}

function sum(x, y) {
    console.log(x + y);
}

inject(sum, {
    x: 5,
    y: 6
}); // should print 11

inject(sum, {
    x: 13,
    y: 45
}); // should print 58

inject(sum, {
    x: 33,
    z: 1 // we are missing 'y'
}); // should throw an error: Unknown argument: 'y'. This cannot be injected.

Ответ 3

Для меня yusufaytas ответ был именно тем, что мне нужно! Единственными недостающими функциями были:

  • Получение зависимости от пользовательских параметров.
  • Регистрация зависимостей с использованием обратных вызовов.

Я хотел иметь возможность сделать что-то вроде этого:

Injector.register('someDependency', function () {
        return new ConcreteDependency();
});

function SomeViewModel(userId, someDependency) {
    this.userId = userId;
    this.someDependency = someDependency;
}

var myVm = Injector.get(SomeViewModel, { "userId": "1234" });

Итак, у меня появился следующий код:

var Injector = {

    factories = {},        
    singletons = {},

    register: function (key, factory) {
        this.factories[key] = factory;
    },

    registerSingle: function (key, instance) {
        this.singletons[key] = instance;
    },

    get: function (CTor, params) {            

        var dependencies = this.resolveDependencies(CTor, params);

        // a workaround to allow calling a constructor through .apply
        // see https://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
        function MiddlemanCTor() {
            CTor.apply(this, dependencies);
        }

        MiddlemanCTor.prototype = CTor.prototype;

        return new MiddlemanCTor();
    },

    resolveDependencies: function(CTor, params) {
        params = params || {};
        var args = this.getArguments(CTor);

        var dependencies = [];
        for (var i = 0; i < args.length; i++) {
            var paramName = args[i];
            var factory = this.factories[paramName];

            // resolve dependency using:
            // 1. parameters supplied by caller
            // 2. registered factories
            // 3. registered singletons
            var dependency = params[paramName] ||
                (typeof factory === "function" ? factory() : undefined) ||
                this.singletons[paramName];

            dependencies.push(dependency);
        }
        return dependencies;
    }

    getArguments: func(func) {
        // Regex from require.js
        var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
        var args = func.toString().match(FN_ARGS)[1].split(',').map(function (str) {
            return str.trim();
        });
        return args;
    }
};

Ответ 4

Я бы сказал, что DI - это готовая функция JS/ES2015.:-) Конечно, это не полнофункциональные контейнеры IOC, но выглядит полезным, не так ли? Посмотрите пример ниже!

const one = () => 1;
const two = ({one}) => one + one;
const three = ({one, two}) => one + two;

// IOC container
const decimalNumbers = {
  get one() { return one(this); },
  get two() { return two(this); },
  get three() { return three(this); }
};

const binaryTwo = ({one}) => one + 9;

// child IOC container
const binaryNumbers = Object.create(decimalNumbers, {
  two: { get() { return binaryTwo(this); } }
});

console.log(`${decimalNumbers.three} is ${binaryNumbers.three} in binary`);

Ответ 5

Возьмите добычу в Flyspeck: https://gist.github.com/elfet/11349215

var c = new Flyspeck();

c.set('name', 'GistHub');

c.set('config', {
    server: 'https://gist.github.com'
});

c.set('user', function (c) {
    return new User(c.get('name'));
});

c.extend('user', function (user, c) {
    return new ProxyUser(user);
});

c.set('app', function (c) {
    return new Application(c.get('config'), c.get('user'));
});

var app = c.get('app');

Ответ 6

candiJS - это легкая неявная инъекция зависимости и библиотека создания объектов. Посмотрите

Пример:

candi.provider.singleton('ajax', function() {
    return {
        get: function() { /* some code */ },
        put: function() { /* some code */ }
    };
});

candi.provider.singleton('carService', function(ajax) {
    return {
        getSpecs: function(manufacturer, year, model, trim) {
            return ajax.get();
        }
    };
});

var Car = candi.provider.instance('Car', function(carService, year, manufacturer, model, trim) {
    this.year = year;
    this.manufacturer = manufacturer;
    this.model = model;
    this.trim = trim;
    this.specs = carService.getSpecs(manufacturer, year, model, trim);
});

var car = new Car(2009, 'honda', 'accord', 'lx');

Ответ 8

bubble-di - это легкий контейнер DI для Javascript и Typescript.

Он позволяет регистрировать методы factory (обратные вызовы) или экземпляры. Ниже приведен простой пример (несколько примеров).

npm install --save bubble-di

var {DiContainer} = require("bubble-di");
// import { DiContainer } from "bubble-di";

DiContainer.setContainer(new DiContainer());

class Bar { sayBar(){ console.log("bar"); } }
class Baz { sayBaz(){ console.log("baz"); } }
class Foo { 
    constructor (bar, baz)
    {
        bar.sayBar();
        baz.sayBaz();
        // ...
    }
};

DiContainer.getContainer().registerInstance("bar", new Bar());
DiContainer.getContainer().registerInstance("baz", new Baz());
DiContainer.getContainer().register("foo", {
    dependencies: ["bar", "baz"],
    factoryMethod: (bar, baz) => new Foo(bar, baz) },
);
const foo = DiContainer.getContainer().resolve("foo"); // will print "bar" and "baz".

Ответ 9

Я новичок в JavaScript. Интересно, как инъекция зависимостей внедряется в JavaScript? Я искал в Интернете, но ничего не мог найти.

Чтобы быть полностью честным после работы с JavaScript (в основном на стороне сервера) и всей экосистемы в течение нескольких лет, я чувствую, что инъекция зависимостей (не говоря уже о контейнерах) на самом деле не превратила ее в обычный JS программатор. Вероятно, причина в том, что там не так много информации об этом (тем лучше, хотя).

В отличие от языка, такого как Java, вы не можете полагаться на статические типы в JavaScript. Этот факт сам по себе исключает традиционный способ декларирования зависимостей через интерфейсы. Вы можете, конечно, добавить типы в JS (см. Flow), но они будут устранены до того, как код будет выполнен. То же самое относится к TypeScript, но я считаю, что есть способ сохранить типы как неработающие метаданные. Кроме того, JavaScript не поддерживает аннотации (хотя для него есть ).

Люди сталкиваются с ограничениями по-разному. Некоторые контейнеры анализируют определение функции/класса (как в случае вызова .toString() в переданной функции/классе и анализируют результирующую строку), и ищут зависимости на основе имен, некоторые требуют, чтобы функции/классы предоставляли свойство/статический метод для получить список зависимостей.

Я работал над контейнером Ashley, который просто запрашивает зависимости как часть процесса привязки. Никакой дополнительной проверки не требуется.

container.instance('Client', Client, ['DependencyA', 'DependencyB']);
container.instance('DependencyA', DependencyA, ['DependencyC']);
container.instance('DependencyB', DependencyB, ['DependencyC']);
container.instance('DependencyC', DependencyC, [], {
  scope: 'Prototype', // Defaults to Singleton
  initialize: true,
  deinitialize: true
});

const client = await container.resolve('Client');

Дополнительные примеры на GitHub.

Ответ 10

Даже если это старый вопрос, я чувствую желание.;)

//dependency injection
class Thing1 {
    constructor(aThing){
        this.otherThing = aThing;
    }
}
class Thing2 {}

const thing = new Thing1(new Thing2())

//dependency inversion
class Thing1 {
    constructor({
        read = null
    } = {}){
        if(typeof read !== 'function'){
            //establish a simple contract
            throw new TypeError(read + ' is not a function.');
        }
        this._read = read;
        //Somewhere an instance of Thing1()
        //will call this._read()
    }
}

class Thing2 {
    read(){
       //read something
    }
}

const thing2 = new Thing2();
const thing1 = new Thing1({
    read(){
        //Here is the equivalent to the so called "interface"
        return thing2.read();
    }
});

Ответ 11

Я закодировал свою собственную платформу инъекций зависимостей JavaScript под названием Di-Ninja https://github.com/di-ninja/di-ninja

Это полнофункциональный и в настоящее время единственный в javascript, как я знаю, который реализует шаблон структуры композиции-корня, помогая вам сохранить все вещи развязанными и подключить компоненты приложений и конфигурацию в одном уникальном корневом месте. http://blog.ploeh.dk/2011/07/28/CompositionRoot/

Он хорошо работает с NodeJS и Webpack

Любая обратная связь будет оценена