조만간 다른 개발자의 추상 결과를 사용해야합니다. 즉, 다른 사람의 코드에 의존합니다. 나는 무료 (종속성이없는) 모듈에 의존하는 것을 좋아하지만 달성하기가 어렵습니다. 당신이 만든 아름다운 블랙 박스 구성 요소조차도 다소 의존 할 것입니다. 이것이 바로 의존성 주입을 훌륭하게 만드는 이유입니다. 의존성을 효과적으로 관리하는 능력은 이제 절대적으로 필요합니다. 이 기사는 문제에 대한 탐구와 일부 솔루션을 요약합니다.
1. 목표
두 개의 모듈이 있다고 상상해보십시오. 첫 번째는 Ajax 요청 서비스를 담당하고 두 번째는 라우터입니다.
코드 사본은 다음과 같습니다.
var service = function () {
반환 {이름 : 'service'};
}
var router = function () {
반환 {이름 : '라우터'};
}
이 두 모듈을 사용해야하는 또 다른 기능이 있습니다.
코드 사본은 다음과 같습니다.
var dosomething = function (기타) {
var s = service ();
var r = 라우터 ();
};
더 흥미롭게 만들기 위해이 기능은 인수를 받아들입니다. 물론 위의 코드를 완전히 사용할 수 있지만 이것은 분명히 유연하지 않습니다. ServiceXML 또는 ServiceJSON을 사용하려면 어떻게 해야하는지 또는 테스트 모듈이 필요한 경우 어떻게해야합니까? 기능 본문 만 편집하여 문제를 해결할 수 없습니다. 먼저 함수의 매개 변수를 통해 종속성을 해결할 수 있습니다. 지금 바로:
코드 사본은 다음과 같습니다.
var dosomething = function (서비스, 라우터, 기타) {
var s = service ();
var r = 라우터 ();
};
추가 매개 변수를 전달하여 원하는 기능을 구현하지만 새로운 문제가 발생합니다. 코드에 우리의 복장 방법이 흩어져 있다고 상상해보십시오. 종속성 조건을 변경 해야하는 경우 기능을 호출하는 모든 파일을 변경하는 것은 불가능합니다.
우리는 이러한 일을 끝내는 데 도움이되는 도구가 필요합니다. 이것은 의존성 주입이 해결하려고하는 문제입니다. 의존성 주입 솔루션을 달성 해야하는 목표 중 일부를 적어 봅시다.
우리는 종속성을 등록 할 수 있어야합니다
1. 주입은 함수를 수락하고 필요한 함수를 반환해야합니다.
2. 우리는 너무 많이 쓸 수 없습니다 - 우리는 아름다운 문법을 간소화해야합니다.
3. 주입은 전송 된 기능의 범위를 유지해야합니다.
4. 전달 된 함수는 설명에 의존하는 것이 아니라 사용자 정의 매개 변수를 허용 할 수 있어야합니다.
5. 완벽한 목록, 아래에서 달성합시다.
3. 요구 사항/amd 메소드
의존성 주입을 해결하기에 좋은 선택입니다.
코드 사본은 다음과 같습니다.
정의 ([ 'service', 'router'], function (service, router) {
// ...
});
아이디어는 필요한 종속성을 먼저 설명한 다음 기능을 작성하는 것입니다. 여기서 매개 변수 순서는 매우 중요합니다. 위에서 언급했듯이 동일한 구문을 수용 할 수있는 인젝터라는 모듈을 작성해 봅시다.
코드 사본은 다음과 같습니다.
var dosomething = injector.resolve ([ 'service', 'router'], 함수 (서비스, 라우터, 기타) {
expling (service (). name) .to.be ( 'service');
expling (router (). name) .to.be ( 'router');
기대 (기타) .to.be ( '기타');
});
복용량 ( "기타");
계속하기 전에 Dosomething 기능 본문의 내용을 명확하게 설명해야합니다. 방법.
훌륭한 싱글 톤 패턴 인 인젝터 모듈을 시작하겠습니다. 프로그램의 다른 부분에서 잘 작동합니다.
코드 사본은 다음과 같습니다.
var injector = {
종속성 : {},
레지스터 : 함수 (키, 값) {
이.
},
해결 : 함수 (deps, func, scope) {
}
}
이것은 매우 간단한 객체이며, 하나는 부동산을 저장하기위한 두 가지 방법입니다. 우리가하고 싶은 것은 DEPS 어레이를 확인하고 종속성 변수의 답변을 검색하는 것입니다. 남은 것은 .apply 메소드를 호출하고 이전 func 메소드의 매개 변수를 전달하는 것입니다.
코드 사본은 다음과 같습니다.
해결 : 함수 (deps, func, scope) {
var args = [];
for (var i = 0; i <deps.length, d = deps [i]; i ++) {
if (this.dependencies [d]) {
args.push (this.dependencies [d]);
} 또 다른 {
새 오류를 던지십시오 ( 'can/'t resolve ' + d);
}
}
return function () {
func.Apply (스코프 || {}, args.concat (array.prototype.slice.call (arguments, 0)));
}
}
스코프는 옵션, array.prototype.slice.call (Arguments, 0)을 실제 배열로 변환하려면 필요합니다. 지금까지는 나쁘지 않습니다. 우리의 시험이 통과되었습니다. 이 구현의 문제점은 필요한 부품을 두 번 작성해야하며 주문을 혼동 할 수 없다는 것입니다. 추가 사용자 정의 매개 변수는 항상 종속성 뒤에 있습니다.
4. 반사 방법
Wikipedia의 정의에 따르면, 반사는 런타임에 객체의 구조와 동작을 확인하고 수정하는 프로그램의 능력을 나타냅니다. 간단히 말해서, JavaScript의 맥락에서, 이것은 특히 읽고 분석되는 객체 또는 함수의 소스 코드를 나타냅니다. 기사의 시작 부분에서 언급 된 복장 기능을 완료합시다. 콘솔에서 dosomething.toString ()을 출력하는 경우. 다음 문자열을 얻을 수 있습니다.
코드 사본은 다음과 같습니다.
"기능 (서비스, 라우터, 기타) {
var s = service ();
var r = 라우터 ();
} "
이 방법으로 반환 된 문자열은 우리에게 매개 변수를 가로 지르는 기능을 제공하며, 더 중요한 것은 이름을 얻는 것입니다. 이것은 실제로 종속성 주입을 구현하는 Angular의 방법입니다. 나는 약간 게으르고 각도 코드에서 매개 변수를 얻는 정규 표현을 직접 차단했습니다.
코드 사본은 다음과 같습니다.
/^function/s*[^/(]*/(/s*([^/)]*)/)/m
다음과 같이 Resolve 코드를 수정할 수 있습니다.
코드 사본은 다음과 같습니다.
Resolve : function () {
var func, deps, scope, args = [], self = this;
func = 인수 [0];
deps = func.tostring (). match (/^function/s*[^/(]*/(/s*([^/)]]*)/)/m) '').나뉘다(',');
범위 = 인수 [1] ||;
return function () {
var a = array.prototype.slice.call (Arguments, 0);
for (var i = 0; i <deps.length; i ++) {
var d = deps [i];
args.push (self.dependencies [d] && d! = ''? self.dependencies [d] : a.shift ());
}
func.apply (스코프 || {}, args);
}
}
정규 표현식 실행 결과는 다음과 같습니다.
코드 사본은 다음과 같습니다.
[ "기능 (서비스, 라우터, 기타)", "서비스, 라우터, 기타"]]]]
두 번째 항목 만 있으면됩니다. 공백을 비우고 문자열을 분할하면 DEP 배열이됩니다. 큰 변화는 하나뿐입니다.
코드 사본은 다음과 같습니다.
var a = array.prototype.slice.call (Arguments, 0);
...
args.push (self.dependencies [d] && d! = ''? self.dependencies [d] : a.shift ());
우리는 종속성 배열을 루프하고 누락 된 항목을 찾으면 인수 객체에서 가져 오려고 노력합니다. 고맙게도 배열이 비어 있으면 오류를 던지는 대신 Shift 메소드가 정의되지 않은 것을 반환합니다 (웹의 아이디어 덕분). 새 버전의 인젝터는 다음과 같이 사용할 수 있습니다.
코드 사본은 다음과 같습니다.
var dosomething = injector.resolve (함수 (서비스, 기타, 라우터) {
expling (service (). name) .to.be ( 'service');
expling (router (). name) .to.be ( 'router');
기대 (기타) .to.be ( '기타');
});
복용량 ( "기타");
종속성을 다시 작성할 필요가 없으며 해당 순서가 중단 될 수 있습니다. 그것은 여전히 작동하며 우리는 Angular의 마술을 성공적으로 복사했습니다.
그러나이 관행은 완벽하지 않으며 이는 반사 유형 주입의 매우 큰 문제입니다. 압축은 매개 변수의 이름을 변경하고 올바른 매핑 관계를 유지할 수 없기 때문에 논리를 파괴합니다. 예를 들어, DoSometing ()는 압축 후 다음과 같이 보일 수 있습니다.
코드 사본은 다음과 같습니다.
var dosomething = function (e, t, n) {var r = e (); var i = t ()}
Angular 팀이 제안한 솔루션은 다음과 같습니다.
var dosomething = injector.resolve ([ 'service', 'router', function (service, router) {
}]);
이것은 우리가 시작한 솔루션과 매우 흡사합니다. 더 나은 솔루션을 찾을 수 없었기 때문에 둘 다 결합하기로 결정했습니다. 다음은 인젝터의 최종 버전입니다.
코드 사본은 다음과 같습니다.
var injector = {
종속성 : {},
레지스터 : 함수 (키, 값) {
이.
},
Resolve : function () {
var func, deps, scope, args = [], self = this;
if (typeof arguments [0] === 'string') {
func = 인수 [1];
deps = arguments [0] .replace ( / / g, '') .split ( ',');
범위 = 인수 [2] ||;
} 또 다른 {
func = 인수 [0];
deps = func.tostring (). match (/^function/s*[^/(]*/(/s*([^/)]]*)/)/m) '').나뉘다(',');
범위 = 인수 [1] ||;
}
return function () {
var a = array.prototype.slice.call (Arguments, 0);
for (var i = 0; i <deps.length; i ++) {
var d = deps [i];
args.push (self.dependencies [d] && d! = ''? self.dependencies [d] : a.shift ());
}
func.apply (스코프 || {}, args);
}
}
}
방문자는 2 ~ 3 개의 매개 변수를 허용합니다. 두 개의 매개 변수가있는 경우 실제로 이전 기사에 작성된 것과 동일합니다. 그러나 세 가지 매개 변수가있는 경우 첫 번째 매개 변수를 변환하고 DEPS 배열을 채우십시오. 다음은 테스트 예입니다.
코드 사본은 다음과 같습니다.
var dosomething = injector.resolve ( 'router ,, service', function (a, b, c) {
기대 (a (). name) .to.be ( 'router');
기대 (b) .to.be ( '기타');
기대 (c (). name) .to.be ( 'service');
});
복용량 ( "기타");
첫 번째 매개 변수 다음에 두 개의 쉼표가 있음을 알 수 있습니다. 이것은 오타가 아닙니다. NULL 값은 실제로 "기타"매개 변수 (자리 표시 자)를 나타냅니다. 이것은 우리가 매개 변수 순서를 어떻게 제어하는지 보여줍니다.
5. 범위의 직접 주입
때로는 작업 함수의 범위 (즉,이 개체)가 포함되는 세 번째 주입 변수를 사용합니다. 따라서이 변수는 많은 경우에 필요하지 않습니다.
코드 사본은 다음과 같습니다.
var injector = {
종속성 : {},
레지스터 : 함수 (키, 값) {
이.
},
해결 : 함수 (deps, func, scope) {
var args = [];
스코프 = 스코프 ||;
for (var i = 0; i <deps.length, d = deps [i]; i ++) {
if (this.dependencies [d]) {
범위 [d] = this.dependencies [d];
} 또 다른 {
새 오류를 던지십시오 ( 'can/'t resolve ' + d);
}
}
return function () {
func.apply (스코프 || {}, array.prototype.slice.call (arguments, 0));
}
}
}
우리가하는 일은 실제로 범위에 종속성을 추가하는 것입니다. 이것의 장점은 더 이상 종속성 매개 변수를 작성할 필요가 없다는 것입니다.
코드 사본은 다음과 같습니다.
var dosomething = injector.resolve ([ 'service', 'router'], function (기타) {
expling (this.service (). name) .to.be ( 'service');
기대 (this.router (). name) .to.be ( 'router');
기대 (기타) .to.be ( '기타');
});
복용량 ( "기타");
6. 결론
사실, 우리 대부분은 의존성 주입을 사용했지만 우리는 그것을 깨닫지 못합니다. 이 용어를 모르더라도 코드에서 백만 번 사용했을 수 있습니다. 이 기사가 당신의 이해가 심화되기를 바랍니다.