JQuery对象

来源:互联网 发布:胡夏 知乎 编辑:程序博客网 时间:2024/05/01 01:54

转载:
http://blog.sina.com.cn/s/blog_6fda308501012yhu.html

jQuery对象是什么,举个例子,$(‘#id’) 返回的就是jQuery对象,这个东西是整个jQuery的核心所在,所以我先来分析它。
var jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor ‘enhanced’
return new jQuery.fn.init( selector, context, rootjQuery );
};
……
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function( selector, context, rootjQuery ),
selector: “”,
jquery: “1.7.2”,
length: 0,
size: function(),
toArray: function(),
get: function( num ),
pushStack: function( elems, name, selector ),
each: function( callback, args ),
ready: function( fn ),
eq: function( i ),
first: function(),
last: function(),
slice: function(),
map: function( callback ),
end: function(),
push: push,
sort: [].sort,
splice: [].splice
};
jQuery.fn.init.prototype = jQuery.fn;

我敢说,第一次看到这段代码的人都会被它的结构搞晕,因为这种写法实在是太罕见了。关于john的想法,我斗胆一猜:

  1. jQ对象的构造函数为啥是protptype.init?
    答:构造函数中的逻辑和prototype的其他 属性/方法 有联系,比如 selector 和 length,写在一起比较易读。
    调用init 方法时,如果参数为空,jQ对象可以等同于 prototype,这样看起来结构很清晰;
    如果参数不为空,会覆盖一些 prototype 的属性(如 selector 和 length,也就说prototype 上的属性只是为了给一个默认值而已),还会创建一些 prototype 上没有列出的属性(如context),这些属性本该写在构造函数中的。但同样的,为了让逻辑保持紧凑,就一并写在 init 方法中。

  2. jQ到底想创建一个怎样的对象?
    答:这个对象有点像数组,比如下面这段代码:
    ?
    function FuckYou(who) {
    this[0] = who;
    }
    var fy = new FuckYou(‘john’);
    于是 fy[0] === ‘john’,是不是有点小晕了?千万搞清楚,这不是数组,别被你的眼睛蒙蔽了,这是一个对象,0是它的属性名而已。因为this.0 = who 会语法报错,所以就用了一个小技巧。

再看prototype 中的其他方法,如each, slice, map, push, sort, splice,无一不是在模拟数组。

  1. 解释一下pushStack呗?
    答:顾名思义,就是一个压栈操作,主要是用于undo

pushStack: function( elems, name, selector ) {
// 创建一个全新的jQ对象,API和prototype一模一样
var ret = this.constructor();

// elems 是数组,直接pushif ( jQuery.isArray( elems ) ) {    push.apply( ret, elems );// elems 是类数组,调用merge(),这存在两种情况:// 1. elems是childNodes这样的类数组// 2. elems是this[0], this[1]这样的模拟数组,上面举过例子的} else {    jQuery.merge( ret, elems );}// 如 $("p").find("span")// 第一次是匹配p,第二次是在上次的结果中匹配span// 类似这样破坏上一个链的行为,jQ都会把上一个链存起来,以便回退(调用end())ret.prevObject = this;ret.context = this.context;// 这小段很有意思,从这里你几乎可以看出整个jQ的设计思想// name 表示一个方法名// selector 表示 选择器// 你懂的,jQ里面有很多DOM相关的方法,比如after, find之类的,它们的参数就是一个选择器if ( name === "find" ) {    // find 相当于后代选择器,如 "div span" 这样的    ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;} else if ( name ) {    // 这个分支比较复杂,光看这里的代码是看不懂的    // 我就一直为什么要加 . 因为除了匹配class,是用不到 . 的    // 我觉得吧,这个应该涉及到 Sizzle 的匹配模式问题,我不关心这种细节问题,囧    ret.selector = this.selector + "." + name + "(" + selector + ")";}return ret;

}

  1. 再解释一下 init 呗?
    答: 必须解释啊,这个方法真是重中之重,jQ的构造函数不解释还能解释啥。
    init: function( selector, context, rootjQuery ) {
    var match, elem, ret, doc;

    // 处理 (""),(null), or $(undefined)
    if ( !selector ) {
    return this;
    }

    // 处理 $(DOMElement)
    if ( selector.nodeType ) {
    this.context = this[0] = selector;
    this.length = 1;
    return this;
    }

    // 因为 body 元素只有一个,所以可以优化一下
    if ( selector === “body” && !context && document.body ) {
    this.context = document;
    this[0] = document.body;
    this.selector = selector;
    this.length = 1;
    return this;
    }

    // 处理 HTML 字符串
    if ( typeof selector === “string” ) {
    // 如果是标签,如
    ,可省略第二个分支的正则匹配
    if ( selector.charAt(0) === “<” && selector.charAt( selector.length - 1 ) === “>” && selector.length >= 3 ) {
    match = [ null, selector, null ];

    } else {    // quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/    // 再来看早期的一个版本    // quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/    //    // 大同小异,但前者肯定是做了更多的考虑,    // 比如防止通过location.hash 进行XSS攻击    //    // 这个正则有点复杂,它分为两个分支    // 1. ^[^#<]*(<[\w\W]+>)[^>]*$    // 2. ^#([\w\-]*)$    // 第二个不用说,是匹配ID的    // 第一个用来匹配HTML代码段:    //   [^#<]* 表示开头不能包含#和<</span>    //   (<[\w\W]+>) 表示匹配完整的标签,如

    123
    // [^>]*$ 表示结尾不能包含>
    match = quickExpr.exec( selector );
    }

    // 匹配成功,并且匹配上ID时没有指定context// 为什么 ID 和context 不能同时指定?拜托,ID全局唯一的好不if ( match && (match[1] || !context) ) {    // 处理 $(html) -> $(array)    if ( match[1] ) {        // 确保 context 是一个 HTMLElement        context = context instanceof jQuery ? context[0] : context;        // 确保 doc 是 document        doc = ( context ? context.ownerDocument || context : document );        // rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/        // 就是匹配单个标签,如 或        // 如果匹配成功,不用往下走了,直接 createElement_x 就完事        ret = rsingleTag.exec( selector );        if ( ret ) {            // 我凌乱了,context 可能是纯对象么?            // 按第一个分支的逻辑,context应该是 {id: 'id', title: 'title'}之类的            if ( jQuery.isPlainObject( context ) ) {                selector = [ document.createElement_x( ret[1] ) ];                jQuery.fn.attr.call( selector, context, true );            } else {                selector = [ doc.createElement_x( ret[1] ) ];            }        } else {            // 不是单个标签,就创建文档碎片吧            ret = jQuery.buildFragment( [ match[1] ], [ doc ] );            selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;        }        return jQuery.merge( this, selector );    // 处理$("#id")    } else {        elem = document.getElementByIdx_x( match[2] );        // Check parentNode to catch when Blackberry 4.6 returns        // nodes that are no longer in the document #6963        // 囧,黑莓还有这种问题,我觉得移动开发就该另写一个库,不能把所有bugfix都写在jQ里        // 就像说,难道移动开发还要考虑IE678? 肯定不要啊,基本都支持HTML5了        // 既然这样,那又何必在这fix手机浏览器呢?john 您蛋疼了么        if ( elem && elem.parentNode ) {            // IE 和 Opera 调用 getElementById 会依据name返回,而不是ID,再囧            if ( elem.id !== match[2] ) {                return rootjQuery.find( selector );            }            // Otherwise, we inject the element directly into the jQuery object            this.length = 1;            this[0] = elem;        }        this.context = document;        this.selector = selector;        return this;    }// 处理 $(expr, $(...)),相当于 $(...).find(expr)// 这么传参数的人纯属蛋疼,jQ就是这样被搞大的// 反正我觉得接口就该定死,context只能传 HTMLElement 或 选择器// 搞得我现在觉得jQ的接口好像万能一样,啥都能传} else if ( !context || context.jquery ) {    return ( context || rootjQuery ).find( selector );// 处理 $(expr, context),也相当于$(context).find(expr)// 同样蛋疼的写法} else {    return this.constructor( context ).find( selector );}

    // 处理 $(function)
    // 就是文档加载完成时调用的函数
    } else if ( jQuery.isFunction( selector ) ) {
    return rootjQuery.ready( selector );
    }

    // selector 是一个jQ对象,我真心觉得除了蛋疼没有别的理由这么写了
    if ( selector.selector !== undefined ) {
    this.selector = selector.selector;
    this.context = selector.context;
    }

    // 把 selector 加到 this 的尾部
    // 因为能走到这,this已经是一个类数组了
    return jQuery.makeArray( selector, this );
    }

  2. 还有啥想说的不?
    答:最后提一点吧:
    ?
    jQuery.fn = jQuery.prototype = {…}
    jQuery.fn.init.prototype = jQuery.fn;
    为啥要这么写?比如第一行,为什么不直接赋给一个变量,而是赋给jQuery.fn?

  3. 赋给一个变量就只能在jQ这个文件内部用,而jQ是有强大的插件机制的,所以为了便于外部扩展,赋给jQuery.fn。当然不处理这部分也没事,您就多打几个字,但是jQ毕竟是一个超级流行的框架,能缩写的就自己缩了吧,省的别人去做这种无畏的劳动。

  4. 第二行的写法更加让人蛋疼。首先 init 是构造函数,还要我继续说?好吧,虽然我觉得这都是基础知识了。
    ?
    1
    2
    3
    4
    function jQuery() { }
    jQuery.prototype = {
    constructor: jQuery
    }
    如果jQ不写这一行,之后 obj instanceof jQuery 就永远为 false。

技巧:
1. 对象上的 属性/方法 会覆盖原型上对应的 属性/方法(说 override 可能好些);
2. 巧用数组的方法,如下:
?
1
2
3
4
5
6
7
8
9
10
11
var push = Array.prototype.push;

function ArrayLike() {
this[0] = 0;
this[1] = 1;
this.length = 2;
}

var al = new ArrayLike();
push.apply(al, [2, 3]);
console.log(al)
最后的结果是 al.length === 4,现在知道jQ对象为啥需要 length 属性了吧
分享到:
java枚举类型 | query .fn.fx是什么意思

2013-05-17 17:02浏览 6171评论(0)分类:Web前端相关推荐

评论
发表评论

您还没有登录,请您登录后再发表评论
铁布衫的博客
铁布衫

浏览: 178040 次性别: Icon_minigender_1来自: 北京

最近访客 更多访客>>
fzbook的博客
fzbook
brucehu8的博客
brucehu8
sunzy_1024的博客
sunzy_1024
chendongkun的博客
chendongkun
文章分类

全部博客 (108)spring (11)hibernate (13)struts (0)数据库 (4)开发工具 (0)服务器 (6)操作系统 (1)网络 (0)JS (12)web前端 (11)java (53)MongoDB (1)C... (1)

社区版块

我的资讯 (0)我的论坛 (0)我的问答 (1)

存档分类

2016-03 (1)2013-09 (1)2013-06 (15)更多存档...

最新评论

孙艳霞111:  java.lang.IllegalStateException: Argument [RedirectAttributes] is of type Modelllnyxxzj: 如果问题未解决可以看看http://blog.360chwl. ...Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Beau013810758: 楼主求帮助类 ReflectionUtils,Hibernat ...BaseDao可以这样设计rankx: 你好,楼主可否发一份 ReflectionUtils,Hibe ...BaseDao可以这样设计

声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。若作者同意转载,必须以超链接形式标明文章原始出处和作者。
© 2003-2016 ITeye.com. All rights reserved. [ 京ICP证110151号 京公网安备110105010620 ]

0 0
原创粉丝点击