function publishExternalAPI(angular){}
publishExternalAPI就是將一些通用的方法掛載到全局變量angular上,然後我們就可以以API的形式進行調用
函數首先傳入一個angular的全局變量,下面將會遇到在前面定義的好的一個extend函數
function extend(dst) {
var h = dst.$$hashKey;
return forEach(arguments, function(obj) {
obj !== dst && forEach(obj, function(value, key) {
dst[key] = value
})
}), setHashKey(dst, h), dst
}
這裏的方法就是將某些通用的方法掛到一個對象上面,extend傳入和返回都是dst形參對象,裏面經歷了兩個forEach,forEach也是angular前面定義好的函數,extend根據傳入的arguments個數進行判斷,dst是需要掛載的對象,arguments中除了dst外都是需要被掛載到dst中的對象
obj !== dst && forEach(obj, function(value, key) {})
上面這句就是將arguments中的dst對象排除出去,然後對dst進行對象掛載
然後就轉化成
angular['boostrap'] = boostrap;
angular['copy'] = copy;
angular['extend'] = extend;
//...省略若干個通用的方法
上面就不一一列出所有方法,當然當我們對加載了angular.js的頁面進入控制檯進行打印就會很清晰的看出這裏的全部方法
console.log(angular);
如圖所示
[外鏈圖片轉存失敗(img-hOr6YKs6-1567235827024)(https://cloud.githubusercontent.com/assets/17243165/15562024/16e25fd2-232e-11e6-9093-fad75a9fed3d.png)]
這些通用的工具函數在實際的項目中會經常使用到,挺方便的例如forEach,toJson和fromJson這些常用函數
當然就如上面的extend也可以是做API調用,也寫個簡單的例子,同樣在控制器裏面輸入一段代碼,然後打印,這裏創建一個空對象,並把該對象後面的對象拷貝並掛載到該空對象中,並賦值給wscat,那麼wscat對象就有對應的這些屬性
var wscat = angular.extend({}, {name:"wscats",skill:"none"})
console.log(wscat)
[外鏈圖片轉存失敗(img-IDoZoX6h-1567235827026)(https://cloud.githubusercontent.com/assets/17243165/15562172/7bf9a6c2-232f-11e6-9a95-a7fc549c2cb6.png)]
咱們繼續往下看
angularModule = setupModuleLoader(window)
這裏爲angular對象創建和加載module()函數,這裏我在Angular源碼解讀setupModuleLoader函數有詳細的分析,這裏就不多說了,有需要的的可以回頭看一下
繼續下面這句
try {
angularModule("ngLocale")
} catch (e) {
angularModule("ngLocale", []).provider("$locale", $LocaleProvider)
}
try代碼塊裏面首先判斷是否獲取到獲取ngLocale模塊,如果我們獲取不到,則就報個異常(ngLocale默認沒有創建的)
catch代碼塊接受上面的異常(也就是獲取不到ngLocale模塊走catch分支),然後在這個位置註冊ngLocale模塊
簡單的說這裏angular.module在創建模塊的時候,傳遞一個參數的時候,是獲取模塊;傳遞一個以上的是創建新模塊;所以try裏面是獲取,catch裏面就是創建了
那麼這裏就成功的創建了一個ngLocale模塊,其實angularModule實際上就是angular.module
angularModule = angular.module
angularModule("ngLocale", []) = angular.module("ngLocale", [])
所以我們就可以鏈式去調用對象moduleInstance中的directive和provider
annotate用來分析一個函數的簽名,也就是函數的參數
function annotate(fn) {
var $inject, fnText, argDecl, last;
return "function" == typeof fn ? ($inject = fn.$inject) || ($inject = [], fn.length && (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) : isArray(fn) ? (last = fn.length - 1, assertArgFn(fn[last], "fn"), $inject = fn.slice(0, last)) : assertArgFn(fn, "fn", !0), $inject
}
注入器$injector的annotate方法的作用爲分析函數的參數簽名
上面代碼把它展開如下
function annotate(fn) {
var $inject,
fnText,
argDecl,
last;
if (typeof fn == 'function') {
if (!($inject = fn.$inject)) {
$inject = [];
if (fn.length) {
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;
}
annotate傳參的第一種情況(傳函數)
首先判斷傳入的值是否函數
typeof fn == 'function'
再判斷函數有沒有inject = fn.$inject)首先經過toString()序列化 處理轉換爲字符串,
fnText = fn.toString().replace(STRIP_COMMENTS, ‘’);`
arg.replace(FN_ARG, function(all, underscore, name){
$inject.push(name);
});
STRIP_COMMENTS和FN_ARG在前面定義好的正則表達式
STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm
FN_ARG = /^\s*(_?)(\S+?)\1\s*$/
以上正則獲取了依賴模塊的名稱並存入$inject數組中返回
簡單演示這部分函數,走了了個類似的流程代碼方便理解,如下
var
FN_ARG_SPLIT = /,/,
FN_ARG = /^\s*(_?)(\S+?)\1\s*$/,
FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m,
STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
function annotate(fn) {
var $inject,
fnText,
argDecl;
if (typeof fn == 'function') {
if (!($inject = fn.$inject)) {
$inject = [];
if (fn.length) {
fnText = fn.toString().replace(STRIP_COMMENTS, '');
argDecl = fnText.match(FN_ARGS);
argDeclSplit = argDecl[1].split(FN_ARG_SPLIT);
argDeclSplit[0].replace(FN_ARG, function(all, underscore, name) {
$inject.push(name);
});
argDeclSplit[1].replace(FN_ARG, function(all, underscore, name) {
$inject.push(name);
});
argDeclSplit[2].replace(FN_ARG, function(all, underscore, name) {
$inject.push(name);
});
console.log(fn)
console.log(fnText);
console.log(argDecl);
console.log(argDeclSplit);
console.log($inject);
}
fn.$inject = $inject;
}
}
return $inject;
}
annotate(function(a , $scope ,$window) {});
打印的結果如下圖
裏面省略了forEach等angular方法,不過思路和目的是相同的,可以看到一些諸如空格等被正則正確的處理掉,然後最後返回一個數組
annotate傳參的第二種情況(傳數組)
那就是走了else if (isArray(fn)) {}
的分支
這裏用了angular自定義的函數assertArgFn
function assertArg(arg, name, reason) {
if (!arg) throw ngMinErr("areq", "Argument '{0}' is {1}", name || "?", reason || "required");
return arg
}
function assertArgFn(arg, name, acceptArrayAnnotation) {
return acceptArrayAnnotation && isArray(arg) && (arg = arg[arg.length - 1]), assertArg(isFunction(arg), name, "not a function, got " + (arg && "object" == typeof arg ? arg.constructor.name || "Object" : typeof arg)), arg
}
其實就是處理數組中最後的元素,而最後的元素是個函數
var fn = ["a", "$scope", "$window", function(){}];
function annotate(fn){
last = fn.length - 1;
assertArgFn(fn[last], 'fn');
$inject = fn.slice(0, last);
console.log($inject)
}
function assertArgFn(arg, name, acceptArrayAnnotation) {
return acceptArrayAnnotation && (arg = arg[arg.length - 1]), arg
}
annotate(fn);
createInternalInjector返回一個對象如下
return {
invoke: invoke,
instantiate: instantiate,
get: getService,
annotate: annotate,
has: function(name) {
return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name)
}
}
裏面有五個方法invoke,instantiate,get,annotate,has
annotate已經詳細介紹過了,就是獲取函數的參數
instantiate則是用來實例化一個對象
get用來獲得一個服務的實例對象
invoke用來調用一個函數
首先分析invoke函數
function invoke(fn, self, locals) {
var length, i, key, args = [],
$inject = annotate(fn);
for (i = 0, length = $inject.length; length > i; i++) {
if (key = $inject[i], "string" != typeof key) throw $injectorMinErr("itkn", "Incorrect injection token! Expected service name as string, got {0}", key);
args.push(locals && locals.hasOwnProperty(key) ? locals[key] : getService(key))
}
switch (fn.$inject || (fn = fn[length]), self ? -1 : args.length) {
case 0:
return fn();
case 1:
return fn(args[0]);
case 2:
return fn(args[0], args[1]);
case 3:
return fn(args[0], args[1], args[2]);
case 4:
return fn(args[0], args[1], args[2], args[3]);
case 5:
return fn(args[0], args[1], args[2], args[3], args[4]);
case 6:
return fn(args[0], args[1], args[2], args[3], args[4], args[5]);
case 7:
return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
case 8:
return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
case 9:
return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
case 10:
return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
default:
return fn.apply(self, args)
}
}
首先把函數的fn的$inject = annotate(fn);
參數用annotate以數組形式保存在$inject裏面
再驗證函數的參數
for (i = 0, length = $inject.length; length > i; i++) {
if (key = $inject[i], "string" != typeof key) throw $injectorMinErr("itkn", "Incorrect injection token! Expected service name as string, got {0}", key);
args.push(locals && locals.hasOwnProperty(key) ? locals[key] : getService(key))
}
不正確註冊簽名,服務名稱應該爲字符串Incorrect injection token! Expected service name as string
然後判斷是否有傳入locals,如果有則判斷它的屬性是否含有fn函數的參數,如果有並且包含這些參數然後把他們保存到args數組裏面
例如angular在bootstrap方法中調用invoke函數
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate',
function(scope, element, compile, injector, animate) {
scope.$apply(function() {
element.data('$injector', injector);
compile(element)(scope);
});
}]
);
return injector;
開始args.push(getService(rootElement))…
然後判斷fn.inject = annotate(fn),key = rootScope’, ‘compile’, ‘animate’,function(scope, element, compile, injector, animate) {})’])的回調函數就可以在執行的時候調用裏面的依賴
我寫了個簡單的流程來模仿invoke函數實際上做了什麼如下
var fn = ['wsccat','wscats',function fn(){
console.log(arguments);
}];
function invoke(fn, self, locals) {
var args = ['wsccat','wscats'];//annotate(fn)處理後放入args
var fn = fn[fn.length-1];
var self = self ? -1 : fn.length-1;//switch處理
switch (self) {
case 0:
return fn();
break;
default:
return fn.apply(self, args);
}
}
//invoke(fn);
invoke(['wsccat','wscats',function fn(){
console.log(arguments);
}]);
最後執行的打印結果是運行了閉包函數並輸出apply後'wsccat','wscats'
的兩個參數
實際上apply會執行匿名函數一次,此時的匿名函數fn(){}已經有了wsccat,wscats兩個參數了
get就是createInternalInjector裏面的getService閉包函數
後面是這樣調用的var provider = providerInjector.get(servicename + providerSuffix);
也就是相當於var provider = providerInjector.get(servicename + ‘Provider’);
function getService(serviceName) {
if (cache.hasOwnProperty(serviceName)) {
if (cache[serviceName] === INSTANTIATING) throw $injectorMinErr("cdep", "Circular dependency found: {0}", path.join(" <- "));
return cache[serviceName]
}
try {
return path.unshift(serviceName), cache[serviceName] = INSTANTIATING, cache[serviceName] = factory(serviceName)
} finally {
path.shift()
}
}
先看看在執行getService時候,createInternalInjector函數體裏有個path = [],path的數組
首先執行這個函數前要用到createInternalInjector函數傳入的參數createInternalInjector(cache, factory),cache和factory,比如下面的這句
createInternalInjector(providerCache, function() {
throw $injectorMinErr("unpr", "Unknown provider: {0}", path.join(" <- "))
}),
providerCache對象是下面這個
providerCache = {
$provide: {
provider: supportObject(provider),
factory: supportObject(factory),
service: supportObject(service),
value: supportObject(value),
constant: supportObject(constant),
decorator: decorator
}
}
這些參數再放到閉包函數getService裏面執行
createInjector方法裏面,其實是通過createInternalInjector方法來創建注入器的。
var INSTANTIATING = {}, providerSuffix = "Provider",
path = [],
loadedModules = new HashMap,
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() {
throw $injectorMinErr("unpr", "Unknown provider: {0}", path.join(" <- "))
}),
instanceCache = {}, instanceInjector = instanceCache.$injector = createInternalInjector(instanceCache, function(servicename) {
var provider = providerInjector.get(servicename + providerSuffix);
return instanceInjector.invoke(provider.$get, provider)
});
這裏會用createInternalInjector方法創建兩個$injector對象
providerInjector是用來創建provider的,instanceInjector是用來創建一個對象實例的。
第一個創建providerInjector對象
providerInjector = providerCache.$injector = createInternalInjector(providerCache, function() {
throw $injectorMinErr("unpr", "Unknown provider: {0}", path.join(" <- "))
})
實際上providerCache就變成了這樣
providerCache = {
$provide: {
provider: supportObject(provider),
factory: supportObject(factory),
service: supportObject(service),
value: supportObject(value),
constant: supportObject(constant),
decorator: decorator
}
},
$injector:{
get:getService,
annotate:annotate,
instantiate:instantiate,
invoke:invoke,
has:has
}
}
而providerInjector變成了這樣
providerInjector:{
nvoke: invoke,
instantiate: instantiate,
get: getService,
annotate: annotate,
has: has
}
第二個創建instanceInjector對象
instanceInjector = instanceCache.$injector = createInternalInjector(instanceCache, function(servicename) {
var provider = providerInjector.get(servicename + providerSuffix);
return instanceInjector.invoke(provider.$get, provider)
})
實際上instanceCache就變成了這樣
instanceCache:{
$injector:{
invoke: invoke,
instantiate: instantiate,
get: getService,
annotate: annotate,
has: has
}
}
而instanceInjector變成了這樣
instanceInjector = {
invoke: invoke,
instantiate: instantiate,
get: getService,
annotate: annotate,
has: has
}
從angular源碼可以知道,創建provider的這幾種方式:provider/factory/service/constant/value,左往右靈活性越低,provider方法是基礎,其他都是調用provider方法實現的,只是的參數不一樣而已
function provider(name, provider_) {
assertNotHasOwnProperty(name, 'service');
if (isFunction(provider_) || isArray(provider_)) {
provider_ = providerInjector.instantiate(provider_);
}
if (!provider_.$get) {
throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
}
return providerCache[name + providerSuffix] = provider_;
}