首页 > 代码库 > jQuery选择器源码分析和easyui核心分析

jQuery选择器源码分析和easyui核心分析

写在选择器源码分析之前

 

     这里指对1.7.2版本的源码分析,更高版本添加了更多代码。

 

     整个jQuery的代码是写在一个(function(window, undefined){})(window);这样一个闭包里。请思考,为什么要这样做?

     将其写在一个闭包函数里,并传入window直接运行的好处有三:

        1,统一命名空间,防止变量的污染;

    2,将window作为参数传入函数,在函数里调用window的时候,就不用再去找外层的对象,可以提高效率

       3undefined并不是javascript的关键字,使用参数undefined是为了防外面定义了全局的undefined变量而受到污染。

     jQuery的构造函数,其实返回的是一个jQuery.fn.init的对象。然后通过jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype来将jQuery的原型和jQuery.fn.init的原型联系起来。这样写看起来有点晕,但是正因为这样的联系,使得new jQuery()jQuery()都能返回一个jQuery对象(其实这里是jQuery.fn.init对象,但是由于原型都指向同一个对象,所以表面上可以无视这个问题)

    最后,jQuery对外提供的方法,末了都会将自身返回,以提供链式操作。

 

jQuery选择器源码分析

 

按选择器分类逐句分析

 

   //rootjQuery = jQuery(document);

   init: function (selector, context, rootjQuery) {

                var match, elem, ret, doc;

 

               // Handle $(""), $(null), or $(undefined) ->返回本身

                if (!selector) {

                    return this;

                }

 

                // Handle $(DOMElement) -> 如果是element ,就直接封装成jquery对象

                if (selector.nodeType) {

                    this.context = this[0] = selector;

                    this.length = 1;

                    return this;

                }

 

                //如果是body ->这里document.body作为条件,应该是为了兼容问题

                //可是今天我在主流浏览器里测试,都能够同时找到document.body

                //document.documentElement

                if (selector === "body" && !context && document.body) {

                    this.context = document;

                    this[0] = document.body;

                    this.selector = selector;

                    return this;

                }

 

                if (typeof selector === "string") {

                    //HTML string

                    if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {

 

                        //跳过检查

                        match = [null, selector, null];

                    } else {

                        //RegExpObject.exec(string)返回一个数组,其中存放匹配的结果。如      //果未找到匹配,则返回值为 null

     //quickExpr是检查HTML字符串或ID字符串的正则

                        match = quickExpr.exec(selector);

                    }

 

                    //验证match, match[1](这里存放是html)为非的时候context也必须为非

                    //(这种情况是#id)

                    if (match && (match[1] || !context)) {

 

                        // HANDLE: $(html) -> $(array)

                        if (match[1]) {

                            //这里的context其实可以理解成selectorparentNode或者parent()

                         //context ->DOM对象

                            context = context instanceof jQuery ? context[0] : context;

                            //如果制定了context,就返回context.ownerDocument(这里是

                            //context当前所属的document) || context,否则返回document

                            doc = (context ? context.ownerDocument || context : document);

 

                            //匹配成独立的标签(不含有属性之类,比如<a></a>

                            ret = rsingleTag.exec(selector);

 

                            if (ret) {

                                //方法jQuery.isPlainObject( object )用于判断传入的参数是否      //是“纯粹”的对象,即是否是用对象直接量{}new Object()创建                                 //的对象,如果是,那么context就是selector的属性

                                if (jQuery.isplainObject(context)) {

                                    selector = [document.createElement(ret[1])];

                                    jQuery.fn.attr.call(selector, context, true);

                                } else {

                                    selector = [doc.createElement(ret[1])];

                                }

                            } else {

                                //缓存selectorhtml

                                ret = jQuery.buildFragment([match[1]], [doc]);

                                //如果是缓存了的,就clone fragment(文档碎片节点在添加到文档树                      //之后便不能再对其进行操作),否则就直接取fragment

                          //childNodes

                                selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;

                            }

                            //selector合并到this,返回

                            return jQuery.merge(this, selector);

 

                            // HANDLE: $("#id")

                        } else {

                            elem = document.getElementById(match[2]);

 

                            if (elem && elem.parentNode) {

                                //处理 IE and Opera 混淆IDNAMEbug

                                if (elem.id !== match[2]) {

                                  //调用Sizzle的方法 -- TODO,关于Sizzle.js,有待研究!

                                    return rootjQuery.find(selector);

                                }

 

                                this.length = 1;

                                this[0] = elem;

                            }

 

                            this.context = document;

                            this.selector = selector;

                            return this;

                        }

 

                        // HANDLE: $(expr, $(...)) 

                    } else if (!context || context.jquery) {

                        return (context || rootjQuery).find(selector);

 

                        // HANDLE: $(expr, context)

                    } else {

                        return this.constructor(context).find(selector);

                    }

 

                    // HANDLE: $(function)    

                } else if (jQuery.isFunction(selector)) {

                    return rootjQuery.ready(selector);

                }

 

                //selector本身就是一个jQuery对象的情况

                if (selector.selector !== undefined) {

                    this.selector = selector.selector;

                    this.context = selector.context;

                }

 

                //合并属性(jQuery.merge不同的是,这里的selector可能不是数组)

                return jQuery.makeArray(selector, this);

            }

 

     

easyui核心分析

 

      Easyui控件是在jQuery.fn上扩展的,所以使用这些控件就如同使用jQuery方法一样方便(其实就是!)。精妙的地方:

1,创建控件和调用控件的方法,用法都和调用jQuery的方法一样,只在传参的区别。非常方便。

2,在某dom上创建的控件,需要添加或修改一些控件的属性时,和创建控件时用法也一样。

      

      以combobox为例,

 

$.fn.combobox = function(options, param){

 

//如果传入的第一个参数是string,则认为是调用控件的方法

if (typeof options == ‘string‘){

//查找对应的方法

var method = $.fn.combobox.methods[options];

if (method){

//存在对应的方法,则调用

return method(this, param);

} else {

//没有找到对应的方法,则调用$.fn.combobox(easyui

//的控件继承关系的原理)

return this.combo(options, param);

}

}

//传入的options不是string,则认为是创建combobox控件

options = options || {};

return this.each(function(){

//获取绑定在dom上的combobox对应的数据

var state = $.data(this, ‘combobox‘);

if (state){

//如果dom上有绑定combobox对应的数据,则与传入的

//options合并,重新创建控件

$.extend(state.options, options);

create(this);

} else {

state = $.data(this, ‘combobox‘, {

options: $.extend({}, $.fn.combo.defaults, $.fn.combobox.defaults, parseOptions(this), options)

});

create(this);

//重新绑定用到的其他的控件的对应数据到dom

loadData(this, transformData(this));

}

if (state.options.data){

loadData(this, state.options.data);

}

request(this);

});

};

 

 

关于$.data

   $.data可以将数据存在dom上,但又相比直接存放在HTMLElement的属性上要更安全,而且在dom的元素中添加过多的属性,对浏览器来说也是一种负荷。