一个工厂
(function(global, factory){
"use strict"
// operation_1
})(typedef window !== "undefined" ? window : this, function (window, noGlobal) {
// operation_2
})
=====> (function(args,...){
...})(params,...) // 一种自执行方法,形式为(function(){})(),也可以写为 !function(){}() 或 ;function(){}();
其中function(window, noGlobal){}有11000多行代码,并且注入到operation_1中执行。
// operation_1
// 这个一个兼容操作,旨在检测文件是否运行在CommonJS中,如NodeJS,如果是的话进入此分支,一般不会进入
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = global.document ?
factory(global, true) : // 如果global.document存在,则向外面暴露出 factory 方法
function (w) {
if (!w.document) {
// 因为不存在w,所以 !w.document为true,所以报错
throw new Error("jQuery requires a window with a document");
}
return factory(w);
};
} else {
// 通常直接进入此分支
factory(global); // 没有传入noGlobal,将引发其他操作
}
至此完成了方法的注入调用。但是我们发现传入的实参比形参少一个noGlobal,为此我们全局查找此参数
// noGlobal第三次出现在11124行
if (typeof noGlobal === "undefined") {
// 如果noGlobal未定义(未传入)
window.jQuery = window.$ = jQuery; // 将实例jQuery赋值给窗口的$或jQuery,这也就是$()和jQuery()两个选择器的由来
}
通过使用窗口(全局)的jQuery或$,就可以实现jQuery类或对象的调用,当然调用对象需要用到其构造方法,我们接着往下看
知识点:
- 自执行方法: 形式有
(function(){})()
,!function(){}()
,;function(){}()
- “use strict”:开启严格模式,此模式下代码质量要求及书写规范要求更高,但能保证代码的安全,提高编译器效率,加快运行速度
- typedef module === “object”: 在其他JS模块中的执行检测,详细请跳转至此处
3个变量,6个函数
/**
* 内部变量
*/
var arr = [];
var getProto = Object.getPrototypeOf;
var slice = arr.slice;
... // 有些变量看起来没有必要定义,但是为了避免魔鬼变量(数字、字符串),代码中具有特殊用途的变量必须定义
/**
* 内部方法
*/
// 数组扁平化,将数组深度遍历并组成新的一维数组
var flat = arr.flat ? function(array){
return arr.flat.call(array);
} : function (array) {
// 如果不能直接扁平化,则利用concat.apply()方法实现
// Array.prototype.concat.apply([],array) 递归扫描array数组,并扁平化追加到参数1内,因此需要输入一个空数组
return arr.concat.apply([], array);
};
// 判断是否是方法
var isFunction = function isFunction(obj) {
return typeof obj === "function" && typeof obj.nodeType !== "number"; // typeof obj.nodeType !== "number",任何DOM元素的nodetype都是数字,常见的有1 9 11
};
// 简单的理解为,创造一个类似node的Script节点(内容为code)并挂载到doc上
function DOMEval(code, node, doc) {
doc = doc || document;
var i, val, script = doc.createElement("script");
script.text = code;
if (node) {
for (i in preservedScriptAttributes) {
// 对于xx中的每个属性
// 有些浏览器不支持脚本的“nonce”属性。另一方面,仅仅使用“getAttribute”是不够的,因为每当“nonce”属性连接到上下文时,
// 它就会重置为空字符串。在`节点.getAttribute`添加支票是为了`jQuery.globalEval`这样它就可以通过一个对象伪造一个包含nonce的节点。
val = node[i] || node.getAttribute && node.getAttribute(i); // 如果node存在这个属性,则设置到script中
if (val) {
script.setAttribute(i, val);
}
}
}
doc.head.appendChild(script).parentNode.removeChild(script); // script挂载到头节点上
}
// 获取对象的类型
function toType(obj) {
if (obj == null) {
return obj + ""; // 强转成字符串,null + "" == "null"