Рано или поздно вам нужно использовать абстрактные результаты других разработчиков, то есть вы полагаетесь на код других людей. Мне нравится полагаться на свободные (без зависимости) модули, но этого трудно достичь. Даже те красивые компоненты черного ящика, которые вы создаете, будут зависеть от чего -то более или менее. Это именно то, что делает инъекцию зависимости отличным. Способность эффективно управлять зависимостями в настоящее время абсолютно необходима. Эта статья суммирует мое исследование проблемы и некоторых решений.
1. Цель
Представьте, что у нас есть два модуля. Первый несет ответственность за службу запроса AJAX, а второй - маршрутизатор.
Кода -копия выглядит следующим образом:
var service = function () {
return {name: 'service'};
}
var router = function () {
return {name: 'router'};
}
У нас есть другая функция, которая должна использовать эти два модуля.
Кода -копия выглядит следующим образом:
var dosomething = function (другой) {
var s = service ();
var r = router ();
};
Чтобы сделать это более интересным, эта функция принимает аргумент. Конечно, мы можем полностью использовать приведенный выше код, но это, очевидно, недостаточно гибко. Что если мы хотим использовать Servicexml или ServiceJson, или что, если нам понадобятся некоторые тестовые модули. Мы не можем решить проблему, редактируя тело функционирования в одиночку. Во -первых, мы можем решить зависимость через параметры функции. Прямо сейчас:
Кода -копия выглядит следующим образом:
var dosomething = function (сервис, маршрутизатор, другой) {
var s = service ();
var r = router ();
};
Мы реализуем функциональность, которую мы хотим, передавая дополнительные параметры, однако это приносит новые проблемы. Представьте себе, если наш метод разбросана в нашем коде. Если нам нужно изменить условия зависимости, мы не можем изменить все файлы, вызывающие функцию.
Нам нужен инструмент, который может помочь нам сделать эти вещи. Это проблема, которую инъекция зависимости пытается решить. Давайте запишем некоторые цели, которые должно достичь наше решение для инъекции зависимости:
Мы должны иметь возможность зарегистрировать зависимости
1. Инъекция должна принять функцию и вернуть необходимую функцию
2. Мы не можем писать слишком много - нам нужно оптимизировать красивую грамматику
3. Инъекция должна поддерживать объем переносимой функции
4. Продолжительная функция должна иметь возможность принимать пользовательские параметры, а не только зависеть от описаний
5. Идеальный список, давайте достигнем его ниже.
3. TevallyJS/AMD Метод
Возможно, вы слышали о edrejs, который является хорошим выбором для решения инъекции зависимости.
Кода -копия выглядит следующим образом:
определить (['service', 'router'], function (service, router) {
// ...
});
Идея состоит в том, чтобы сначала описать необходимые зависимости, а затем написать вашу функцию. Порядок параметров здесь очень важен. Как упомянуто выше, давайте напишем модуль, называемый инжектором, который может принять тот же синтаксис.
Кода -копия выглядит следующим образом:
var dosomething = injector.resolve (['service', 'router'], function (служба, маршрутизатор, другой) {
weals (service (). name) .to.be ('service');
ожидайте (router (). name) .to.be ('router');
Ожидайте (другое) .to.be ('firth');
});
DOSOMETH («Другое»);
Прежде чем продолжить, я должен четко объяснить содержание функции DOSOMENTE. метод
Давайте начнем наш модуль инжектора, который является отличным шаблоном синглтона, поэтому он хорошо работает в разных частях нашей программы.
Кода -копия выглядит следующим образом:
var injector = {
зависимости: {},
Регистрация: функция (ключ, значение) {
this.Epectendenties [key] = value;
},
Resolve: Function (Deps, Func, Scope) {
}
}
Это очень простой объект, с двумя методами, один для хранения свойства. Что мы хотим сделать, так это проверить массив DEPS и найти ответы в переменной зависимости. Все, что осталось, - это вызвать метод .apply и передать параметры предыдущего метода фанка.
Кода -копия выглядит следующим образом:
Resolve: Function (Deps, Func, Scope) {
var args = [];
for (var i = 0; i <deps.length, d = deps [i]; i ++) {
if (this.dependencies [d]) {
args.push (this.ependencies [d]);
} еще {
бросить новую ошибку ('can/' t Resolve ' + d);
}
}
return function () {
func.apply (scope || {}, args.concat (array.prototype.slice.call (аргументы, 0)));
}
}
Область возможности необязательна, Array.prototype.slice.call (аргументы, 0) требуется для преобразования переменных аргументов в реальные массивы. Пока это неплохо. Наш тест прошел. Проблема с этой реализацией заключается в том, что нам нужно дважды написать необходимые части, и мы не можем путать их заказ. Дополнительные пользовательские параметры всегда за зависимостью.
4. Метод отражения
Согласно определению Википедии, отражение относится к способности программы проверять и изменять структуру и поведение объекта во время выполнения. Проще говоря, в контексте JavaScript это специфически относится к исходному коду объекта или функции, который читается и анализируется. Давайте завершим функцию DOSOMENTY, упомянутую в начале статьи. Если вы выводите dosomething.toString () в консоли. Вы получите следующую строку:
Кода -копия выглядит следующим образом:
"Функция (служба, маршрутизатор, другой) {
var s = service ();
var r = router ();
} "
Строка, возвращаемая этим методом, дает нам возможность пересекать параметры и, что более важно, получить их имена. Это на самом деле метод Angular для реализации инъекции зависимости. Я был немного ленивым и непосредственно перехватил регулярное выражение, которое получает параметры в угловом коде.
Кода -копия выглядит следующим образом:
/^function/s*[^/(]*/(/s*([^/)]*)/)/m
Мы можем изменить код разрешения таким образом:
Кода -копия выглядит следующим образом:
Resolve: function () {
var func, deps, scope, args = [], self = this;
func = аргументы [0];
deps = func.tostring (). match (/^function/s*[^/(]*/(/s*([^/)]*)/)/m) [1] .Replace (//g, '').расколоть(',');
Scope = аргументы [1] || {};
return function () {
var a = array.prototype.slice.call (аргументы, 0);
for (var i = 0; i <deps.length; i ++) {
var d = deps [i];
args.push (self. -sependencies [d] && d! = ''?
}
func.apply (scope || {}, args);
}
}
Результат нашего исполнения регулярных выражений заключается в следующем:
Кода -копия выглядит следующим образом:
[«Функция (сервис, маршрутизатор, другой)», «Сервис, маршрутизатор, другой»]
Кажется, что нам нужен только второй пункт. Как только мы очистим пробелы и разделяем строку, мы получим массив DEPS. Есть только одно большое изменение:
Кода -копия выглядит следующим образом:
var a = array.prototype.slice.call (аргументы, 0);
...
args.push (self. -sependencies [d] && d! = ''?
Мы пробираемся через массив зависимостей и стараемся получить его из объекта Argimes, если мы найдем недостающие элементы. К счастью, когда массив пуст, метод сдвига просто возвращает неопределенную вместо того, чтобы бросить ошибку (это благодаря идее Интернета). Новая версия инжектора может использоваться как следующее:
Кода -копия выглядит следующим образом:
var dosomething = injector.resolve (function (service, fore, маршрутизатор) {
weals (service (). name) .to.be ('service');
ожидайте (router (). name) .to.be ('router');
Ожидайте (другое) .to.be ('firth');
});
DOSOMETH («Другое»);
Нет необходимости переписать зависимости, и их порядок может быть нарушен. Это все еще работает, и мы успешно скопировали магию Angular.
Тем не менее, эта практика не идеальна, что является очень большой проблемой с инъекцией типа рефлекса. Сжатие разрушит нашу логику, потому что она изменяет имя параметра, и мы не сможем поддерживать правильные отношения отображения. Например, dosometing () может выглядеть так после сжатия:
Кода -копия выглядит следующим образом:
var dosomething = function (e, t, n) {var r = e (); var i = t ()}
Решение, предложенное угловой командой, выглядит как:
var dosomething = injector.resolve (['service', 'router', function (service, router) {
}]);
Это очень похоже на решение, которое мы начали. Я не смог найти лучшее решение, поэтому я решил объединить оба. Вот окончательная версия инжектора.
Кода -копия выглядит следующим образом:
var injector = {
зависимости: {},
Регистрация: функция (ключ, значение) {
this.Epectendenties [key] = value;
},
Resolve: function () {
var func, deps, scope, args = [], self = this;
if (typeof Arguments [0] === 'String') {
func = аргументы [1];
deps = аргументы [0] .replace ( / / g, '') .split (',');
Scope = аргументы [2] || {};
} еще {
func = аргументы [0];
deps = func.tostring (). match (/^function/s*[^/(]*/(/s*([^/)]*)/)/m) [1] .Replace (//g, '').расколоть(',');
Scope = аргументы [1] || {};
}
return function () {
var a = array.prototype.slice.call (аргументы, 0);
for (var i = 0; i <deps.length; i ++) {
var d = deps [i];
args.push (self. -sependencies [d] && d! = ''?
}
func.apply (scope || {}, args);
}
}
}
Решающие посетители принимают два или три параметра, если есть два параметра, на самом деле это то же самое, что и то, что было написано в предыдущей статье. Однако, если есть три параметра, он преобразует первый параметр и заполняет массив DEPS, вот тестовый пример:
Кода -копия выглядит следующим образом:
var dosomething = injector.resolve ('router ,, service', function (a, b, c) {
ожидайте (a (). name) .to.be ('router');
Ожидайте (b) .to.be ('firth');
ожидайте (c (). name) .to.be ('service');
});
DOSOMETH («Другое»);
Вы можете заметить, что после первого параметра есть две запятые - обратите внимание, что это не опечатка. Нулевое значение фактически представляет параметр «другой» (заполнитель). Это показывает, как мы контролируем порядок параметров.
5. Прямая инъекция объема
Иногда я использую третью переменную инъекции, которая включает в себя объем функции операции (другими словами, этот объект). Следовательно, эта переменная не требуется во многих случаях.
Кода -копия выглядит следующим образом:
var injector = {
зависимости: {},
Регистрация: функция (ключ, значение) {
this.Epectendenties [key] = value;
},
Resolve: Function (Deps, Func, Scope) {
var args = [];
Scope = Scope || {};
for (var i = 0; i <deps.length, d = deps [i]; i ++) {
if (this.dependencies [d]) {
Scope [d] = this.Epectendenties [d];
} еще {
бросить новую ошибку ('can/' t Resolve ' + d);
}
}
return function () {
func.apply (scope || {}, array.prototype.slice.call (аргументы, 0));
}
}
}
Все, что мы делаем, это на самом деле добавляем зависимости к объему. Преимущество этого заключается в том, что разработчикам больше не нужно писать параметры зависимости;
Кода -копия выглядит следующим образом:
var dosomething = injector.resolve (['service', 'router'], function (firth) {
ожидайте (this.service (). name) .to.be ('service');
ожидайте (this.router (). name) .to.be ('router');
Ожидайте (другое) .to.be ('firth');
});
DOSOMETH («Другое»);
6. Заключение
На самом деле, большинство из нас использовали инъекцию зависимости, но мы этого не понимаем. Даже если вы не знаете этот термин, вы, возможно, использовали его в своем коде миллион раз. Я надеюсь, что эта статья углубит ваше понимание этого.