jQuerySourceCode

简介

虽然jQuery在近几年已经不再那么火热,伴随着新兴框架VUE/React等框架的崛起,jQuery的作用正在被弱化,传统的jQuery已经被新兴的框架化工程化所代替。但jQuery中的一些优秀的思想、特质,还是非常值得我们学习的。本文主要讲述这些优秀的思想。

jQ的版本和下载

jQuery的版本

1.x:兼容IE6~8浏览器,是目前PC端开发常用的类库

2.x/3.x:不支持IE6~8的兼容,目前市场上应用的特别少(移动端开发一般用zepto.js)

jquery-1.9.3.min.js
jquery-1.11.3.min.js
jquery.min.js

下载

官网下载:http://jquery.com/

Github:https://github.com/jquery/jquery

基于NODE的npm下载jQuery

1
2
3
4
5
6
7
8
//=>查jquery版本信息存放在jquery.version.json文件中
npm view jquery > jquery.version.json

//=> 下载指定版本的jquery
npm install jquery@1.11.3

//=> 下载最新版本
npm install jquery

如何学习jQuery

研究JQ的源码:研究的过程是学习JQ的精华,促进自己的原生JS能力以及插件或者类库的封装能力

看JQ的API手册: http://jquery.cuishifeng.cn/

锋利的JQ第二版:对于JQ的基础知识和实战应用讲解得非常好点击下载.zip

JQ的核心原理解读(分析源代码)

jQuery 是一个常用方法类库,提供了很多真是项目中需要使用的属性和方法(这些方法jQ已经帮我们完善了浏览器兼容处理以及一些细节的优化)

jQuery本身就是一个类(jQ是基于构造函数模式构建的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var jQuery = function(selector, context){
return new jQuery.fn.init( selector, context );
};

jQuery.fn = jQuery.prototype = {
jquery:version,
constructor: jQuery
...
};

...

init = jQuery.fn.init = function( selector, context ) {
if ( typeof selector === "string" ) {
...
} else if ( selector.nodeType ) {
...
} else if ( jQuery.isFunction( selector ) ) {
...
}

return jQuery.makeArray( selector, this ); //=> 返回一个类数组
}

init.prototype=jQuery.fn;

...

window.jQuery=window.$=jQuery;

当我们在js中执行

\$()

jQuery()

都是在创建一个jQ类的实例($===jQuery),这些实例都是一个类数组(我们把这个类数组称之为JQ对象),JQ的实例可以使用JQ原型上提供的共有属性和方法

项目中我们把\$()称之为JQ选择器,因为执行这个方法可以传递一个selector参数进去,通过selector我们可以获取到需要操作的DOM元素集合(JQ类数组集合),传递的第二个参数context它是当前获取元素的上下文(不传递默认是document,如果传递一个JS元素对象即可)

但是把它叫做JQ选择器有点笼统,因为传递的selector支持三种格式

传递的是个字符串,就是我们所谓的选择器,能够通过选择器获取元素

传递的是一个元素对象,它的意思是把JS原生对象转换为JQ对象

传递的是个函数,它代表等DOM结构加载完成在执行对应的JS代码(类似于window.onload)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ === jQuery; // true
$() === jQuery(); // false 不同实例
$() instanceof jQuery; // true
var $example = $(); //=> 我们用JQ选择器获取的值,一般都是用以$开头的变量名来存储(以后看见变量名是以$开头,我们就知道是JQ获取的实例【JQ对象】)

/*
* $example
* xxx:xxx 私有属性
* 0:某一个元素
* ...
* length:类数组长度
* context:document
* selector:传递的选择器内容
* prevObject:当前执行上下文的JQ对象
* __proto__: jQuery.prototype
* [xxx:xxx 共有的属性和方法]
* add
* addClass
* ...
* __proto__:Object.prototype
*/

第一个参数[selector]传递的是一个字符串,就是通过选择器获取到的元素集合(获取的结果都是类类数组集合;获取多个元素也就是索引多点,获取一个元素就只有索引0,一个都没获取到就是一个空的类数组集合【而不是null】)

一般css或者css3中支持的选择器,jq都支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//=> 基本选择器
$('.aa')
$('#aa')
$('div')
$('*')
$('.aa,#bb')

//=> 后代选择器
$('.box a')
$('.box>a')
$('.box~a')
$('.box+a')

//=> 伪类选择器
$('.box:contains(xxx)') //=> 包含xxx内容的
$('a:first')
$('a:last')
$('a:eq(1)') //=> 索引为1的
$('a:gt(1)') //=> 索引大于1
$('a:lt(10)') //=> 索引小于10
$('a:not()') //=> 不包含
$('a:not(:gt(5))')

jQ对象和原生js对象转换

JQ对象:通过$()获取的JQ实例(类数组)
原生JS对象:通过ES中提供的属性或者方法获取的JS元素对象(nodeType === 1)

把原生JS对象转换为JQ对象

1
2
3
4
5
6
var oBox = document.getElementById('box'); //=> 原生JS对象

oBox.addClass(); //=> 报错:oBox不是JQ对象,不能使用JQ原型上的方法

var $box = $(oBox); //=> 把原生JS对象转化为JQ对象
$box.addClass('bg');

把JQ对象转换为原生JS对象

1
2
3
4
var $body = $('body');
$body[0];
$body.get(0); //=> 获取到的是原生JS对象
$body.eq(0); //=> 获取结果还是一个JQ对象

selector是一个方法

1
2
3
4
5
6
7
8
$(function(){
//=> 当页面中的dom结构加载完成就会执行回调函数中的js代码
//=> 类似玉window.onload : 等到页面中的DOM元素以及资源文件都加载完成才会执行对应的js代码
});

$(document).ready(function(){
//=> 这种写法和上面的写法一模一样
});

和window.onload不太一样
1、$(function(){}) 可以在同一个页面使用多次,多次都生效(所以在使用jQuery完成代码的时候,我们一般都会把代码放在回调函数中;首先不仅是等到结构加载完在执行,而且还形成了一个闭包)
原理:利用了DOM二级事件绑定(可执行多次),监听的是DOMContentLoaded时间(DOM结构加载完成就会触发)

2、window.onload 本身就是资源都加载完成才会执行,使用的是DOM零级事件,在同一个页面中只能使用一次

1
2
3
4
window.onload = function(){};
window.onload = function(){};

//=> 只能留最后一个,最后一次赋值替换了原有的赋值

JQ既是一个类也是一个对象

jQuery.prototype上设置了很多的属性和方法,这些是供JQ实例(DOM集合或DOM元素)使用的属性和方法

addClass
css
removeClass
attr

jQuery也是一个普通的对象,在对象上也有一些自己的属性和方法(和实例没有任何关系),这些都是工具类的方法

ajax
isFunction
unique

jQuery.prototype

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$('#box').index(); //=> 获取当前元素的索引(是在自己兄弟元素中的索引,它有几个哥哥,索引就是几)

$('body').data(key,value); //=> data方法可以获取到在html结构上设置的data-xxx的自定义属性

$('#box').attr(); //=> 设置/批量修改/获取当前元素的内置/自定义属性 removeAttr

$('#box').prop(); //=> 和attr一样也是操作元素属性的,但是prop一般都是操作表单元素的内置或者自定义属性 removeProp

addClass: 增加样式类
removeClass: 移除样式类
toggleClass: 切换样式类
hasClass: 验证是否存在某个样式类名

$('#box').html([val]); //=> 不传val就是获取内容,传递val就是设置内容,等价于原生的innerHTML
$('input').val([val]); //=> 表单元素value值的操作(设置/获取)

css: 设置或者批量设置元素的样式(获取的结果没有去单位)

offset(); //=> 获取距离body的偏移
position() //=> 获取距离父级参照物的偏移
scrollTop/scrollLeft([val]); //=> 获取/设置当前元素卷去的高度/宽度
height/width([val]);
innerWidth/innerHeight: 等价于clientWidth/clientHieght
outerWidth/outerHeight: 等价于offsetWidth/offsetHieght

$('#box').on('click', function...); //=> jQuery中的事件绑定
...

写在对象上的方法

1
2
3
4
5
var j = $,noConflict(); //=> 如果当前项目中引入了两个类库,都是使用$操作的,为了防止$使用权的冲突,jQ做了一个处理,转让使用权;此处返回的j就是代表原始$的变量,以后可以使用j()执行

var j = $,noConflict(true); //=> 把jQuery和$使用权都转让给j(深度转让)

$.ajax(); 帮助我们放松ajax请求的

筛选方法

filter: 同级过滤

children: 子集过滤

find: 后代过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var $links = $('a');
$links.filter('.bg'); //=> 首先获取所有的a,在所有的a中把具备样式类bg的获取到

$('#box').children('a'); //=> 首先获取id为box的所有子元素,在所有子元素中筛选出标签为a的元素集合

$('#box').find('.bg'); //=> 首先或取代#box后代中所有的元素,在所有的元素中筛选出样式类名具备bg的元素集合 <=> $('#box .bg')

prev: 获取上一个哥哥
prevAll: 所有的哥哥
next: 下一个弟弟
nextAll: 所有的弟弟
siblings: 所有的兄弟
parent: 父元素
parents: 所有的祖先元素(一直到html为止)

each

JQ中的each有两种
1、 写在原型上的each: 遍历JQ对象中的每一项
2、 写在对象上的each: 工具方法,可以用来遍历数组、类书组、对象等
3、 内置的each其实也是调用原型上的each处理的,只不过JQ在处理的时候会内部自己调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$('a').addClass('select'); //=> 我们获取的a可能有很多个,执行一次addClass,相当于给每个获取的a都增加一个叫做select的样式类(JQ中大部分方法在执行的时候,都会把获取的JQ集合中的每一项调用each进行遍历,把需要操作的任务对每一个遍历一边的元素进行操作)

$('a').css('width'); //=> 获取的时候只返回第一个元素的样式(设置走内置的each批量处理,获取之处理第一个)

//=> 原型上的each
$('a').each(function(index, item){
//=> 床底参数的顺序和数组内置的forEach顺序相反,ary.forEach(function(item, index){...});
//=> 获取的a有多少个,回调函数被触发执行多少次;index当前遍历这一项的索引 item是当前遍历这一项的内容
//=> this -> item(原生JS对象)方法中的this是昂钱遍历的这一项
//=> $(this) 也是当前遍历这一项,当时属于JQ对象
})

//=> JQ对象上提供的工具方法: each
$.each([数组/类数组],function(index, item){
//=> this:item
});

$.each([对象],function(key, value){
//=> this:value
//=> jQ也是采用for in 循环来遍历对象的,这样的话就存在吧共有属性和方法遍历到的问题
if([对象].hasOwnProperty(key)){
//=> 私有
}
});

animate

JQ中提供了元素运动的动画库

stop: 结束当前元素正在运行的动画,继续执行下一个新的动画(一般我们实现动画,stop方法基本必然执行)

finish: 和stop类似,finish需要让元素立即运动到上一个动画的目标位置,从目标位置执行下一个动画,而stop是从上一个动画停止的位置执行下一个动画

animate([target],[duration],[effect],[callBack]):
[target] 对象
[duration] 时间
[effect] linear/ease/ease-in/ease-out/ease-in-out
[callBack] 回调函数,动画结束做的事情

快捷动画

show(1000/fast/slow)
hide
toggle

fadeIn
fadeOut
fadeToggle

slideUp
slideDown
slideToggle