2.3 jQuery架构
2.1节和2.2节从JavaScript角度演绎jQuery框架模型的实现过程。本节将从不同视角和层级解析jQuery框架的内部结构和逻辑设计。
2.3.1 jQuery结构变化概述
jQuery框架结构经历4个发展阶段,简单说明如下。
1.原始结构
在jQuery 1.1.3版本(2007年7月)及之前,jQuery框架没有封装,在全局作用域中直接定义jQuery类型。例如,在jQuery-1.1.3.js中,框架结构代码如下:
2.初步封装
从jQuery 1.1.4版本(2007年8月)开始,jQuery框架开始注意代码的封装问题,把jQuery源码全部放在一个自调用的匿名函数中。
(function(){ … })()
在匿名函数内直接通过window.jQuery和window.$暴露自己,对外开放jQuery的使用权。
3.完善封装
从jQuery 1.4.0版本(2010年1月)开始,jQuery框架开始完善封装的结构,注重隐私保护,确保内外变量的隔离。例如,在jQuery 1.4.0.js中,框架结构代码如下:
提示:从jQuery 1.3.0.js版本开始,jQuery框架初步成型,具有模块化的组织架构。也是从这个版本开始,jQuery把选择器模块独立出来,并命名为Sizzle。
4.高级封装
从jQuery 1.11版本和jQuery 2.1版本(2014年1月)开始,jQuery框架结构做了较大升级,主要目的是适应JavaScript环境的变化。
随着JavaScript语言的广泛应用,以及JavaScript编译环境的多样性,如Node.js的出现,JavaScript不仅仅在客户端浏览器中运行,还可能在服务器端,或者其他设备、环境中运行。而在非客户端浏览器中没有全局对象window,因此为了适应不同的环境,jQuery需要对框架结构进行重构。
例如,在jQuery 1.11.0.js中,框架结构代码如下:
关于这段结构代码的详细解析,请参考2.3.2节介绍。
2.3.2 jQuery新框架结构
下面以jQuery 3.2.1版本为例来说明jQuery新框架结构,主要代码如下:
提示:自从CommonJS和NodeJS两个项目出现,JavaScript作为本地编程语言开始流行。
CommonJS API定义了很多普通应用程序使用的API,主要指非浏览器的应用。它的终极目标是提供一个类似Python、Ruby和Java标准库,让用户可以使用JavaScript开发服务器端JavaScript应用程序、命令行工具、图形界面应用程序、混合应用程序(如Adobe AIR)。
CommonJS是一种规范,NodeJS是这种规范的实现,官网地址为http://www.commonjs. org/。两者关系类似于ECMAScript和JavaScript。
因此,jQuery不仅要考虑浏览器JavaScript编程,还要考虑其他本地应用程序编程。
下面解析上述代码。
第1步,可以看到,整个jQuery框架被置于一个匿名函数中,并自调用。
(function (global, factory) { … })(typeof window !== "undefined" ? window : this, function (window, noGlobal) { … });
匿名函数包含两个形参:global、factory。
global:定义全局对象。在浏览器中指代window对象,在NodeJS环境中指代this。
factory:设置工厂模式。仅指jQuery框架。
在调用匿名函数时,传入两个实参。
typeofwindow !== "undefined" ? window : this:使用typeof运算符检测当前环境中是否存在window对象。如果存在,则传入window对象;如果不存在,则传入this。
function (window, noGlobal) {}:一个函数直接量表达式,实际上就是jQuery库函数。
第2步,下面来分析一下外层匿名函数。
第3行代码:"use strict";,表示启动严格模式。
提示:除了正常运行模式外,ECMAscript 5新添加了第二种运行模式:严格模式。顾名思义,这种模式使得JavaScript在更严格的条件下运行,体现了JavaScript更合理、更安全、更严谨的发展方向,包括IE 10+在内的主流浏览器都已经支持它,许多大项目已经开始全面使用它。严格模式具有以下优势。
消除JavaScript语法的一些不合理、不严谨之处,减少一些怪异行为。
消除代码运行的一些不安全之处,保证代码运行的安全。
提高编译器效率,提高运行速度。
为未来新版本的JavaScript做好铺垫。
如果读者想了解在严格模式下JavaScript的语法和用法,请扫码阅读。
第3步,第5行为一个条件语句,检测在当前环境下是否支持module和module.exports对象。如果支持,则说明当前环境为NodeJS运行环境;如果不支持,则说明当前环境为浏览器运行环境。
if (typeof module === "object" && typeof module.exports === "object") {
提示:module管理是NodeJS中比较有特色的部分,而JavaScript没有模块系统,没有标准库,不能自动加载和安装依赖。
第4步,在NodeJS环境下,使用module.exports = global.document引入全局作用域下的document对象。如果引入成功,则调用factory(global, true),在NodeJS环境中安装jQuery库;如果引入失败,则在函数作用域中尝试引入。
第5步,在函数作用域中,默认参数w表示全局对象,再次尝试探测w.document是否存在。如果不存在,则抛出异常,警告jQuery库需要一个包含document的全局作用域。document表示一个DOM文档对象。
第6步,如果在函数作用域中探测到document,则返回factory(w)。也就是调用factory(w),安装jQuery库,并把特定的全局对象w传入jQuery安装函数。
第7步,如果在浏览器环境中,则直接调用factory(global),安装jQuery库,并把全局对象global传入jQuery安装函数。
} else { //兼容浏览器环境 factory(global); }
提示:这里的global表示window对象。
第8步,在jQuery安装函数中,包含两个参数:window、noGlobal。
function (window, noGlobal) { … }
第一个形参window表示全局对象,第二个形参表示一个标识变量,确定第一个参数是否为最高作用域。
第9步,当参数noGlobal为undefined,或者为false时,说明当前环境有全局对象window,可使用“window.jQuery = window.$ = jQuery;”开放jQuery访问权。如果为NodeJS环境,则没有window对象,通过“return jQuery;”返回jQuery控制权。