Description
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);
如图所示
这些通用的工具函数在实际的项目中会经常使用到,挺方便的例如forEach,toJson和fromJson这些常用函数
当然就如上面的extend也可以是做API调用,也写个简单的例子,同样在控制器里面输入一段代码,然后打印,这里创建一个空对象,并把该对象后面的对象拷贝并挂载到该空对象中,并赋值给wscat,那么wscat对象就有对应的这些属性
var wscat = angular.extend({}, {name:"wscats",skill:"none"})
console.log(wscat)
咱们继续往下看
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
Activity
Wscats commentedon May 26, 2016
annotate用来分析一个函数的签名,也就是函数的参数
annotate传参的第一种情况(传函数)
首先判断传入的值是否函数
typeof fn == 'function'
再判断函数有没有$inject属性,如果没有的话
!($inject = fn.$inject)
首先经过toString()序列化 处理转换为字符串,
fnText = fn.toString().replace(STRIP_COMMENTS, '');
STRIP_COMMENTS和FN_ARG在前面定义好的正则表达式
STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm
FN_ARG = /^\s*(_?)(\S+?)\1\s*$/
以上正则获取了依赖模块的名称并存入$inject数组中返回
简单演示这部分函数,走了了个类似的流程代码方便理解,如下
打印的结果如下图

里面省略了forEach等angular方法,不过思路和目的是相同的,可以看到一些诸如空格等被正则正确的处理掉,然后最后返回一个数组
annotate传参的第二种情况(传数组)
那就是走了
else if (isArray(fn)) {}
的分支这里用了angular自定义的函数assertArgFn
其实就是处理数组中最后的元素,而最后的元素是个函数
Wscats commentedon May 26, 2016
createInternalInjector返回一个对象如下
里面有五个方法invoke,instantiate,get,annotate,has
annotate已经详细介绍过了,就是获取函数的参数
instantiate则是用来实例化一个对象
get用来获得一个服务的实例对象
invoke用来调用一个函数
首先分析invoke函数
首先把函数的fn的
$inject = annotate(fn);
参数用annotate以数组形式保存在$inject里面再验证函数的参数
不正确注册签名,服务名称应该为字符串Incorrect injection token! Expected service name as string
然后判断是否有传入locals,如果有则判断它的属性是否含有fn函数的参数,如果有并且包含这些参数然后把他们保存到args数组里面
例如angular在bootstrap方法中调用invoke函数
开始args.push(getService($rootScope)),args.push(getService($rootElement))...
然后判断fn.$inject || (fn = fn[length]),如果是数组的话就会拿数组最后一个,也就是传入最后的那个函数赋给fn
然后进入因为没有传入self所以
self ? -1 : args.length
self就是-1,就是走default分支return fn.apply(self, args)
,如果self有值得话其实很好理解就是把$inject = annotate(fn),key = $inject[i]
annotate函数拿到的参数一个一个的传进去function(){}里面,然后后面的injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate',function(scope, element, compile, injector, animate) {})'])的回调函数就可以在执行的时候调用里面的依赖我写了个简单的流程来模仿invoke函数实际上做了什么如下
最后执行的打印结果是运行了闭包函数并输出apply后

'wsccat','wscats'
的两个参数实际上apply会执行匿名函数一次,此时的匿名函数fn(){}已经有了wsccat,wscats两个参数了
get就是createInternalInjector里面的getService闭包函数
后面是这样调用的
var provider = providerInjector.get(servicename + providerSuffix);
也就是相当于var provider = providerInjector.get(servicename + ‘Provider’);
先看看在执行getService时候,createInternalInjector函数体里有个path = [],path的数组
首先执行这个函数前要用到createInternalInjector函数传入的参数createInternalInjector(cache, factory),cache和factory,比如下面的这句
providerCache对象是下面这个
这些参数再放到闭包函数getService里面执行
createInjector方法里面,其实是通过createInternalInjector方法来创建注入器的。
这里会用createInternalInjector方法创建两个$injector对象
providerInjector是用来创建provider的,instanceInjector是用来创建一个对象实例的。
第一个创建providerInjector对象
实际上providerCache就变成了这样
而providerInjector变成了这样
第二个创建instanceInjector对象
实际上instanceCache就变成了这样
而instanceInjector变成了这样
Wscats commentedon May 27, 2016
从angular源码可以知道,创建provider的这几种方式:provider/factory/service/constant/value,左往右灵活性越低,provider方法是基础,其他都是调用provider方法实现的,只是的参数不一样而已