jQuery源码解读----part 1

来源:慕课网 https://www.imooc.com/video/4392

jQuery整体架构

53fa8fec0001754806930473

jQuery按我的理解分为五大块,选择器DOM操作事件AJAX动画, 那么为什么有13个模块?因为jQuery的设计中最喜欢的做的一件事,就是抽出共同的特性使之“模块化”,当然也是更贴近S.O.L.I.D五大原则的“单一职责SRP”了,遵守单一职责的好处是可以让我们很容易地来维护这个对象。


立即调用表达式

任何库与框架设计的第一个要点就是解决命名空间与变量污染的问题。jQuery就是利用了JavaScript函数作用域的特性,采用立即调用表达式包裹了自身的方法来解决这个问题。

写法一:

(function(window, factory) {
    factory(window)
}(this, function() {
    return function() {
       //jQuery的调用
    }
}))

写法二:(由写法一简化得)

var factory = function(){
    return function(){
        //执行方法
    }
}
var jQuery = factory();

上面的代码效果和方法1是等同的,但是这个factory有点变成了简单的工厂方法模式,需要自己调用,不像是一个单例的jQuery类,所以我们需要改成“自执行”,而不是另外调用。

写法三:

(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的保留字。


jQuery的类数组对象结构

首先我们看jQuery的入口都是统一的$, 通过传递参数的不同,实现了9种方法的重载:

  1. jQuery([selector,[context]])
  2. jQuery(element)
  3. jQuery(elementArray)
  4. jQuery(object)
  5. jQuery(jQuery object)
  6. jQuery(html,[ownerDocument])
  7. jQuery(html,[attributes])
  8. jQuery()
  9. jQuery(callback)

9种用法整体来说可以分三大块:选择器、dom的处理、dom加载。

换句话说jQuery就是为了获取DOM、操作DOM而存在的!所以为了更方便这些操作,让节点与实例对象通过一个桥梁给关联起来,jQuery内部就采用了一种叫“类数组对象”的方式作为存储结构,所以我们即可以像对象一样处理jQuery操作,也能像数组一样可以使用push、pop、shift、unshift、sort、each、map等类数组的方法操作jQuery对象了。

53fad4240001c7b805050236

从上图可看出,jQuery对象通过对象键值对的关系保存着属性,原型保存着方法。我们来简单的模拟一个这样的数据结构:

var aQuery = function (selector) {
    // 强制为对象 这里保证了必须是new操作符构建(太NB了这里)
    if (!(this instanceof aQuery)) {
        return new aQuery(selector);
    }
    
    // 个人对这里的正则抱有疑问,感觉 /(?<=#).*/ 比较好理解
    var elem = document.getElementById(/[^#].*/.exec(selector)[0]);
    
    // 把所有的属性与方法作为对象的key与value的方式给映射到this上,所以如上结构就可以模拟出jQuery的这样的操作了
    this.length = 1;
    this[0] = elem;
    this.context = document;
    this.selector = selector;
    this.get = function (num) {
        return this[num];
    }
    return this;
}

$("#test3").click(function () {
    $('#show3').append(aQuery("#book")[0])
})

以上是模拟jQuery的对象结构,通过aQuery方法抽象出了对象创建的具体过程,这也是软件工程领域中的广为人知的设计模式-工厂方法。


jQuery中ready与load事件

jQuery有3种针对文档加载的方法:

$(document).ready(function() {
    // ...代码...
})
// 上面方法的简写
$(function() {
    // ...代码...
})
$(document).load(function() {
    // ...代码...
})

ready和load谁先执行呢?答案是ready先执行,load后执行。原因请看下面DOM文档加载的步骤:

(1) 解析HTML结构。

(2) 加载外部脚本和样式表文件。

(3) 解析并执行脚本代码。

(4) 构造HTML DOM模型。 // ready

(5) 加载图片等外部文件。

(6) 页面加载完毕。 // load

ready意味着DOM结构的加载完成,此时为可以进行DOM操作的最早的时间点,对于代码来说应该越快加载越好,不需要等到图片资源都加载后才去处理框架的加载,图片资源过多load事件就会迟迟不会触发。

在jQuery种,针对高级的浏览器,使用DOMContentLoaded事件处理文档加载时机,省时省力。


jQuery多库共存处理

为了解决变量$与其他库框架或插件相冲突的情况,jQuery给出了noConflitct函数用于解决冲突的情况。

引入jQuery运行这个noConflict函数将变量$的控制权让给第一个实现它的那个库,确保jQuery不会与其他库的$对象发生冲突。
在运行这个函数后,就只能使用jQuery变量访问jQuery对象。例如,在要用到$("a")的地方,就必须换成jQuery("a"),因为$的控制权已经让出去了。

// 让出$控制权
jQuery.noConflict();
// 使用 jQuery
jQuery("aaron").show();
// 使用其他库的 $()
$("aaron").style.display = ‘block’;

这个函数必须在你导入jQuery文件之后,并且在导入另一个导致冲突的库之前使用。当然也应当在其他冲突的库被使用之前,除非jQuery是最后一个导入的。

实现的代码:

// 在暴露内部jQuery变量之前,先用私有属性将全局环境下旧的$和jQuery命名空间缓存起来
var _jQuery = window.jQuery,
    _$ = window.$;

jQuery.noConflict = function( deep ) {
    if ( window.$ === jQuery ) { // 判断当前$空间是否被jQuery接管,如果是则让出控制权给引用之前的环境
        window.$ = _$;
    }
if ( deep && window.jQuery === jQuery ) { // 若deep为true,则把jQuery的控制权也让出去了
        window.jQuery = _jQuery;
    }
    return jQuery;
};

猜你喜欢

转载自www.cnblogs.com/simpul/p/11027191.html