天天看點

jQuery選擇器源碼分析和easyui核心分析

寫在選擇器源碼分析之前

     這裡指對1.7.2版本的源碼分析,更高版本添加了更多代碼。

     整個jquery的代碼是寫在一個(function(window, undefined){})(window);這樣一個閉包裡。請思考,為什麼要這樣做?

     将其寫在一個閉包函數裡,并傳入window直接運作的好處有三:

        1,統一命名空間,防止變量的污染;

    2,将window作為參數傳入函數,在函數裡調用window的時候,就不用再去找外層的對象,可以提高效率 ;

       3,undefined并不是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;

                //如果是body ->這裡document.body作為條件,應該是為了相容問題

                //可是今天我在主流浏覽器裡測試,都能夠同時找到document.body和

                //document.documentelement

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

                    this.context = document;

                    this[0] = document.body;

                    this.selector = selector;

                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其實可以了解成selector的parentnode或者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 {

                                //緩存selector的html。

                                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 混淆id與name的bug

                                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)

                        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);

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

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

});

//重新綁定用到的其他的控件的對應資料到dom上

loaddata(this, transformdata(this));

if (state.options.data){

loaddata(this, state.options.data);

request(this);

};

關于$.data

   $.data可以将資料存在dom上,但又相比直接存放在htmlelement的屬性上要更安全,而且在dom的元素中添加過多的屬性,對浏覽器來說也是一種負荷。