AngularJS-2.의존 주입
21062 단어 AngularJS
function createInjector(modulesToLoad, strictDi) {
strictDi = (strictDi === true);
var INSTANTIATING = {},
providerSuffix = 'Provider',
path = [],
loadedModules = new HashMap([], true),
providerCache = {
// ... ...
},
providerInjector = (providerCache.$injector =
createInternalInjector(providerCache, function(serviceName, caller) {
// ... ...
})),
instanceCache = {},
instanceInjector = (instanceCache.$injector =
createInternalInjector(instanceCache, function(serviceName, caller) {
// ... ...
}));
forEach(loadModules(modulesToLoad), function(fn) {
if (fn) instanceInjector.invoke(fn);
});
return instanceInjector;
function supportObject(delegate) {}
function provider(name, provider_) {}
function enforceReturnValue(name, factory) {}
function factory(name, factoryFn, enforce) {}
function service(name, constructor) {}
function value(name, val) {}
function constant(name, value) {}
function decorator(serviceName, decorFn) {}
function loadModules(modulesToLoad) {}
function createInternalInjector(cache, factory) {
function getService(serviceName, caller) {}
function invoke(fn, self, locals, serviceName) {}
function instantiate(Type, locals, serviceName) {}
return {
// ... ...
};
}
}
이 함수는 비교적 길고 논리가 복잡하기 때문에 여기서 단락을 나누어 분석한다.
1.createInjector
strictDi = (strictDi === true);
var INSTANTIATING = {},
providerSuffix = 'Provider',
path = [],
loadedModules = new HashMap([], true),
providerCache = {
$provide: {
provider: supportObject(provider),
factory: supportObject(factory),
service: supportObject(service),
value: supportObject(value),
constant: supportObject(constant),
decorator: decorator
}
},
providerInjector = (providerCache.$injector =
createInternalInjector(providerCache, function(serviceName, caller) {
if (angular.isString(caller)) {
path.push(caller);
}
throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' ));
})),
instanceCache = {},
instanceInjector = (instanceCache.$injector =
createInternalInjector(instanceCache, function(serviceName, caller) {
var provider = providerInjector.get(serviceName + providerSuffix, caller);
return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
}));
forEach(loadModules(modulesToLoad), function(fn) {
if (fn) instanceInjector.invoke(fn);
});
return instanceInjector;
보시다시피 일부 변수의 정의가forEach에서loadModules를 실행하고 모듈을 순서대로 불러오고 마지막으로instanceInjector로 돌아갑니다.
다음은 정의된 변수에 대한 설명입니다.
2. createInternalInjector
이 방법은 캐치(대상)와factory(함수) 두 개의 매개 변수를 수신하는 주사기를 만들고 최종 반환 값은 다음과 같다.
{
invoke: invoke,
instantiate: instantiate,
get: getService,
annotate: createInjector.$$annotate,
has: function(name) {
return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
}
}
2.1getService
function getService(serviceName, caller) {
if (cache.hasOwnProperty(serviceName)) {
if (cache[serviceName] === INSTANTIATING) {
throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
serviceName + ' + path.join(' ));
}
return cache[serviceName];
} else {
try {
path.unshift(serviceName);
cache[serviceName] = INSTANTIATING;
return cache[serviceName] = factory(serviceName, caller);
} catch (err) {
if (cache[serviceName] === INSTANTIATING) {
delete cache[serviceName];
}
throw err;
} finally {
path.shift();
}
}
}
이 방법의 주요 사고방식은 서비스Name에 대응하는 서비스가 있는지 캐치에서 확인하고 있으면 바로 되돌려줍니다. 그렇지 않으면factory를 호출해서 하나를 만들고 캐치에 캐시합니다.
2.2annotate
이 방법은 주로 주입을 추정하는 데 쓰인다.예를 들어 다음 코드는 다음과 같습니다.
angular.module('MyModule', [])
.factory('service', function() {
return {
greeting: 'hello world'
};
})
.controller('ctrl', ['$scope', '$injector', function($scope, $injector) {
$injector.invoke(function(service) {
console.log(service.greeting);
});
}]);
$injector를 호출하고 있습니다.invoke에서 서비스를 주입했습니다.AngularJS는 주로 함수의 toString() 방법을 통해 이 기능을 수행합니다.관련 소스는 다음과 같습니다.
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var $injectorMinErr = minErr('$injector');
function anonFn(fn) {
// For anonymous functions, showing at the very least the function signature can help in
// debugging.
var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
args = fnText.match(FN_ARGS);
if (args) {
return 'function(' + (args[1] || '').replace(/[\s\r
]+/, ' ') + ')';
}
return 'fn';
}
function annotate(fn, strictDi, name) {
var $inject,
fnText,
argDecl,
last;
if (typeof fn === 'function') {
if (!($inject = fn.$inject)) {
$inject = [];
if (fn.length) {
if (strictDi) {
if (!isString(name) || !name) {
name = fn.name || anonFn(fn);
}
throw $injectorMinErr('strictdi',
'{0} is not using explicit annotation and cannot be invoked in strict mode', name);
}
fnText = fn.toString().replace(STRIP_COMMENTS, '');
argDecl = fnText.match(FN_ARGS);
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
arg.replace(FN_ARG, function(all, underscore, name) {
$inject.push(name);
});
});
}
fn.$inject = $inject;
}
} else if (isArray(fn)) {
last = fn.length - 1;
assertArgFn(fn[last], 'fn');
$inject = fn.slice(0, last);
} else {
assertArgFn(fn, 'fn', true);
}
return $inject;
}
소스 코드를 통해 다음을 확인할 수 있습니다.
angular.module('MyModule', [])
.factory('service', function() {
return {
greeting: 'hello world'
};
})
.controller('ctrl', ['$scope', '$injector', function($scope, $injector) {
console.log($injector.annotate(function(arg1, arg2, arg3) {}));
// ["arg1", "arg2", "arg3"]
console.log($injector.annotate(['service', function(arg1) {}]));
// ["service"]
}]);
2.3invoke
소스는 다음과 같습니다.
function invoke(fn, self, locals, serviceName) {
if (typeof locals === 'string') {
serviceName = locals;
locals = null;
}
var args = [],
$inject = createInjector.$$annotate(fn, strictDi, serviceName),
length, i,
key;
for (i = 0, length = $inject.length; i < length; i++) {
key = $inject[i];
if (typeof key !== 'string') {
throw $injectorMinErr('itkn',
'Incorrect injection token! Expected service name as string, got {0}', key);
}
args.push(
locals && locals.hasOwnProperty(key) ? locals[key] : getService(key, serviceName)
);
}
if (isArray(fn)) {
fn = fn[length];
}
// http://jsperf.com/angularjs-invoke-apply-vs-switch
// #5388
return fn.apply(self, args);
}
아이디어는 다음과 같습니다.
3.loadModules
이 방법은 주로 모듈을 로드하는 데 사용되며 소스는 다음과 같습니다.
function loadModules(modulesToLoad) {
var runBlocks = [],
moduleFn;
forEach(modulesToLoad, function(module) {
if (loadedModules.get(module)) return;
loadedModules.put(module, true);
function runInvokeQueue(queue) {
var i, ii;
for (i = 0, ii = queue.length; i < ii; i++) {
var invokeArgs = queue[i],
provider = providerInjector.get(invokeArgs[0]);
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
}
}
try {
if (isString(module)) {
moduleFn = angularModule(module);
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
runInvokeQueue(moduleFn._invokeQueue);
runInvokeQueue(moduleFn._configBlocks);
} else if (isFunction(module)) {
runBlocks.push(providerInjector.invoke(module));
} else if (isArray(module)) {
runBlocks.push(providerInjector.invoke(module));
} else {
assertArgFn(module, 'module');
}
} catch (e) {
if (isArray(module)) {
module = module[module.length - 1];
}
if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
// Safari & FF's stack traces don't contain error.message content
// unlike those of Chrome and IE
// So if stack doesn't contain message, we create a new string that contains both.
// Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
/* jshint -W022 */
e = e.message + '
' + e.stack;
}
throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:
{1}",
module, e.stack || e.message || e);
}
});
return runBlocks;
}
함수 loadModules의 역할은 배열 modulesToLoad의 각 항목에 대해 각각 로드하는 것입니다.
요약:
angularJS 내부에 두 개의 캐시를 설치했습니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
AngularJS의 ng-options best practise쓸데없는 말은 하지 말고 바로 코드를 찍어라. 리소스를api에 직접 전달하지 말고 문자열이나 정형(예를 들어 귀속된ng-model="selected")을 권장합니다 angular에서 생성된 의value가 무엇인지, ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.