启动 angular 之前都做了什么 ?

@license AngularJS v1.4.8

该代码块基于源码进行了删减
该代码块仅用于学习 分享 交流
只梳理了代码的初始化,具体方法是干什么的等到后续说明

在启动angular之前,会做一些初始化工作

  • 第一步 绑定 JQLite(JQuery)
  • 第二步 初始化对外暴露的 angular 方法
  • 第三步 初始化 angular.module 方法
  • 第四步 初始化 ng 模块
  • 第五步 初始化 ngLocale 模块
  • 第六步 添加 ng 样式
  • 最后是 启动 angular

angular 在处理方法上采用了大量的闭包 对外只公开一个 angular 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// 直接定位到代码的最后
// 从这开始执行初始化
/*** 第一步 绑定 JQLite(JQuery) ***/
bindJQuery();

// 植入 JQuery ng-jq="newJquery"
var jq = function() {
var el;
var i, ii = ngAttrPrefixes.length, prefix, name;
for (i = 0; i < ii; ++i) {
prefix = ngAttrPrefixes[i];
if(el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')){
name = el.getAttribute(prefix + 'jq');
break;
}
}

return (jq.name_ = name);
};

// 节流字段 防止重复绑定
var bindJQueryFired = false;
function bindJQuery() {
if (bindJQueryFired) {
return;
}

var jqName = jq();
// 是否植入了 JQuery 需要在 angular 之前加载
// 是:用植入的 并对于手动植入的 JQuery 添加 angular 拓展
// 否:用 angular 内置的
jQuery = isUndefined(jqName) ? window.jQuery :
!jqName ? undefined :
window[jqName];

if (jQuery && jQuery.fn.on) {
jqLite = jQuery;
// 添加的拓展具体什么作用暂不考虑
extend(jQuery.fn, {
scope: JQLitePrototype.scope,
isolateScope: JQLitePrototype.isolateScope,
controller: JQLitePrototype.controller,
injector: JQLitePrototype.injector,
inheritedData: JQLitePrototype.inheritedData
});
} else {
jqLite = JQLite;
}

// 最终将 JQuery 挂载到 angular 对象上
angular.element = jqLite;

bindJQueryFired = true;
}

/*** 第二步 初始化对外暴露的 angular 方法 ***/
publishExternalAPI(angular);

var version = {
full: '1.4.8',
major: 1,
minor: 4,
dot: 8,
codeName: 'ice-manipulation'
};
function publishExternalAPI(angular) {
// 添加方法
extend(angular, {
'bootstrap': bootstrap,
'element': jqLite,
'injector': createInjector,
'version': version,
// ...
});

/*** 第三步 初始化 angular.module 方法 ***/
angularModule = setupModuleLoader(window);

/*** 第四步 初始化 ng 模块 ***/
angularModule('ng', ['ngLocale'], ['$provide',
function ngModule($provide) {
// $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
$provide.provider({
$$sanitizeUri: $$SanitizeUriProvider
});
$provide.provider('$compile', $CompileProvider).
directive({
select: selectDirective,
ngBind: ngBindDirective,
ngHide: ngHideDirective,
ngInit: ngInitDirective
// ...
}).
directive({
ngInclude: ngIncludeFillContentDirective
}).
directive(ngAttributeAliasDirectives).
directive(ngEventDirectives);
$provide.provider({
$controller: $ControllerProvider,
$filter: $FilterProvider,
$http: $HttpProvider,
$rootScope: $RootScopeProvider,
$q: $QProvider
// ...
});
}
]);
}

// 处理模块
function setupModuleLoader(window) {
function ensure(obj, name, factory) {
// 如果存在模块直接返回模块实例,否则添加到 modules 之后再返回模块实例
return obj[name] || (obj[name] = factory());
}

var angular = ensure(window, 'angular', Object);
// 初始化 angular.module 方法
return ensure(angular, 'module', function() {
// 通过闭包 用 modules 缓存模块实例
var modules = {};
// angular.module = module;
return function module(name, requires, configFn) {
// 重复定义模块 后面的会把前面的覆盖
if (requires && modules.hasOwnProperty(name)) {
modules[name] = null;
}

// 模块的定义和使用 巧妙的运用 ensure 的 短路 运算
// 通过 modules[name] 和 requires 双重判断
return ensure(modules, name, function() {
if (!requires) {
throw '抛出异常';
}

/** @type {!Array.<Array.<*>>} */
var invokeQueue = [];

/** @type {!Array.<Function>} */
var configBlocks = [];

/** @type {!Array.<Function>} */
var runBlocks = [];

var config = invokeLater('$injector', 'invoke', 'push', configBlocks);

/** @type {angular.Module} */
// 返回的 module 实例
var moduleInstance = {
// Private state
_invokeQueue: invokeQueue,
_configBlocks: configBlocks,
_runBlocks: runBlocks,
requires: requires,
name: name,
provider: invokeLaterAndSetModuleName('$provide', 'provider'),
factory: invokeLaterAndSetModuleName('$provide', 'factory'),
service: invokeLaterAndSetModuleName('$provide', 'service'),
value: invokeLater('$provide', 'value'),
constant: invokeLater('$provide', 'constant', 'unshift'), // 追加到栈前
decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
config: config,
run: function(block) {
runBlocks.push(block);
return this;
}
};

if (configFn) {
config(configFn);
// > configBlocks: [['$injector', 'invoke', configFn]]
}

return moduleInstance;
// var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
function invokeLater(provider, method, insertMethod, queue) {
if (!queue) queue = invokeQueue;
return function() {
// 添加到 栈/执行栈 中
queue[insertMethod || 'push']([provider, method, arguments]);
// 返回 模块实例 实现链式调用
return moduleInstance;
};
}

function invokeLaterAndSetModuleName(provider, method) {
return function(recipeName, factoryFunction) {
if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
// 添加到执行栈中
invokeQueue.push([provider, method, arguments]);
return moduleInstance;
};
}
});
};
});
}

/*** 第五步 初始化 ngLocale 模块 ***/
angular.module("ngLocale", [], ["$provide", function($provide) {
// ...
$provide.value("$locale", {
"DATETIME_FORMATS": {
"AMPMS": [
"AM",
"PM"
]
// ...
},
"id": "en-us",
"pluralCat": function(n, opt_precision) {});
}]);

/*** 第六步 添加 ng 样式 ***/
window.angular.element(document.head).prepend('<style>....</style>');

jqLite(document).ready(function() {
/*** 最后是 启动 angular 后续再讲 ***/
angularInit(document, bootstrap);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 初始化之后的结构如下
// 全局的 angular
angular: {
$$csp: ƒ (),
$$minErr: ƒ minErr(module, ErrorConstructor),
$interpolateMinErr: ƒ (),
bind: ƒ bind(self, fn),
bootstrap: ƒ bootstrap(element, modules, config),
callbacks: {counter: 0},
copy: ƒ copy(source, destination),
element: ƒ JQLite(element),
equals: ƒ equals(o1, o2),
extend: ƒ extend(dst),
forEach: ƒ forEach(obj, iterator, context),
fromJson: ƒ fromJson(json),
getTestability: ƒ getTestability(rootElement),
identity: ƒ identity($),
injector: ƒ createInjector(modulesToLoad, strictDi),
isArray: ƒ isArray(),
isDate: ƒ isDate(value),
isDefined: ƒ isDefined(value),
isElement: ƒ isElement(node),
isFunction: ƒ isFunction(value),
isNumber: ƒ isNumber(value),
isObject: ƒ isObject(value),
isString: ƒ isString(value),
isUndefined: ƒ isUndefined(value),
lowercase: ƒ (string),
merge: ƒ merge(dst),
module: ƒ module(name, requires, configFn),
noop: ƒ noop(),
reloadWithDebugInfo: ƒ reloadWithDebugInfo(),
toJson: ƒ toJson(obj, pretty),
uppercase: ƒ (string),
version: {full: "1.4.8", major: 1, minor: 4, dot: 8, codeName: "ice-manipulation"}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 闭包内的 modules
modules: {
ng: {
animation: ƒ (recipeName, factoryFunction),
config: ƒ (),
constant: ƒ (),
controller: ƒ (recipeName, factoryFunction),
decorator: ƒ (recipeName, factoryFunction),
directive: ƒ (recipeName, factoryFunction),
factory: ƒ (recipeName, factoryFunction),
filter: ƒ (recipeName, factoryFunction),
name: "ng",
provider: ƒ (recipeName, factoryFunction),
requires: ["ngLocale"],
run: ƒ (block),
service: ƒ (recipeName, factoryFunction),
value: ƒ (),
_configBlocks: [["$injector", "invoke", Arguments(1)]],
_invokeQueue: [],
_runBlocks: []
},
ngLocale: { /* ... */ }
}