页面

2011年5月5日

read jquery.js

抽空读了读jquery代码,确实颇有收获。首先放上整体代码结构:

(function (window, undefined) {

var document = window.document;

var jQuery = (function() {

var jQuery =  function( selector, context ) {

return new jQuery.fn.init( selector, context, rootjQuery );

};

jQuery.fn = jQuery.prototype = {

constructor: jQuery,

init: function (selector, context, rootjQuery) {

// ...

if (selector.nodeType) {

this.context = this[0] = selector;

this.length = 1;

return this;

}

// ...

},

// ...

};

jQuery.fn.init.prototype = jQuery.fn;

jQuery.extend = jQuery.fn.extend = function() {

     // ...

};

jQuery.extend({

noConflict:function(){},

Ready:function(){},

// ...

});

// ...

return jQuery;

})();

jQuery.extend({

});

// ...

window.jQuery = window.$ = jQuery

})(window);


首先为何会有两层(function…)()自执行函数的疑问我觉得应该解释为为了更好的模块化开发,在github中有模块化的源码


看过《javasctipt权威指南》应该能更好的理解new语法到底为何物,也就自然能理解为何在jquery中new $()与执行$()结果一模一样


重点:

jQuery.prototype

=== jQuery.fn

=== jQuery.fn.init.prototype

逻辑上此处有矛盾,fn与fn.init.prototype形成了循环包含,但这也正体现了js为值对象语言的特征,一切皆为值拷贝(jQuery.fn.init.prototype = jQuery.fn)

PS:$.extend使用深度copy突破了值拷贝障碍,下节分析,目前$.extend的可读性并不好,充斥着魔鬼数字。


jquery.fn的成员:

  • init - 初始化
  • constructor - 手动指定构造函数,因为默认是 jQuery.fn.init
  • length - 让这个对象更接近一个原生的数组
  • size - 返回 length
  • toArray - 通过 Array.prototype slice 实现生成数组
  • get - 即 this[ num ] 参数索引的处理。
  • pushStack - 加入一个元素
  • ready – DOM文档加载后执行
  • end - 返回保存的 prevObject 对象
  • each - 循环执行

平常的 $("#id") 就是  new jQuery.fn.init("#id");

init函数中,返回值this附加上contextlength等参数后再附带上jQuery.fn.init.prototype(即jQuery.fn返回给调用者

以下为init函数注释代码:

init: function( selector, context, rootjQuery ) {

     // 参数:  selector  选择器

     //        context  上下文

     //        rootjQuery 父节点

 

     // 处理 $("") $(null) $(undefined)

     if ( !selector ) {

return this;

     }

 

     // 处理 $(DOMElement)

     if ( selector.nodeType ) {

         // 直接扔数组中, 就搞定了。

     this.context = this[0] = selector;

     this.length = 1;

     return this;

     }

 

     // 处理 $("body")   body 元素只存在一次,单独找它

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

            // 同样扔数组中, 顺便把 selector 更新更新。

        this.context = document;

        this[0] = document.body;

        this.selector = "body";

        this.length = 1;

       return this;

     }

 

     // 处理 $(HTML 代码 或者是 css 选择器) 

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

        

match = quickExpr.exec( selector );

// 检查是否正确匹配

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

// 处理: $(html) -> $(array)

if ( match[1] ) {

      

// 获取正文,默认 document

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

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

 

// 如果传入简单的  "<标签>"   ( /^<(\w+)\s*\/?>(?:<\/\1>)?$/)

ret = rsingleTag.exec( selector );

 

               // 返回 createElement("tag")

               return jQuery.merge( this, selector );

 

               // 处理: $("#id")

               } else {

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

                    // 因为有的浏览器 getElementById 不只返回 id匹配的,所以做检查。

                    return this;

                }

 

            // 处理 $("标签")

            } else if ( !context && !rnonword.test( selector ) ) {

                this.selector = selector;

                this.context = document;

                selector = document.getElementsByTagName( selector );

                return jQuery.merge( this, selector );

            //处理: $(选择器, $(...))

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

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

            // 处理: $(选择器,  上下文)

            // (相当于: $(上下文).find(选择器)

            } else {

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

            }

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

       //如果是函数,则执行$(document).ready,这样$(document).ready(func)简为$(func)

       return rootjQuery.ready( selector );

     }

  

   // 略。

 

   // 如果传入的是一个 Dom列表 ( getElementsByTagName 结果 ) 则转为 jQuery 数组。

   return jQuery.makeArray( selector, this );

}


$.extend({
    mm:
function () { alert('mm'
); }
});

$.mm();

$.fn.mm = function () {
    alert(
this
.text());
}

$.fn.extend({
    mm:
function () { alert(this
.text()); }
});

$('h2').mm();

23方案一模一样,13方案为同一个extend函数,1方案执行宿主会被extend自动判断为不带context$对象

var opts = $.extend({
val:
false

}, options);

返回俩参数合并后的对象,后者覆盖前者 // 可见extend函数有多复杂

贴一下$.extend函数代码:

jQuery.extend = jQuery.fn.extend = function() {

var target = arguments[0] || {}, // 第一个参数是目标

i = 1, length = arguments.length, deep = false, options;

if (target.constructor == Boolean) {// 第一个参数是bool型的

deep = target;// 深度copy

target = arguments[1] || {};// target指向第二个参数

i = 2;

}

// target string 型的或?

if (typeof target != "object" && typeof target != "function")

target = {};

if (length == i) {// 只有一个参数?或deep copy 时,两个参数

target = this;// 目标为this

--i;

}

for (;i < length; i++)

if ((options = arguments[i]) != null)

for (var name in options) {

var src = target[name], copy = options[name];

if (target === copy)// 防止死循环

continue;

// 深度copy处理,最深为元素

if (deep && copy && typeof copy == "object" && !copy.nodeType)

target[name] = jQuery.extend(deep, src

|| (copy.length != null ? [] : {}), copy);

else if (copy !== undefined)// 直接copy

target[name] = copy;

}

return target;

};

extend不但支持深度clone,还能支持多个参数的对象clone到一个指定对象,而不是jquery中。

没有评论:

发表评论