jQuery对象
发布在读懂jQuery2014年8月20日view:7859
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

开篇先来讲述一下平时使用最多的jQuery对象到底是什么。(这里的jQuery对象指的是$(selector)返回的对象)

jQuery最擅长的就是DOM操作,也是其流行的原因之一,用$()方法选择要操作的DOM元素,然后进行各种操作。

以下面的代码为例:

    <ul>
        <li id="test">1</li>
        <li>2</li>
        <li>3</li>
    </ul>

    console.log($('li')[0].nodeName);    // 输出'li'
    console.log($('#test')[0].nodeName);    //输出'li'
    console.log($('li').length);    //输出'3'

无论是通过标签名选择多个元素还是通过ID选择唯一的元素,只要元素存在,上面的代码总能正确得输出’li’,而且length也是正确的。

这么看来,jQuery似乎返回了一个数组,到底是不是,我们可可以通过下面的方法检验:

Object.prototype.toString.call($('li'));    //输出"[object Object]" 而非  "[object String]"

所以,jQuery其实返回了一个对象,而非数组。

让我们再来看下这个对象的结构:

console.log('%O', $('li'));

q.fn.q.init[3]
|- 0: li#test
|- 1: li
|- 2: li
|- context: document
|- length: 3
|- prevObject: q.fn.q.init[1]
|- selector: 'li'

原来就是一个拥有length属性,并且属性名为数字的对象,而非数组,大家都亲切的称它为类数组对象 -_-! 。

好了,知道了这个对象的结构,我们来思考下为什么jQuery要这么干吧。网上找了一些资料,奈何没什么发现,以下是小Y个人对这样做的看法,如果大家发现有什么不对或更多的理解,请不吝指教:

首先,我们经常要操作的是一堆DOM元素,所以我们希望能像操作数组一样对多个DOM元素进行相同的操作,但是又需要在jQuery对象中放置如prevObject,selector等属性,所以不采用数组,而采用类数组对象的方式。

第二,jQuery为jQuery对象提供了很多工具方法,比如.val(),.each(),.html()等,这些都是jQuery对象的方法,如果采用数组的方式,可能需要对Array.prototype进行扩展,而对原生对象的扩展应该是需要尽量避免的。

第三,如果在jQuery对象中建立一个属性,名叫eles,其值为选中的DOM元素的数组呢?这样做在小Y看来是可以的。但是如果要获取选中DOM元素的个数,那么需要$(selector).eles.length,既不方便,也降低效率,因为需要多进行一层对象的读取。这样一来,每次遍历jQuery对象时,都要进行深一层的读取,更加降低效率。

既然jQuery对象是一个类数组对象,那么它能进行数组操作呢?让我们试试吧:

var items = $('li');
var lic = items.push({'name':'zack','age':30,'gender':'male'});

你可以发现,lic现在等于4,我们知道数组的push方法返回的是push以后数组的长度,那么items的length已经自动从3变成了4。再观察items,可以发现items多了一个items[6],其值就是我们刚才push进去的对象。

这么说来这个jQuery类数组对象还能直接使用数组的方法,这是为什么呢?刚开始小Y很疑惑,这怎么也可以,后来翻了翻代码,发现被自己逗了,jQuery的代码里有这么一段,为了演示,略有改动:

jQuery.fn = {
    push: Array.prototype.push;
}

下面的代码可以演示如何使用数组方法:

$('li').pop();    //TypeError: undefined is not a function
[].pop.call($('li'));    // It works. Chrome,火狐,IE10亲测可用,其他浏览器未知
Array.prototype.pop.call($('li'));    // It works too. Chrome,火狐,IE10亲测可用,其他浏览器未知

在网上翻了翻,找到了下面的解释

NOTE The push function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the push function can be applied successfully to a host object is implementation-dependent. push函数被有意的设计成通用的;它并不要求它的this属性是一个Array对象。因此,它可以被其他对象作为方法使用。push方法是否能成功的被其他宿主对象apply依赖浏览器的实现。

最后来说一下如何创建类数组对象。原先以为一个对象有length属性,属性名是从0开始的数组就可以,可是发现console里打印出来显示还是Object,而不是像jQuery对象的q.fn.q.init[n]。而且jQuery对象是new一个函数生成的,并不是对象字面量,它是如何实现的呢?

在网上扫了一圈,终于在SO上找到了答案,下面是我总结的方法:

  1. 一个对象有length属性
  2. 如果是对象字面量,它需要有个splice属性,值为函数;如果是函数,则函数的prototype对象要有splice属性,值为函数。这里的splice属性值可为任意函数,空函数也可以。
var foo  =function(){this.length=0}
foo.prototype.splice = function(){}
var a = new foo();

小广告:我的博客 #^_^#

评论
发表评论
暂无评论
WRITTEN BY
YuC_C
陈胜后裔吗?博客:lovecicy.com
TA的新浪微博
PUBLISHED IN
读懂jQuery

用了这么久jQuery了,想看看到底jQuery怎么写的,为什么这么牛呢。本栏解读的版本为1.8.1,本人(小Y)才疏学浅,如有疏漏、错误之处,还望各位大牛雅正。个人博客:YuuuuC

我的收藏