继续咱们的jQuery源码解析。
(function(){
原文中此处为链接,暂不支持采集
(96 , 283) 给JQ对象,添加一些方法和属性
(285 , 347) extend : JQ的继承方法
(349 , 817) jQuery.extend() : 扩展一些工具方法
(877 , 2856) Sizzle : 复杂选择器的实现
(2880 , 3042) Callbacks : 回调对象 : 对函数的统一管理
(3043 , 3183) Deferred : 延迟对象 : 对异步的统一管理
(3184 , 3295) support : 功能检测
(3308 , 3652) data() : 数据缓存
(3653 , 3797) queue() : 队列方法 : 执行顺序的管理
(3803 , 4299) attr() prop() val() addClass()等 : 对元素属性的操作
(4300 , 5128) on() trigger() : 事件操作的相关方法
(5140 , 6057) DOM操作 : 添加 删除 获取 包装 DOM筛选
(6058 , 6620) css() : 样式的操作
(6621 , 7854) 提交的数据和ajax() : ajax() load() getJSON()
(7855 , 8584) animate() : 运动的方法
(8585 , 8792) offset() : 位置和尺寸的方法
(8804 , 8821) JQ支持模块化的模式
(8826) window.jQuery = window.$ = jQuery;
})();
第一部分,已经简单地分析完毕了,
当我分析完的代码,我都会在前面打'
原文中此处为链接,暂不支持采集
',说明咱们这一块已经读完,
大家也可以直接点击跳转相应的内容,
接下来,咱们来看第二块,
第二部分,差不多200行代码,
这一块儿是干嘛的呢?
这一块就是给jQuery添加一些常用的属性和方法,
其实我们知道jQuery就是基于面向对象的程序,
所以说面向对象就离不开来属性和方法,
这一块,我也给它进行了简化处理,
我们先来看一下这个简化的代码。
<script>
jQuery.fn = jQuery.prototype{ // 添加实例属性和方法
jQuery:版本
constructor:修正指向问题
init():初始化和参数管理
selector:存储选择字符串
length:this对象的长度
toArray():this对象的长度
get():转原生集合
pushStack():JQ对象的入栈
each():遍历集合
ready():DOM加载的接口
slice():集合的截取
first():集合的第一项
last():集合的最后一项
eq():集合的指定项
map():返回集合前一个状态
push():(内部使用)
sort():(内部使用)
splice():(内部使用)
}
</script>
这里面的方法都在后面加上了括号,
属性是不加括号的,
这些方法和属性大家看着有点比较眼熟吧,
那咱们也一个一个分析,从第一个看起,
先看一下这个版本,版本这个属性是什么?
jquery: core_version,
其实版本就是指向这个变量,
这个变量咱们前面看见过,
core_version= "2.0.3",
就是这个字符串,
所以咱们可以找到这个版本这个属性,
版本这个属性可能会在后面的代码上用到,
所以咱们先了解了解这个,
咱们来找一下这一个属性,
<!-- 咱们先引入jQuery库 -->
<script src="./jquery-2.0.3.js"></script>
<script>
// 很简单,我们想创建一个jQuery对象
// 然后再来找一下这一个属性,看一下现在的版本是多少
alert($().jquery);
</script>
弹出的结果如下:
这时的版本就是这个2.0.3这个版本。
OK,咱们在往下,看下面的这块,
constructor:修正指向问题
咱们来看下源码,就是这句话,
constructor: jQuery,
如果了解面向对象的小伙伴,
应该知道constructor是什么,
constructor在面向对象当中,
创建出来的对象所拥有的一个属性,
这个属性就指向这个对象所属的构造函数,
比如我们来简单的来写一写,
// 假如我在这里写一个构造函数
function Aaa(){
}
// 然后我们来创建一个对象
var a1 = new Aaa();
// a1下边就会自动生成constructor这个属性
alert(a1.constructor);
弹出来的结果如下:
可以发现这个属性弹出来的就是这个对象对应的构造函数,
所以说当写完这个函数之后,这个属性就已经生成了,
在JS源码当中,不是jQuery源码哈,
// 当一个函数创建完成后,就会在原型下添加constructor属性
// 指向的就是这个构造函数
Aaa.prototype.constructor = Aaa;
这句话是JS源码当中自动生成的,
所以当我们调用的时候是可以找到它的,
既然是自动生成的,
为什么jQuery还要手动地去指向一下呢?
原因是因为它这种写法是把指向改了,
咱们来看一下啊,
function Aaa(){
}
// 比如说我们现在不指向Aaa,指向数组
Aaa.prototype.constructor = Array;
var a1 = new Aaa();
alert(a1.constructor);
这样的话一弹出结果,
发没发现就跑到这个数组上面去了,
所以constructor这个属性非常容易会
被我们不经意之间就修改掉了,
所以说有些特殊情况需要修复一下,
比如,什么样的特殊情况,我们来看下,
function Aaa(){
}
// 比如我们给原型下添加两个属性
Aaa.prototype.name = 'Hello';
Aaa.prototype.age = 20;
var a1 = new Aaa();
alert(a1.constructor);
现在这样写完之后这是没有任何问题的,
而且我们知道原型本身就是对象,
既然它是对象,我们也可以改成简写方式,
Aaa.prototype = {
name:'Hello',
age:20
}
这两种写法其实是一样的,
在大部分的情况下都是没有区别的,
但是在面向对象当中呢,
这两种写法是有区别的,
我们先来看一下上边的写法,
弹出来的结果如下,没有任何问题,
接下来,我们看下面这种写法,
弹出的结果如下:
发没发现这个并不是Aaa了,
而是object这个对象构造函数,
这个是怎么回事呢?
上面这种写法这个name和age,
都是往原型上添加处理,
所以说默认添加,
这句话是不会受到任何影响的,
但是,下面这一种就不一样了,
这种写法不是添加,
而是对这个原型进行覆盖,
所以这时的constructor,
肯定是指向覆盖它的这个对象所对应的构造函数,
所以说指向就出现问题了,
既然指向出了问题了,我们平时用的时候,
还需要将它的这个指向修正过来,
要不然后期用的时候,
肯定是会找不到或出问题的,
所以说经常我们会去修正它,
就是让它指向正确给它修正一下,
Aaa.prototype = {
constructor:Aaa,
name:'Hello',
age:20
}
修正完了,再来看一下结果,
修正完了后是不是就正确了,
这时大家就知道了jQuery源码
为什么会写这一句话,
这就是解决这句话的一个原因,
下边我们来看一下init这个方法,
init():初始化和参数管理
前面我们都知道在对外提供接口的时候,
是不是只有 $ 和 jQuery 这两个,
$();
jQuery();
$ 就是jQuery的简写方式,
其实它们俩就是同一个函数,
最终都调到哪个函数?
前面我们也说过就是下面这个,
// 第60行开始
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
},
调到这个接口之后,
里面真正的构造函数,
其实是这个init,
所以说对外接口两个参数传过来的时候,
一个是元素,另一个是作用域,
这两个传过来之后,
它们都传到了init当中了,
还是在init当中进行处理的,
而第三个参数是没有提供给我们的,
只是它内部写的一个根节点,
其实在jQuery当中可以接收的类型非常之多,
这个init会对这些类型简单的分配,
然后再分别的进行处理,
// 107行
// HANDLE: $(""), $(null), $(undefined), $(false)
if ( !selector ) {
return this;
}
这一块就是对注释的进行处理,
其实当我们有的时候写一些不正确的选择元素的时候,
然后它去判断了字符串,
// 109行
// Handle HTML strings
if ( typeof selector === "string" ) {
if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
// Assume that strings that start and end with <> are HTML and skip the regex check
match = [ null, selector, null ];
} else {
match = rquickExpr.exec( selector );
}
既然是判断字符串的话,
那么说明,$选择的有id、class、标签,
或者一些更复杂的,
是不是这些都是字符串啊,
$('#box1');
$('.box2');
$('div');
$('#div div.box');
除了这种选择字符串的方法,
还有类似于如下的,
$('<li>');
咱们都知道这样写是创建一个li标签,
所以字符串不仅可以选择,还可以创建,
所以说这个字符串的类型也比较多,
比如还有一些稍微复杂一点的创建,
$('<li>1</li><li>2</li>');
其实都是走的这个字符串判断,
接下来它是对哪里进行处理的呢?
// 176行
// HANDLE: $(DOMElement)
} else if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
这一块的对DOM元素进行处理的,
// 比如选择的this
$(this);
// 或者document
$(document);
就会走这里。
接着往后,比如说下面这一块,
// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
}
这一块处理的是 $ 传函数的形式,
// 比如说加个函数
// 咱们平时用它做文档加载
// 所以传函数的形式该怎么处理
$(function(){
})
好,就是这样一个顺序,
下边就是下面这一块了,
return jQuery.makeArray( selector, this );
这一块就比较简单,
它处理的是数组和对象的形式。
所以我们简单把init框架写一下,
$(' '),$(null),$(undefined),$(false)
$('#div1'),$('.box'),$('div'),$('#div div.box')
$('<li>'),$('<li>1</li><li>2</li>')
第一块其实很简单,
就是当你写错的时候,
无非就是走if了,
让它直接返回,
就不让它继续往下执行了,
这个其实很简单,
程序也不会说有问题会报错啊,
或者是影响到下边的运行啊,
return 对象是很正常的,
这个对象就会在外面生成的。
OK,这一节咱先讲到这里
回看上一集:
原文中此处为链接,暂不支持采集
别走开,下集更精彩。
喜欢文章的小伙伴,
希望大家多多转发分享,
你的分享就是我的动力!
or