jQuery源码解析(1)—— jq基础、data缓存系统

闲话

jquery 的源码已经到了1.12.0 版本,据官网说1版本和2版本若无意外将不再更新,3版本将做一个架构上大的调整。但估计能兼容IE6-8的,也许这已经是最后的样子了。

我学习jq的时间很短,应该在1月,那时的版本还是1.11.3,通过看妙味课堂的公开课视频和文档里的所有api的注解学习。

源码则是最近些日子直接生啃,跳过了sizzle和文档处理的部分(待业狗压力大,工作以后再看),关注datareadyeventqueueDefferred(jq的promise编程)、ajaxanimation的处理,初看甚至有点恶心,耐着性子坚持却叹其精妙,在这里记录下来加深印象。

(本文采用 1.12.0 版本进行讲解,用 #number 来标注行号)

jQuery初始化

整体封装上,考虑了两点(写轮子的重要参考)

模块化支持:通过 noGlobal 变量来决定是否绑定到全局变量,返回值为jQuery
冲突避免:保存window.$ window.jQuery,可以调用noConflict 还原,返回jQuery对象。(noGlobal为true时不需要)

主体使用了如下结构,抽离 if 分支,只关注主体逻辑的书写

(function( a, fun ) {
   
    // 判断是否调用、如何调用fun
})(a, function( _a, _b ) {
   
    // 具体逻辑
});

/* ---- 区别于如下模式 ----*/
(function( _a, _b) {
   
    if (判断A) {
        // 调整参数或退出
    } else if (判断B) {
        // 调整参数或退出
    } ...
    // 这里开始时具体逻辑
})( a );

[源码]

(function( global, factory ) {
   

    if ( typeof module === "object" && typeof module.exports === "object" ) {
        module.exports = global.document ?
            factory( global, true ) :
            // 若无window,则模块存为函数,可取出通过传入window来调用一次
            // noGlobal为false,一定会污染全局
            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 ) {
   
    /* ---- jQuery具体逻辑 ----*/
    ...
    // 对AMD的支持,#10991
    if ( typeof define === "function" && define.amd ) {
        define( "jquery", [], function() {
   
            return jQuery;
        } );
    }
    // 保存之前的 window.$   window.jQuery
    var _jQuery = window.jQuery,
        _$ = window.$;

    jQuery.noConflict = function() {
   
        if ( window.$ === jQuery ) {
            window.$ = _$;
        }
        if ( deep && window.jQuery === jQuery ) {
            window.jQuery = _jQuery;
        }
        return jQuery;
    };
    // 模块化时,不设置全局
    if ( !noGlobal ) {
        window.jQuery = window.$ = jQuery;
    }
    return jQuery;
});


jQuery工厂结构

jq为了能有$.func、 $().func两种调用方法,选择了共享 jQuery.prototypereturn new jQuery.fn.init

这并不是唯一的方式(可以如下),之所以选择如此,个人认为应该是使用频率太高,这样每次可以省掉两次类型判断。而 jQuery.fn 我想也是起到简写、别名的作用

jQuery.extend/fn.extend 则决定了jq重要的插件扩展机制

var jQuery = function( selector, context ) {
   
    if ( this instanceof jQuery) {
        return new jQuery( selector, context );     
    }
    // 具体逻辑
}

[源码]

// #71
var jQuery = function( selector, context ) {
   
    return new jQuery.fn.init( selector, context );
};
// #91, 原型的别名 fn,省字符
jQuery.fn = jQuery.prototype = {
    ...
};
// #175, jQuery 扩展方法extend定义,只填一个参数表示对this进行扩展
jQuery.extend = jQuery.fn.extend = function(){
   
    ...
};
// #2866, 支持选择器,节点html,element,jq对象,函数
var init = jQuery.init = function( selector, context, root ) {
   
    ...
};
// #2982, 跟jQuery共享原型对象
init.prototype = jQuery.fn;


jQuery链式调用

精髓:通过 return this , return this.pushStack() , return this.prevObject 实现链式调用、增栈、回溯

[源码]

// # 122, 创建一层新的堆栈, 并引用 prevObject
pushStack: function( elems ) {
   
    // merge -> #433, 支持把数组、类数组的0-length项添加到第一个参数
    var ret = jQuery.merge( this.constructor(), elems );

    ret.prevObject = this;
    ret.context = this.context;

    return ret;
};

// #164, 可以回溯上一级 preObject
end: function() {
   
    return this.preObject || this.constructor();
}

// #3063, 添加到新层 this.constructor()
add: function( selector, context ) {
   
    return this.pushStack(
        // 去重排序
        jQuery.uniqueSort(
            // 合并到 this.get()
            jQuery.merge( this.get(), jQuery( selector, context ) )
        )
    );
},
addBack: function( sele
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值