一.4-jQuery.prototype对象中的属性与方法(下)

上一篇写到了jQuery.prototype对象的init方法,因为init方法内容较多,因此弄了个上下篇

先列一下jQuery.prototype对象上的方法与属性吧

jQuery.fn = jQuery.prototype = {
    jquery: core_version, // 49行  core_version = "2.0.3"
    constructor: jQuery,
    init: function(){},
    selector: "",
    length: 0,  // 伪数组的元素个数
    toArray: function(){},
    get: function(){},
    pushStack: function(){},
    each: function(){},
    ready: function(){},
    slice: function(){},
    first: function(){},
    last: function(){},
    eq: function(){},
    map: function(){},
    end: function(){},
    push: core_push,   
    // 其实这个方法就是Array.prototype.push。这样可以方便压缩代码。见53行及47行
    sort: [].sort,
    splice: [].splice
}

selector属性, length 属性不用介绍了。

1. toArray

204行, 将jQuery伪数组转为数组

toArray: function () {
     return core_slice.call(this); // [].slice.call(this)
}

2. get

209-217行

有时候我们需要得到node节点,通过这个方法便可以得到相应的节点了

get: function (num) {
    return num == null ?

        // Return a ‘clean‘ array
        this.toArray() :

        // Return just the object
        (num < 0 ? this[this.length + num] : this[num]);
}

如果不传入参数或者传入null,就调用toArray方法,将jQuery伪数组转为数组

如果num为负数,就加上jQuery对象的length,这样就能转为对应的正数了

一般一个jQuery对象长这个样子

jQuery = {
    0: li, // 这里的li是node节点,请注意
    1: li,
    2: li,
    length: 3
    
}
// 其它属性就没写了

$("li").get(-1), 这时jQuery对象的长度是3,-1代表或者这个对象的最后一个node节点。-1+这个jQuery对象的长度 => -1+3 => 2。

3. pushStack

压栈操作,与end方法结合使用

想想这样的操作

$("li").eq(0).css("color", "red");

// 只有第一个li标签变红了,其它li标签没有变红。

$("li")后,jQuery对象会长这个样子

jQuery = {
    0: li, // 这里的li是node节点,请注意
    1: li,
    2: li,
    length: 3
}

eq(0)是返回对象的第一个元素(对象的形式)。

怎么只让第一个元素变红呢? 直接将其它元素去掉? 这个不太行吧,可能用户还需要操作其它元素呢?

比如要完成这样的操作,让第一个li标签变红,第二个标签变蓝,一行代码完成。

$("li").eq(0).css("color","red").end().eq(1).css("color", "blue")

这时候我们就需要使用pushStack了

先看看源代码的实现吧(221-232)

pushStack: function (elems) {
      // Build a new jQuery matched element set
   // this.constructor() 返回新的jQuery对象, elems是一个对象
      var ret = jQuery.merge(this.constructor(), elems);


      // Add the old object onto the stack (as a reference)
  // 保存之前的状态。在用户调用end方法时,该对象就会被返回
      ret.prevObject = this;
      ret.context = this.context;


      // Return the newly-formed element set
      return ret;
}

首先需要知道merge方法是干什么?(merge的用法已经在上篇说明了,详细的可以去上篇看)

merge接受两个参数(可以是数组或者伪数组),然后第一个参数会融合第二个参数的属性。因此第一个参数的值会被修改。第一个参数也会被返回。

 

为什么叫这个名字

当我们调用$("li")时,我们的操作(如css,html)都会落到栈顶的头上

一.4-jQuery.prototype对象中的属性与方法(下)

当我们调用 $("li").eq(0)时,会push一个新栈帧。这时我们的操作就只会落在第一个li标签身上了。

目前栈底会被上一层栈帧引用,通过prevObject引用

一.4-jQuery.prototype对象中的属性与方法(下)

当我们想要对非栈顶元素进行操作时,就可以调用end方法,每调用一次,栈就会被pop一次。

272-274行 end实现

end: function () {
    return this.prevObject || this.constructor(null);
}

看,end方法便直接返回prevObject,如果没有的话,就返回一个新的jQuery对象。

each方法是直接调用工具方法的,暂时不说(可以看出加强版的forEach循环,可以遍历伪数组)

ready方法涉及延迟对象的知识,讲到延迟对象时再回来讲这个。

 

4. slice方法

248-250行

slice: function () {
    return this.pushStack(core_slice.apply(this, arguments));
}
// 这里用了apply是因为我们可以需要接受参数

// 注意下 这里也调用了pushStack方法。
$("li").slice(1, -1) .css("color", "red");  // 可以观察下这里的效果

5. first, last, eq方法

252-262行

这三个方法其实是一起的。而且eq方法是主要的实现, first和last也是直接调用eq方法的。

$("li").first().css("color", "red"); // 让第一个li元素变红

$("li").eq(1).css("color", "red"); // 让第二个li元素变红

先讲讲eq方法的实现吧

eq: function (i) {
    var len = this.length,
        j = +i + (i < 0 ? len : 0); // 对负索引的处理
    return this.pushStack(j >= 0 && j < len ? [this[j]] : []);
// 索引的范围[0, len-1], 否则置一个空数组
}

看起来pushStack方法很重要,因此需要多多理解下pushStack方法的背后操作

 

first方法和last方法是直接调用eq方法的。

first: function () {
    return this.eq(0);
},

last: function () {
    return this.eq(-1);
}

6. map

266-270行

map: function (callback) {
    return this.pushStack(jQuery.map(this, function (elem, i) {
        return callback.call(elem, i, elem);
    }));
}

前面的eq,first,last,slice方法是通过索引来筛选对应的元素的。但有时候我们需要特别的筛选。如下

$("li").map(function(index, ele){
    if (ele.className === ‘default‘){
        return ele;
    }
})

这个将筛选li元素(li元素的class属性值必须是default),只有li元素的class属性名为default时,才会返回。

 

我们需要查看下jQuery.map工具方法

这个方法其实是一个加强版的Array.prototype.map方法,可以对伪对象进行相应操作。如果熟悉数组的map方法的话,应该比较容易。

因此我先说说数组的map方法吧

var ret = [1,2,3,4].map(function(ele, index,array){
   return ele*ele; 
})

console.log(ret); // [2,4,9,16] 
返回一个新数组,我们可以对原数组的元素进行特定的操作,回调函数返回的数值将自动成为新数组的元素

需要注意,jQuery.map工具方法与数组的map的不同点。

首先jQuery.map工具方法支持对伪数组进行操作(返回的是数组)。

其次jQuery.map会根据实际情况来返回新的数组。(因此新数组的长度与原数组的长度并不一致), 数组的map确是原数组长度与新数组长度一致

第三回调函数的参数并不同,jQuery.map回调函数的第一个参数是数组的索引,第二个参数是数组的元素,没有第三个参数(原数组)

  数组的map回调函数的第一个参数是数组的元素,第二个参数是数组的索引,第三个参数是原数组。

调用jQuery.map方法后会根据我们的回调函数来生成新数组,然后再调用pushStack方法形成新的栈帧。

相关推荐