JQ对象到底是什么

时间:2022-12-30 17:35:02

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,无一不是在模拟数组。

3. 解释一下pushStack呗?

答:顾名思义,就是一个压栈操作,主要是用于undo

pushStack: function( elems, name, selector ) {

// 创建一个全新的jQ对象,API和prototype一模一样

var ret = this.constructor();

// elems 是数组,直接push

if ( 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;

}

4. 再解释一下 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 );

}

5. 还有啥想说的不?

答:最后提一点吧:

?

jQuery.fn = jQuery.prototype = {...}

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

为啥要这么写?比如第一行,为什么不直接赋给一个变量,而是赋给jQuery.fn?

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

2. 第二行的写法更加让人蛋疼。首先 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 属性了吧

JQ对象到底是什么的更多相关文章

  1. Js(DOM) 和Jq 对象的相互转换

    JQuery 对象不能使用DOM对象中的方法,同样,Dom对象 不能使用JQuery 中的方法,但有时候 ,我们不得不使用JQuery的方法或者 Dom对象的方法,该怎么办呢? 下面介绍一下 jq对象 ...

  2. jq对象转为dom对象:&dollar;&lpar;&quot&semi;&period;div1&quot&semi;&rpar;&lbrack;0&rsqb; dom对象转为jq对象:&dollar;&lpar;dom对象&rpar;

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>& ...

  3. 一个Java对象到底占用多大内存?

    最近在读<深入理解Java虚拟机>,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好 ...

  4. 一个Java对象到底占用多大内存

    在网上搜到了一篇博客讲的非常好,里面提供的这个类也非常实用: import java.lang.instrument.Instrumentation; import java.lang.reflect ...

  5. 一个Java对象到底占多大内存

    最近在读<深入理解Java虚拟机>,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好 ...

  6. 一个Java对象到底占多大内存?(转)

    最近在读<深入理解Java虚拟机>,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好 ...

  7. js行内式遇到的一些问题 DOM对象和jq对象转换的问题

    这两天给后台页面做页面,我的工作比较简单,只需要写结构和样式就行了,写好之后,后端大哥用ajax重写页面加载数据,顺便给标签添加选中事件,做选中后变色的处理,但是却遇到一个问题,一直选不到触发事件这个 ...

  8. Jq对象与dom对象的互相转换&excl;

    JQ对象转化成dom对象 var a=$('div'); var b=a[0];//dom对象 转化成dom对象以后就可以使用dom方法了 dom对象转化成jq对象 var a=document.ge ...

  9. jquery的2&period;0&period;3版本源码系列&lpar;3&rpar;&colon;96行-283行,给JQ对象,添加一些方法和属性

    jquery是面向对象的程序,面向对象就离不开方法和属性. 方法的简化 jQuery.fn=jQuery.prototype={ jquery: 版本 constructor: 修正指向问题 init ...

随机推荐

  1. Service随系统启动运行

    Android系统启动时,会发出android.intent.action.BOOT_COMPLETED广播,定义一个类继承自BroadcastReceiver,监听该广播,并在收到该广播时启动Ser ...

  2. Install Redis on CentOS 6&period;4--转

    Install Redis on CentOS 6.4 source:http://thoughts.z-dev.org/2013/05/27/install-redis-on-centos-6-4/ ...

  3. 遇见NodeJS:JavaScript的贵人

    在大家的印象中,相当长一段时间里,JavaScript是一门脚本语言,一般不能成为某个项目的担纲主角,作用只是在浏览器里帮忙校验校验输入是不是正确,响应一下鼠标.键盘事件,或者让某个HTML元素动起来 ...

  4. Table表格的一些操作

    首先创建一个table表格: <input type="button" id="btn1" value="获取数据" /> &l ...

  5. 共享内存(shared memory)

    共享内存指在多处理器的计算机系统中,可以被不同*处理器(CPU)访问的大容量内存.由于多个CPU需要快速访问存储器,这样就要对存储器进行缓存(Cache). 任何一个缓存的数据被更新后,由于其他处理 ...

  6. Java操作Excel(使用POI)

    背景说明 以前写过使用 JXL 操作Excel的例子,但JXL对于Excel 2007版本以后的文件(即扩展名为 .xlsx)无法读取,也找不到可以支持的包.所以,有时不得不用 POI 来操作Exce ...

  7. 基于APNs最新HTTP&sol;2接口实现iOS的高性能消息推送&lpar;服务端篇&rpar;

    1.前言 本文要分享的消息推送指的是当iOS端APP被关闭或者处于后台时,还能收到消息/信息/指令的能力. 这种在APP处于后台或关闭情况下的消息推送能力,通常在以下场景下非常有用: 1)IM即时通讯 ...

  8. Maven 项目生成或者update jdk变为1&period;5的问题

    在使用Maven构建项目时,生成的maven项目jdk默认使用的是jdk1.5. 在手动修改了jdk之后,update project之后jdk又会变为1.5. 或者用eclipse的Maven插件生 ...

  9. Android Studio 上传GitHub项目失败后的一些问题

    在Android Studio上传项目到GitHub时候多上传了了一些项目,想删除,但是报诸如 Remote project is already on GitHub 一些乱七八糟的问题,而且,提示p ...

  10. java基础-day17

    第06天 集合 今日内容介绍 u  集合&迭代器 u  增强for & 泛型 u  常见数据结构 u  List子体系 第1章   集合&迭代器 1.1  集合体系结构 1.1 ...