jQuery 中对 CommonJs 的支持处理
jQuery 中对 CommonJs 提供了直接支持,可以在 CommonJs 模块中直接引用 jQuery 对象,这是如何实现的呢?
从 factory 函数说起
说先看 jQuery 的主体函数定义,这个函数用来返回我们定义的 jQuery 函数,所以它就是一个工厂函数 factory,在 jQuery 3.0.0 中,就是第 40 行开始,到 10037 行结束。
# 40 }( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
#10036 return jQuery;
#10037 } ) );
我们将这个函数抽取出来,简化一下,基本就是下面的样子。
# 40 function( window, noGlobal ) {
# 90 jQuery = function( selector, context ) {
# 95 };
#10031 if ( !noGlobal ) { #10032 window.jQuery = window.$ = jQuery; #10033 }
#10034
#10035 #10036 return jQuery; #10037 };
从函数定义可以看到,它需要两个参数,第一个参数就是 window,jQuery 是用来操作 document 的,document 又定义在 window 之上,jQuery 需要通过它来获取 document,以便进行进一步的 DOM 操作。第二个参数则表示是否存在全局对象,如果没有的话,则意味着 jQuery 直接运行在浏览器环境下,不是在 CommonJs 模块环境下,这样的话,就直接将 jQuery 对象挂接到全局的 window 对象上,以后,直接使用 window 来获取 jQuery 对象了。如果在 CommonJs 环境下,就不需要这样做了,因为可以通过 require 来获取 jQuery 对象,就没有必要挂接到 window 对象上了。
但是,在 #10037 行,我们并没有看到熟悉的直接执行圆括号,那么,什么时候调用了这个工厂函数呢?
注册函数
我们回头再来看开头的几行代码。
# 14 ( function( global, factory ) { # 15 # 16 "use strict"; # 17 # 18 if ( typeof module === "object" && typeof module.exports === "object" ) { # 19 # 20 // For CommonJS and CommonJS-like environments where a proper `window` # 21 // is present, execute the factory and get jQuery. # 22 // For environments that do not have a `window` with a `document` # 23 // (such as Node.js), expose a factory as module.exports. # 24 // This accentuates the need for the creation of a real `window`. # 25 // e.g. var jQuery = require("jquery")(window); # 26 // See ticket #14549 for more info. # 27 module.exports = global.document ? # 28 factory( global, true ) : # 29 function( w ) { # 30 if ( !w.document ) { # 31 throw new Error( "jQuery requires a window with a document" ); # 32 } # 33 return factory( w ); # 34 }; # 35 } else { # 36 factory( global ); # 37 }
# 38
# 39 // Pass this if window is not defined yet
# 40 }( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
这样,这个函数需要一个全局对象,和一个用来创建 jQuery 对象的工厂函数。
从 #14 到 #40 可以看到这是一个典型的直接执行函数定义,这个函数也需要两个参数,第一个就是我们所需要的 window 对象,第二个参数就是我们上面提到的用来创建 jQuery 函数的工厂函数,它已经在前面说过了,所以,我们先看一下第一个参数。
理论上讲,我们只需要将浏览器的 window 对象获取到就可以了,但是,还有一种情况是程序运行在 Node.Js 环境下,比如服务器环境下,这时候不是直接运行在浏览器环境下,也就不能直接获取 window 对象了,所以,这里检查了一下,当前是否有 window 对象,没有的化,就将 this 传进来。
#40 typeof window !== "undefined" ? window : this
在这个函数中需要做什么呢?
如果不是在 CommonJs 环境下,很简单了,直接执行工厂函数,让工厂函数将 jQuery 对象注册到 window 对象上,就可以了,这就是 #36 的作用,这里没有传递第二个参数,在 javascript 中,没有提供的参数,值就是 undefined,在用作 boolean 判断的时候,等同于 false。
如果在 CommonJs 环境下,将 jQuery 对象挂接到 module.exports 对象上,就完成任务了。
怎么知道在 CommJs 环境下呢?需要判断一下,#18 就是用来判断当前的运行环境的。
if ( typeof module === "object" && typeof module.exports === "object" ) {
CommJs 环境中会有一个 module 对象,这个对象上会有一个 exports 对象。
这样,#27, #28 也比较容易理解了,就是调用 factory 函数来创建出 jQuery 对象,将这个对象注册到 exports 对象上。这里的 noGlobal 参数传递了一个 true,就不会在 window 上再注册 jQuery 对象了。
#27 还有 global.document 是否存在的检查,别忘了,我们可能运行在非浏览器环境下。
在浏览器环境下,我们按照上述处理就可以了,如果在非浏览器环境下,就没有当前的 window 对象了,你可能自己创建了一个模拟浏览器的环境,有自己的 window 对象,这个时候的 jQuery 对象需要绑定到你的这个特殊的 window 对象上,#29 - #34 的函数定义了一个工厂函数,你需要将你的 window 对象传递进来,这个工厂函数再调用我们前面定义的工厂来创建一个绑定到指定 window 的 jQuery 函数供你使用。