jquery 源码分析「1」

243 阅读2分钟

本文分析的 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;
}

能收获什么

  1. 如果我们去实现一个工具库,想让其支持在各种环境下调用,就可以参考 jquery 的暴露方法。
  2. 如果我们在浏览器环境有个库比较好用,但是它本身不支持 node,我们可以手动给源码增加 commonJs 的暴露方法