JQuery源码解析(一)
jQuery使用户能更方便地处理DOM、事件、实现动画效果,并且方便地为网站提供AJAX交互。
2、jQuery的模块依赖网:
jQuery一共13个模块,模块不是单一的,比如jQuery动画,都会依赖异步队列、动画队列、回调队列与数据缓存模块等。
jQuery抽出了所有可复用的特性,分离出单一模块,通过组合的用法,不管在设计思路与实现手法上jQuery都是非常高明的。
五大块:选择器、DOM操作、事件、AJAX与动画
那么为什么有13个模块?因为jQuery的设计中最喜欢的做的一件事,就是抽出共同的特性使之“模块化”,
当然也是更贴近S.O.L.I.D五大原则的“单一职责SRP”了,遵守单一职责的好处是可以让我们很容易地来维护这个对象,
比如,当一个对象封装了很多职责的时候,一旦一个职责需要修改,势必会影响该对象的其它职责代码。
通过解耦可以让每个职责更加有弹性地变化。
我们来看看jQuery文档针对业务层的Ajax的处理提供了一系列的门面接口:
.ajaxComplete() .ajaxError() .ajaxSend() .ajaxStart() .ajaxStop() .ajaxSuccess()
底层接口:
jQuery.ajax() jQuery.ajaxSetup()
快捷方法:
jQuery.get() jQuery.getJSON() jQuery.getScript() jQuery.post()
jQuery接口的设计原理
业务逻辑是复杂多变的,jQuery的高层API数量非常多,而且也非常的细致,这样做可以更友好的便于开发者的操作,
不需要必须在一个接口上重载太多的动作。我们在深入内部看看Ajax的高层方法其实都是统一调用了一个静态的jQuery.ajax方法,
代码见右侧代码编辑器(27-43行)。
在jQuery.ajax的内部实现是非常复杂的,首先ajax要考虑异步的处理与回调的统一性,
所以就引入了异步队列模块(Deferred)与回调模块(Callbacks), 所以要把这些模块方法在ajax方法内部再次封装成、
构建出一个新的jQXHR对象,针对参数的默认处理,数据传输的格式化等等。
二:立即调用表达式
任何库与框架设计的第一个要点就是解决命名空间与变量污染的问题。
jQuery就是利用了JavaScript函数作用域的特性,采用立即调用表达式包裹了自身的方法来解决这个问题。
jQuery的立即调用函数表达式的写法有三种:
写法1:
(function(window, factory) { factory(window) }(this, function() { return function() { //jQuery的调用 } }))
可以看出上面的代码中嵌套了2个函数,而且把一个函数作为参数传递到另一个函数中并且执行,这种方法有点复杂,我们简化一下写法:
写法2:
var factory = function(){ return function(){ //执行方法 } } var jQuery = factory();
上面的代码效果和方法1是等同的,但是这个factory有点变成了简单的工厂方法模式,需要自己调用,不像是一个单例的jQuery类,所以我们需要改成“自执行”,而不是另外调用。
写法3:
(function(window, undefined) { var jQuery = function() {} // ... window.jQuery = window.$ = jQuery; })(window);
从上面的代码可看出,自动初始化这个函数,让其只构建一次。详细说一下这种写法的优势:
1、window和undefined都是为了减少变量查找所经过的scope作用域。当window通过传递给闭包内部之后,
在闭包内部使用它的时候,可以把它当成一个局部变量,显然比原先在window scope下查找的时候要快一些。
2、undefined也是同样的道理,其实这个undefined并不是JavaScript数据类型的undefined,而是一个普普通通的变量名。
只是因为没给它传递值,它的值就是undefined,undefined并不是JavaScript的保留字。
为什么要传递undefined?
Javascript 中的 undefined 并不是作为关键字,因此可以允许用户对其赋值。
var undefined = '孙丽媛' ;(function(window) { alert(undefined);//IE8 '孙丽媛' })(window)
IE8存在这个问题,当然,大部分浏览器都是不能被修改的
如果函数调用不传递,参数默认就是undefined
;(function(window,undefined) { //undefined })(window)
jQuery为什么要创建这样的一个外层包裹,其原理又是如何?
这里要区分2个概念一个是匿名函数,一个是自执行。顾名思义,匿名函数,就是没有函数名的函数,也就是不存在外部引用。但是是否像下面代码实现呢:
function(){ //代码逻辑 }
上面这种写法是错了,声明了它但是又不给名字又没有使用,所以在语法上错误的,那么怎么去执行一个匿名的函数呢?
要调用一个函数,我们必须要有方法定位它、引用它。所以,我们要取一个名字:
var jQuery = function(){ //代码逻辑 }
jQuery使用()将匿名函数括起来,然后后面再加一对小括号(包含参数列表),那么这小括号能把我们的表达式组合分块,
并且每一块(也就是每一对小括号),都有一个返回值。这个返回值实际上也就是小括号中表达式的返回值。
所以,当我们用一对小括号把匿名函数括起来的时候,实际上小括号返回的,就是一个匿名函数的Function对象。
因此,小括号对加上匿名函数就如同有名字的函数般被我们取得它的引用位置了。
所以如果在这个引用变量后面再加上参数列表,就会实现普通函数的调用形式。
最后,我们回到写法1看看jQuery利用写法3的写法,然后把整个函数作为参数传递给另外一个函数,
主要是为了判断jQuery在不同平台的下的加载逻辑,主流的库一般都有对 AMD 和 CommonJS 的支持代码,看看jQuery的代码:
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); }
总结:全局变量是魔鬼, 匿名函数可以有效的保证在页面上写入JavaScript,而不会造成全局变量的污染,
通过小括号,让其加载的时候立即初始化,这样就形成了一个单例模式的效果从而只会执行一次。