第2章 构造jquery对象 8
-
jQuery对象是一个类数组对象,含有-
连续的整形属性
-
length属性 -
大量的
jQuery方法
-
-
2.1 构造函数
jquery()8jQuery很有意思的一点是,它的方法定义很强大,会根据不同的参数情况执行不同的功能。构造函数
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-
toArrayslice = 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-
.eqeq: 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-
.pushvar foo = $(document); foo.push(document.body); // 2 -
.sortvar 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,则一定是{}创建的对象 -
没有属性
isPropertyOfisPropertyOf是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); // '{ "a": 1 "b": 2 }'
-
-
-
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 )66dom元素的节点名称: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.browser76略
-
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对象上