您的位置:首页 > Web前端 > JQuery

《jQuery源码解析》读书笔记(第二章:构造jQuery对象)

2015-11-12 16:10 691 查看

第2章 构造jquery对象 8

jQuery
对象是一个类数组对象,含有

连续的整形属性

length
属性

大量的
jQuery
方法

2.1 构造函数
jquery()
8

jQuery
很有意思的一点是,它的方法定义很强大,会根据不同的参数情况执行不同的功能。

构造函数
jQuery()
有 7 种用法。

2.1.1 
jquery( selector [, context] )
9

用法

如果传入一个字符串参数,
jQuery
会检查这个字符串是选择器还是
HTML
代码

如果是选择器,则遍历文档

如果有匹配的元素,返回匹配的封装了匹配的
DOM
元素的
jQuery
对象

如果没有匹配的元素,则返回一个空的
jQuery
对象

2.1.2 
jquery( html [, ownerdocument] )、jquery( html, props )
9

用法

如果传入的参数看起来像一段
HTML
代码,那么
jQuery
会尝试创建新的
DOM
元素,并创建一个封装了此
DOM
元素的
jQuery
对象

第二个参数
ownerDocument
用于指定创建新
DOM
对象的文档对象,如果不传入,则默认为当前文档对象

如果
HTML
代码是单独一个标签,那么第二个参数还可以是
props
,是一个包含了属性、事件的普通对象,设置新创建元素的属性、事件

2.1.3 
jquery( element )、jquery( elementarray )
10

用法

传入一个
DOM
对象或
DOM
数组,然后封装这些
DOM
jQuery
对象

2.1.4 
jquery( object )
10

用法

传入一个普通
JavaScript
对象,把该对象封装到
jQuery
对象并返回

可以方便的实现自定义事件的绑定和触发

2.1.5 
jquery( callback )
11

用法

DOM ready
事件的回调函数

2.1.6 
jquery( jquery object )
11

用法

传入一个
jQuery
对象,则返回该对象的一个副本,这个副本与原
jQuery
对象引用相同的
DOM
元素

2.1.7 
jquery()
11

用法

不传入任何一个参数,则返回一个空的
jQuery
对象

可以用来复用
jQuery
对象,例如,创建一个空的
jQuery
对象,然后在需要的时候手动修改其中的元素,再调用
jQuery
方法,从而避免重复创建
jQuery
对象

2.2 总体结构 11

(function(window, undefined) {
// 构造 jQuery 对象
var jQuery = (function() {
var jQuery = function(selector, context) {
return new jQuery.fn.init(selector, context, rootjQuery);
};

// 一堆局部变量声明
// ...

jQuery.fn = jQuery.prototype = {
constructor: jQuery,
init: function(selector, context, rootjQuery) {

}

// 一堆原型属性和方法
};

jQuery.fn.init.prototype = jQuery.fn;

jQuery.extend = jQuery.fn.extend = function() {};

jQuery.extend({
// 一堆静态属性和方法
});

return jQuery;
})();

// 工具方法 Utilities
// 回调函数列表 Callbacks Object
// 异步队列 Defferred Object
// 浏览器功能测试 Support
// 数据缓存 Data
// 队列 Queue
// 属性操作 Attributes
// 事件系统 Events
// 选择器 Sizzle
// DOM遍历 Traversing
// 样式操作 CSS(计算样式、内联样式)
// 异步请求 Ajax
// 动画 Effects
// 坐标 Offset、尺寸 Dimensions

window.jQuery = window.$ = jQuery;
})(window);

以上内容,有几个要点:

jQuery
的定义部分,为什么要用自调用匿名函数包裹?

减少与其他模块的耦合,体现高内聚低耦合的思想

jQuery
jQuery.fn.init
prototype
都被覆盖了

jQuery
jQuery.fn.init
这两个方法的
prototype
都指向了
jQuery.prototype


好处是
jQuery
jQuery.prototype.init
的实例都可以访问构造函数
jQuery()
的原型属性和方法。

用图表示即:

new jQuery()                ---> jQuery.prototype
---->   同一个对象
new jQuery.prototype.init() ---> jQuery.prototype.init.prototype


为什么要增加一个函数
jQuery.fn.init


1、对返回对象的要求:该对象需要能访问
jQuery
原型上的方法和属性。

2、一般情况下返回的是
jQuery
的实例,但如果返回的代码是
return new jQuery()
,会形成死循环

3、因此构建一个新函数,该函数的实例也能访问
jQuery
原型上的方法和属性

为什么要在构造函数
jQuery()
内部用运算符
new
创建并返回另一个构造函数的实例?

创建一个实例的方式是
new
后面跟一个构造函数,如果构造函数有返回值,运算符
new
所创建的对象会被丢弃,返回值将作为
new
表达式的值

jQuery
利用了这一特性,省去了构造函数
jQuery()
之前的运算符
new


为了书写方便,为构造函数定义了别名
$


2.3 
jquery.fn.init( selector, context, rootjquery )
13

构造函数
jquery.fn.init()
负责解析参数
selector
context
的类型,并执行相应的逻辑,返回
jquery.fn.init()
实例。它有12个有效分支

.2.3.1 12个分支 13

2.3.2 源码分析 14

2.3.3 小结 21

2.4
jquery.buildfragment( args, nodes, scripts )
22

2.4.1 实现原理 22

2.4.2 源码分析 22

2.4.3 小结 26

2.5 
jquery.clean( elems, context, fragment, scripts )
27

2.5.1 实现原理 27

2.5.2 源码分析 27

2.5.3 小结 39

2.6 
jquery.extend()、jquery.fn.extend()
40

2.6.1 如何使用 40

二者是一样的功能:
jQuery.extend = jQuery.fn.extend = function()


也就是说,
jQuery
对象和
jQuery
的实例对象都有
extend
方法了

语法

jQuery.extend([deep],target,object1[,objectN])


jQuery.fn.extend([deep],target,object1[,objectN])


参数

deep


是可选的布尔值,表示是否进行深度合并(即递归合并),默认是不递归的,即后面参数的属性的会覆盖第一个参数的同名属性。如果是
true
,表示进行深度合并,合并过程是递归的。

target


目标对象

objectN


源对象,所有源对象的属性都会合并到目标对象

如果只有一个参数

那么参数
target
将被忽略,
jQuery
jQuery.fn
被当做目标对象,用这种方式可以在
jQuery
jQuery.fn
上添加新的属性和方法

2.6.2 源码分析 40

2.7 原型属性和方法 43

以下属性和方法均在下方代码块中定义

jQuery.fn = jQuery.prototype = {
原型属性和方法
}

也就是说,
jQuery
的实例有这些属性和方法

2.7.1 
.selector、.jquery、.length、.size()
44

源码

selector: '',

// 版本号
jquery: '1.7.1',

// jQuery 对象中元素的个数,初始化为 0
length: 0,

// 同 length,不建议使用
size: function() {
return this.length;
},


selector


介绍

当前
jQuery
对象的选择器,主要用来调试,与实际选择器不一定匹配

源码

`jquery`: 当前`jQuery`的版本号
`length`:jQuery 对象中元素的个数,初始化为 0
`size`:同 length,不建议使用,因为有函数调用开销


2.7.2 
.toarray()、.get( [index] )
45

toArray


slice = Array.prototype.slice

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

小技巧:借鸡下蛋

利用
Array.prototype.slice
方法将
jQuery
对象转为数组,形成方法
toArray
slice
方法里的
this
,可以是数组,也可以是类数组。

get


源码:
return num == null ? this.toArray() ? (num < 0 ? this[this.length + num] : this[num])


这样就可以支持: 1、参数为空;2、下标为负数。

2.7.3 
.each( function(index, element) )、jquery.each( collection, callback (indexinarray, valueofelement) )
46

.each


功能介绍

1、遍历当前
jQuery
对象,并在每个对象上执行回调函数

2、回调函数参数是索引(从
0
开始)和当前对象

3、最重要的一点是,回调函数中的
this
总是指向当前元素

4、在回调函数中返回
false
可以终止遍历

实现技巧

在源码中,它通过调用
jQuery.each
方法来实现,又一个借鸡生蛋的应用。

jQuery.each


源码在
627~666
行,就不贴在这里了

源码实现的总体思路

功能上,可以遍历(类)数组和对象(包括函数)

可以自定义回调函数的参数,如果不自定义,则默认给回调函数传
index, element
的参数

小技巧

判断是否是对象:
isObj == object.length === undefined || jQuery.isFunction(object)


遍历对象,用
for in


遍历数组,用
for
循环

执行、判断写在同一句

if(callback.call(object[i], i, object[i++]) === false){ break; }


break
可以跳出
for in
for
循环

2.7.4 
.map( callback(index, domelement) )、jquery.map( arrayorobject, callback(value, indexorkey) )
47

2.7.5 
.pushstack( elements, name, arguments )
49

2.7.6 
.end()
51

功能描述

核心代码:
return this.prevObject || this.constructor(null)


如果是:
$(ul.first).find('.foo').css(..).end().find('.bar')


2.7.7 
.eq( index )、.first()、.last()、.slice( start [, end] )
51

.eq


eq: function(i) {
i = +i;
return i === -1 ?
this.slice(i) :
this.slice(i, i + 1);
}

小技巧:如果
i
是字符串,把前面加上
+
可以把该参数转换为数值

2.7.8 
.push( value, ... )、.sort( [orderfunc] )、.splice( start,deletecount, value, ... )
52

.push


var foo = $(document);

foo.push(document.body); // 2


.sort


var foo = $([33, 4, 1111, 222]);

foo.sort(); // [1111, 222, 33, 4]

foo.sort(function(a, b) {
return a - b;
}); // [4, 33, 222, 1111]


2.7.9 小结 53

2.8 静态属性和方法 54

2.8.1 
jquery.noconflict( [removeall] )
55

看源码即可,绕来绕去的

2.8.2 类型检测:
jquery.isfunction( obj )
jquery.isarray( obj )
jquery.iswindow( obj )
jquery.isnumeric( value )
jquery.type( obj )
jquery.isplainobject( object )
jquery.isemptyobject( object )
56

判断类型

核心代码是用了
toString.call(obj)
判断,该表达式的值是:

[object Array]
[object Boolean]
[object Date]
[object Function]
[object Number]
[object Object]
[object RegExp]
[object String]


还有一个小技巧,循环数组:

('Boolean Date Function Number Object RegExp String').split(' ')


isWindow


判断方法是:
obj == obj.window
,以前的版本里是:
setInterval in obj


jquery.isplainobject( object )


功能

判断对象是否由
{}
new Object()
创建

返回
false
的情况

object
可以转为
false


Object.prototype.toString.call(object)
返回的不是
[object Object]


object
DOM
元素

object.nodeType
非空

object
window
对象

不是由
Object()
函数创建,而是由自定义函数创建的,返回
false


含有属性
constructor


是继承属性

是自身属性

如果不含属性
constructor
,则一定是
{}
创建的对象

没有属性
isPropertyOf


isPropertyOf
Object
原型对象的特有属性

执行时抛出异常。
IE 8/9
中,在某些浏览器对象上执行以上检测时会抛出异常,也应该返回
false


返回
true
的情况

Object.prototype.hasOwnProperty(property)
检测对象是否含有该非继承属性

如果该对象没有属性,或所有属性都是非继承属性,则返回
true


jquery.isemptyobject( object )


for in
遍历自身属性和继承属性

parseFloat(x)
可以解析字符串,并返回字符串的第一个数字;如果没有数字,则返回
NaN
;如果参数是对象,则自动调用该对象的方法
toString()
,得到该对象的字符串表示,然后再解析

2.8.3 解析
json
xml
jquery.parsejson( data )
jquery.parsexml( data )
60

jquery.parsejson( data )


解析思路

先尝试用
JSON.parse
解析,如果没有该方法,则用
(new Function('return' + data))()
解析

其余的是正则表达式匹配字符串之类的东西,在此略过

JSON.parse


解析
json
字符串为
json
对象

判断方法:
window.JSON && window.JSON.parse


JSON.stringify


解析
json
对象为
json
字符串

用法1:

JSON.stringify({a: 1, b: 2}); // '{"a": 1, "b": 2}'

用法2:

JSON.stringify({a: 1, b: 2}, function(key, value) {
if(key == '') return value;
if(key == 'a') return value * 10;
if(key == 'b') return undefined;
return value;
})

// '{"a": 10}'

用法3:

JSON.stringify({a: 1, b: 2}, ['b']); // '{"b": 2}'

用法4:

JSON.stringify({a: 1, b: 2}, null, 4); // '{\n    "a": 1\n    "b": 2\n}'


2.8.4 
jquery.globaleval( code )
65



2.8.5 
jquery.camelcase( string )
65

针对
-ms
单独做了处理,所有处理函数还是很简洁的

return string.replace(/^-ms-/, 'ms-').replace(/-[a-z]|[0-9]/, function(){...});


2.8.6 
jquery.nodename( elem, name )
66

dom
元素的节点名称:
dom.nodeName


2.8.7 
jquery.trim( str )
67

书中的思路很清晰,值得一提的是,这个方法里同样用了
String.prototype.trim()
方法来借鸡生蛋

另外,在
IE9
中,正则
/\s/
不能匹配不间断空格
\xA0
,但其也被认为是空格。测试是否不识别,用:
/\S/.test('\xA0')
,如果是
true
,则不能识别

2.8.8 数组操作方法:
jquery.makearray( obj )、jquery.inarray( value, array [, fromindex] )、jquery.merge( first, second )、jquery.grep( array, function(elementofarray, indexinarray) [, invert] )
68

jquery.makearray( obj )


学到了一点:
push
方法里的
this
可以指定为类数组

jquery.inarray( value, array [, fromindex] )


fromIndex
是查找的起点

如果浏览器支持数组方法
indexOf
,则直接调用,否则就自己写方法

数组的下标有可能是不连续的,所以需要用
i in array
来判断是否存在下标
i


值得学习的就是判断了

if($.inArray(element, array) > -1) {}

太繁琐,改进为

if(!!~$.inArray(element, array)) {}

~
按位取反,相当于改变符号并且减一,只有当
-1
时,
~(-1) == 0


!!
用来形成布尔值

jquery.merge( first, second )


这个方法的源码写的挺不错的,值得一看。

其中,把
second
认为两种对象处理:

1、数组/类数组(判断依据:有无整数/可转为整数的属性
length
,
typeof second.length === 'number'


2、含有连续整形(或可转换为连续整形)的对象,如
{0: 'a', 1: 'b'}
,从下标
0
开始遍历。

最后修正
first.length
,因为
first
不一定是真正的属性,需要手动维护
length
属性。

2.8.9 
jquery.guid、jquery.proxy( function, context )
72

jquery.proxy( function, context )


这个函数使得某个函数在任何环境下执行时,上下文都是
context
,很实用。

而且,它的实现原理是:1、闭包封存上下文
context
;2、返回一个新函数,新函数的作用域链上,有闭包内的上下文
context


其他的,看源码即可。

2.8.10 
jquery.access( elems, key, value, exec, fn( elem, key, value ), pass )
74

2.8.11 
jquery.error( message )
jquery.noop()
jquery.now()
75



2.8.12 浏览器嗅探:
jquery.uamatch( ua )
jquery.browser
76



2.8.13 小结 77

2.9 总结 77

到这一步,值得将
jQuery
的整体结构再回顾一遍了。代码概述在
11
页的图上。

(1)构造函数
jQuery()
有7种用法,根据参数的不同而不同。

(2)
jQuery.fn.init()
方法有
12
种用法,根据参数的不同而不同。

(2)原型属性和方法,直接放在
jQuery.fn = jQuery.prototype
对象上

(3)静态属性和方法,通过
jQuery.extend({...})
放在
jQuery
对象上
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: