菜鸟的jQuery源码学习笔记(二)

jQuery对象是使用构造函数和原型模式相结合的方式创建的。现在来看看jQuery的原型对象jQuery.prototype:

 jQuery.fn = jQuery.prototype = {
//成员变量和方法
}

这里给原型对象起了一个别名叫做jQuery.fn。要注意的是这个jQuery.fn可不是jQuery对象的属性,而是jQuery构造方法本身的属性,它是不会传给它所创建的对象的。如果你在控制台敲$().fn的话输出的结果会是undefined。接下来看看原型对象里面有些什么:

 jquery: "2.1.1",

 constructor: jQuery,

jquery:在原型对象中,先定义了jquery属性来标识版本信息,这里是2.1.1版。在我们写代码的时候可以通过查看这个属性判断对象是不是jQuery对象。

constructor:这个属性在低版本的jQuery代码中是没有的(例如远古的1.2.6版本)。在这里我能看见这个属性我真的觉得现在的jQuery成熟了。每一个对象都有一个constructor属性,是由创建它的构造函数的原型对象传递而来,指向了创建它的构造方法。也就是如果你在chrome的控制台敲:

 var obj = {};

 obj.constructor;

控制台会显示:

  function Object() { [native code] }

构造函数的原型是在定义构造函数的时候自动生成,其中的constructor会自动指向该构造函数。也就是你在输入:

 function Obj(){}

 Obj.prototype.constructor

控制台会输出:

  function Obj(){}

现在回过头我们再来看看jQuery的代码。定义jQuery.prototype时:

 jQuery.fn = jQuery.prototype = {
//成员变量和方法
}

这里的过程其实是新建一个对象,然后将jQuery.prototype指向这个新建的对象。而这时新建对象因为是由function Object(){}方法构建的。所以它的constructor属性其实是指向的function Object(){}方法。进而导致所有由jQuery方法创建的对象的constructor也是指向的

function Object(){}方法。这里重新定义jQuery.prototype.constructor属性,让它重新指向jQuery构造函数。平常我们在使用jQuery的时候也许永远也不会用到constructor这个属性,但是这种小细节的完善无疑让整个代码变得严谨很多。

继续往下看代码:

 // Start with an empty selector
selector: "", // The default length of a jQuery object is 0
length: 0,

selector:这个属性指的是默认的选择器。

length:jQuery对象其实是一个类数组对象。什么叫类数组对象?我的理解是它不是一个数组,而是一个对象但是当我们访问它的数据时某些表现形式跟数组很相似,比如可以用[0]、[1]....[n]来访问数据,可以用length来获取数据的数目。jQuery就是一种类数组对象通过选择器选择页面dom元素,并以jq[0]、jq[1]、jq[2]...、jq[n]的形式将个元素存储在jQuery对象中,这个length属性就是用来记录元素个数。

继续往下:

 var arr = [];

 var slice = arr.slice;

 toArray: function() {
return slice.call(this);
},

toArray:这个方法是将jQuery对象转化为一个数组(是真正的数组哦)。我们知道数组的

slice(start,end)

方法可从已有的数组中返回选定的元素而不破坏原有的数组,当参数不填的时候就返回这个数组的一个副本。因为jQuery对象是类数组对象,所以这里直接将数组arr中slice的对象上下文换成当前jQuery对象(不知道这个对象上下文能不能理解成作用域哈,不过this肯定是指的新对象啦)。slice函数执行后返回一个真正的js数组,里面的元素就是jQuery对象中的各个dom元素,但是又不破坏原来的jQuery对象,这一招真的挺不错,一行代码使用现有的资源解决了很多事情,这种思路要多学习。不过slice的调用我们还可以用

Array.prototype.slice.call(this)

这种方式调用也是可以的,不用特意新建一个对象。

往下:

 get: function(num) {
return num != null ? // Return just the one element from the set
(num < 0 ? this[num + this.length] : this[num]) : // Return all the elements in a clean array
slice.call(this);
},

get:这个方法感觉根据传参不同会有两个功能。一个是如果传参num不为空的话,那么根据num返回jQuery对象中的相应dom元素。另一个就是当num为空得时候将jQuery对象转换为数组。所以我一直很奇怪为啥有了get方法还要专门写一个toArray方法,希望有大大为我解惑。

咱们继续:

 pushStack: function(elems) {

     // Build a new jQuery matched element set
//jQuery.merge:合并两个数组的内容到第一个数组中
var ret = jQuery.merge(this.constructor(), elems); // Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context; // Return the newly-formed element set
return ret;
},

pushStack:大家都知道当我们使用jQuery选择区创建jQuery对象后,例如$(".class"),还可以使用jq.find、jq.filter等方法进一步处理,返回新的jQuery对象。但是这样往往会对旧的jQuery产生“破坏”性德影响,因为它们会修改匹配元素的集合(在jQuery中被称为matched element set)所以jQuery在进行这种“破坏性”的操作之前就会调用pushStack方法将现有的匹配元素集合暂时保存起来。

首先调用jQuery.merge方法,它将两个数组合并到第一个数组中。不过我觉得很有意思的是两个传参:

第一个参数this.constructor指的是jQuery构造方法原型对象中的constructor属性,这个前面说过。的this.constructor()指的是调用jQuery构造方法构建一个新对象,如同$()一样。因为jQuery是类数组对象所以也可以充当merge的传参。我觉得很炫酷的是这里创建新jQuery的写法,要是我本人的话我肯定会直接用jQuery()这么写了。

第二个参数elems就是pushStack传进来的参数了,它可以是真正的数组,也可以是类数组对象。通过merge方法,可以将elems转化为一个jQuery对象。

接下来就很简单了,将当前对象用ret.prevObject保存起来,值得一提的是这里还专门用了一个ret.context来保存当前jQuery对象的选择器上下文,就是使用选择器匹配元素时第二个参数,eg:$("div",document.getElementById("id"))。我查了一下w3c.school,context 属性在 jQuery version 1.10 中被弃用,但是高版本的jQuery中还能看到这个,所以我也不知道到底怎么回事。

不过既然说到jQuery.merge了那干脆来看看它吧:

 merge: function(first, second) {
var len = +second.length,
j = 0,
i = first.length; for (; j < len; j++) {
first[i++] = second[j];
} first.length = i; return first;
},

具体代码就不细说了~~~没什么技术含量,值得一提的是从merge的代码上看好像并没有去重的功能。这里的去重不仅仅是重复数字什么的,更包含重复的元素。比方说:

 var arr1 = [], arr2 = [];

 var ele = document.getElementById("id");

 arr1.push(ele);
arr2.push(ele); var arr3 = jQuery.merge(arr1, arr2);

运行后arr3.length将会是2虽然两个元素其实是同一个dom对象的引用。

上一篇:JVM-程序编译与代码晚期(运行期)优化


下一篇:菜鸟学习Fabric源码学习 — 背书节点和链码容器交互