本文分析的 jquery 版本为 v3.6.0
整体结构分析
源码结构:
( function( global, factory ) {
"use strict";
if ( typeof module === "object" && typeof module.exports === "object" ) {
module.exports = global.document ?
factory( global, true ) :
function( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
} else {
factory( global );
}
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
// ...
});
可以看到,这就是一个自执行函数,并把全局对象传入主函数内, 实际就是如下效果。
// 检测当前运行 JS 的环境是否支持 window 「node环境下运行,g -> global/当前模块 」
var g = typeof window !== 'undefined' ? window : this;
var factory = function factory(window, noGlobal) {
// 这里才是 jqery 的核心,外面的壳子只是在区分环境,判断全局对象归属
// ...
}
(function(global, factory) {
// 如果是浏览器 (webview & webpack): global -> window
// node 环境: global -> global/模块
"use strict";
if ( typeof module === "object" && typeof module.exports === "object" ) {
// node「webpack」:符合 commonJS 规范检测
module.exports = global.document ?
// global -> window noGlobal -> true 说明当前是运行在 webpack 环境当中
factory( global, true ) :
// node 环境中运行
function( w ) {
// node 环境下报错 jquery 不支持 node 环境运行
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
} else {
// 浏览器「webview」不支持 commonJs 规范
// global -> window noGlobal -> undefined
factory( global );
}
})(g, factory);
接下来我们分析下核心部分,也就是 factory 函数中的代码。
var factory = function factory(window, noGlobal) {
"use strict";
// ...
var version = "3.6.0",
// 全局声明 jquery
jQuery = function( selector, context ) {
return new jQuery.fn.init( selector, context );
};
// 导出 API
if ( typeof define === "function" && define.amd ) {
// 如果支持 AMD 规范,则用 AMD 规范返回出 jQuery 模块
define( "jquery", [], function() {
return jQuery;
} );
}
// ...
// 防止和其他库变量名冲突的特殊处理,优秀的思想
// 场景
// 先导入 zepto.js,它本身会声明 window.$ = Zepto
// 再导入 jquery.js
var _jQuery = window.jQuery,
_$ = window.$; // 举例:把 Zepto 赋值给 _$
// 为了避免 jq 会抢了 zepto 对 $ 的使用权,此时需要转让使用权
jQuery.noConflict = function( deep ) {
// 当调用 var jquery = $.noConflict();
// 先判断全局 $ 是不是 jqury,是则归还给前 $ 使用者
// 并且把 jquery 作为函数的返回值。
if ( window.$ === jQuery ) {
window.$ = _$;
}
// 如果 jQuery 存在, jQuery 的使用权也转出去
if ( deep && window.jQuery === jQuery ) {
window.jQuery = _jQuery;
}
return jQuery;
};
if ( typeof noGlobal === "undefined" ) {
// 没传这个参数 说明 浏览器或者 webview 环境,window 存在
// 全局对象中暴露 "jQuery | $"
window.jQuery = window.$ = jQuery;
}
// 当符合 commonJs 规范时候(node 或 webpack环境) module.exports = factory(..)
return jQuery;
}
能收获什么
- 如果我们去实现一个工具库,想让其支持在各种环境下调用,就可以参考 jquery 的暴露方法。
- 如果我们在浏览器环境有个库比较好用,但是它本身不支持 node,我们可以手动给源码增加 commonJs 的暴露方法