Learning open source frameworks, what children want to learn most is the design ideas and implementation skills. I recently studied the jQuery source code, recorded my understanding and experience of jQuery, and shared it with everyone, so I can use it as a starting point.
The overall jQuery framework is very complex and not easy to understand. I have been studying this heavy and powerful framework in the past few days. The overall architecture of jQuery can be divided into: entry module, bottom module and functional module. Here, we take jquery-1.7.1 as an example for analysis.
The overall architecture of jquery
The code is as follows:
16 (function( window, undefined ) { // 构造 jQuery 对象 22 var jQuery = (function() { 25 var jQuery = function( selector, context ) { 27 return new jQuery.fn.init( selector, context, rootjQuery ); 28 }, // 一堆局部变量声明 97 jQuery.fn = jQuery.prototype = { 98 constructor: jQuery, 99 init: function( selector, context, rootjQuery ) { ... }, // 一堆原型属性和方法 319 }; 322 jQuery.fn.init.prototype = jQuery.fn; 324 jQuery.extend = jQuery.fn.extend = function() { ... }; 388 jQuery.extend({ // 一堆静态属性和方法 892 }); 955 return jQuery; 957 })(); // 省略其他模块的代码 ... 9246 window.jQuery = window.$ = jQuery; 9266 })( window );
Analyzing the above code, we found that jquery adopts the anonymous function self-execution method, so The advantage is that it can effectively prevent namespace and variable pollution problems. Abbreviating the above code is:
The code is as follows:
(function(window, undefined) { var jQuery = function() {} // ... window.jQuery = window.$ = jQuery; })(window);
Parameter window
The anonymous function passes two Two parameters come in, one is window and the other is undefined. We know that variables in js have scope chains. The two variables passed in will become local variables of the anonymous function, and they will be accessed faster. By passing in the window object, the window object can be used as a local variable. Then, the parameters of the function also become local variables. When accessing the window object in jquery, there is no need to return the scope chain to the top-level scope, thus The window object can be accessed faster.
Parameter undefined
When js is looking for a variable, the js engine will first look for the variable in the scope of the function itself. If it is not found, it will continue to look up. , if found, the variable will be returned, if not found, undefined will be returned. undefined is a property of the window object. By passing in the undefined parameter without assigning a value, the scope chain when searching for undefined can be shortened. Within the scope of a self-calling anonymous function, ensure that undefined is truly undefined. Because undefined can be overwritten and given a new value.
What is jquery.fn?
The code is as follows:
jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function( selector, context, rootjQuery ) { ... }, // 一堆原型属性和方法 };
By analyzing the above code, we found that jQuery.fn is jQuery.prototype. The advantage of writing it this way is that it is shorter. Later, we saw that jquery simply used a $ symbol instead of jquery for simplicity. Therefore, when we use the jquery framework, we often use $(),
Constructor jQuery( )
Image description
jQuery objects are not created through new jQuery, but through new jQuery.fn.init:
The code is as follows:
var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); }
A variable jQuery is defined here, and its value is the jQuery constructor, which is returned and assigned to the jQuery variable on line 955 (the top code)
jQuery.fn.init
jQuery.fn (line 97 above) is the prototype object of the constructor jQuery(). jQuery.fn.init() is the jQuery prototype method, which can also be called the constructor. Responsible for parsing the types of parameters selector and context and performing corresponding searches.
Parameter context: You can pass in no jQuery object, DOM element, or one of the ordinary js objects
Parameter rootjQuery: jQuery object containing the document object, used for document.getElementById() search Failure, etc.
The code is as follows:
jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype jQuery(selector [,context])
By default, the search for matching elements starts from the root element document object, that is, the search scope is the entire document tree, but You can also pass in the second parameter context to limit its search scope. For example:
The code is as follows:
$('p.foo').click(function () { $('span',this).addClass('bar');//限定查找范围,即上面的context }); jQuery.extend()和jQuery.fn.extend()
The methods jQuery.extend(object) and jQuery.fn.extend(object) are used to merge two or more objects to the first object. The relevant source code is as follows (part):
The code is as follows:
jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone,//定义的一组局部变量 target = arguments[0] || {}, i = 1, length = arguments.length, deep = false;
jQuery.extend(object); Add a class method to the jQuery class, which is understandable Add static method for. For example:
The code is as follows:
$.extend({ add:function(a,b){returna+b;} });
Add a "static method" called add for jQuery, and then you can use it where jQuery is introduced. This is the method,
$.add(3,4); //return 7
jQuery.fn.extend(object), check out a code demonstration on the official website as follows:
The code is as follows:
<label><input type="checkbox" name="foo"> Foo</label> <label><input type="checkbox" name="bar"> Bar</label>
<script> jQuery.fn.extend({ check: function() { return this.each(function() { this.checked = true; }); }, uncheck: function() { return this.each(function() { this.checked = false; }); } }); // Use the newly created .check() method $( "input[type='checkbox']" ).check(); </script>
CSS selector engine Sizzle
It can be said that jQuery was born to operate DOM. The reason why jQuery is so powerful is Thanks to the CSS selector engine Sizzle, the parsing rules refer to an example on the Internet:
selector: "p > p + p.aaron input[type="checkbox"]"
解析规则:
1 按照从右到左
2 取出最后一个token 比如[type="checkbox"]
{
matches : Array[3]
type : "ATTR"
value : "[type="
checkbox "]"
}
3 过滤类型 如果type是 > + ~ 空 四种关系选择器中的一种,则跳过,在继续过滤
4 直到匹配到为 ID,CLASS,TAG 中一种 , 因为这样才能通过浏览器的接口索取
5 此时seed种子合集中就有值了,这样把刷选的条件给缩的很小了
6 如果匹配的seed的合集有多个就需要进一步的过滤了,修正选择器 selector: "p > p + p.aaron [type="checkbox"]"
7 OK,跳到一下阶段的编译函数
deferred对象
开发网站的过程中,我们经常遇到某些耗时很长的javascript操作。其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。
通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。
但是,在回调函数方面,jQuery的功能非常弱。为了改变这一点,jQuery开发团队就设计了deferred对象。
简单说,deferred对象就是jQuery的回调函数解决方案。在英语中,defer的意思是"延迟",所以deferred对象的含义就是"延迟"到未来某个点再执行。
回顾一下jQuery的ajax操作的传统写法:
代码如下:
$.ajax({ url: "test.html", success: function(){ alert("哈哈,成功了!"); }, error:function(){ alert("出错啦!"); } });
在上面的代码中,$.ajax()接受一个对象参数,这个对象包含两个方法:success方法指定操作成功后的回调函数,error方法指定操作失败后的回调函数。
$.ajax()操作完成后,如果使用的是低于1.5.0版本的jQuery,返回的是XHR对象,你没法进行链式操作;如果高于1.5.0版本,返回的是deferred对象,可以进行链式操作。
现在,新的写法是这样的:
代码如下:
$.ajax("test.html") .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出错啦!"); });
为多个操作指定回调函数
deferred对象的另一大好处,就是它允许你为多个事件指定一个回调函数,这是传统写法做不到的。
请看下面的代码,它用到了一个新的方法$.when():
代码如下:
$.when($.ajax("test1.html"), $.ajax("test2.html")) .done(function(){ alert("哈哈,成功了!"); }) .fail(function(){ alert("出错啦!"); });
这段代码的意思是,先执行两个操作$.ajax("test1.html")和$.ajax("test2.html"),如果都成功了,就运行done()指定的回调函数;如果有一个失败或都失败了,就执行fail()指定的回调函数。
jQuery.Deferred( func ) 的实现原理
内部维护了三个回调函数列表:成功回调函数列表、失败回调函数列表、消息回调函数列表,其他方法则围绕这三个列表进行操作和检测。
jQuery.Deferred( func ) 的源码结构:
代码如下:
jQuery.extend({ Deferred: function( func ) { // 成功回调函数列表 var doneList = jQuery.Callbacks( "once memory" ), // 失败回调函数列表 failList = jQuery.Callbacks( "once memory" ), // 消息回调函数列表 progressList = jQuery.Callbacks( "memory" ), // 初始状态 state = "pending", // 异步队列的只读副本 promise = { // done, fail, progress // state, isResolved, isRejected // then, always // pipe // promise }, // 异步队列 deferred = promise.promise({}), key; // 添加触发成功、失败、消息回调函列表的方法 for ( key in lists ) { deferred[ key ] = lists[ key ].fire; deferred[ key + "With" ] = lists[ key ].fireWith; } // 添加设置状态的回调函数 deferred.done( function() { state = "resolved"; }, failList.disable, progressList.lock ) .fail( function() { state = "rejected"; }, doneList.disable, progressList.lock ); // 如果传入函数参数 func,则执行。 if ( func ) { func.call( deferred, deferred ); } // 返回异步队列 deferred return deferred; }, }
jQuery.when( deferreds )
提供了基于一个或多个对象的状态来执行回调函数的功能,通常是基于具有异步事件的异步队列。
jQuery.when( deferreds ) 的用法
如果传入多个异步队列对象,方法 jQuery.when() 返回一个新的主异步队列对象的只读副本,只读副本将跟踪所传入的异步队列的最终状态。
一旦所有异步队列都变为成功状态,“主“异步队列的成功回调函数被调用;
如果其中一个异步队列变为失败状态,主异步队列的失败回调函数被调用。
代码如下:
/* 请求 '/when.do?method=when1' 返回 {"when":1} 请求 '/when.do?method=when2' 返回 {"when":2} 请求 '/when.do?method=when3' 返回 {"when":3} */ var whenDone = function(){ console.log( 'done', arguments ); }, whenFail = function(){ console.log( 'fail', arguments ); }; $.when( $.ajax( '/when.do?method=when1', { dataType: "json" } ), $.ajax( '/when.do?method=when2', { dataType: "json" } ), $.ajax( '/when.do?method=when3', { dataType: "json" } ) ).done( whenDone ).fail( whenFail );
图片描述
异步队列 Deferred
解耦异步任务和回调函数
为 ajax 模块、队列模块、ready 事件提供基础功能。
原型属性和方法
原型属性和方法源代码:
代码如下:
97 jQuery.fn = jQuery.prototype = { 98 constructor: jQuery, 99 init: function( selector, context, rootjQuery ) {} 210 selector: "", 213 jquery: "1.7.1", 216 length: 0, 219 size: function() {}, 223 toArray: function() {}, 229 get: function( num ) {}, 241 pushStack: function( elems, name, selector ) {}, 270 each: function( callback, args ) {}, 274 ready: function( fn ) {}, // 284 eq: function( i ) {}, 291 first: function() {}, 295 last: function() {}, 299 slice: function() {}, 304 map: function( callback ) {}, 310 end: function() {}, 316 push: push, 317 sort: [].sort, 318 splice: [].splice 319 };
属性selector用于记录jQuery查找和过滤DOM元素时的选择器表达式。
属性.length表示当前jquery对象中元素的个数。
方法.size()返回当前jquery对象中元素的个数,功能上等同于属性length,但应该优先使用length,因为他没有函数调用开销。
.size()源码如下:
代码如下:
size():function(){ return this.length; }
方法.toArray()将当前jQuery对象转换为真正的数组,转换后的数组包含了所有元素,其源码如下:
代码如下:
toArray: function() { return slice.call( this ); },
方法.get(index)返回当前jQuery对象中指定位置的元素,或包含了全部元素的数组。其源
码如下:
代码如下:
get: function( num ) { return num == null ? // Return a 'clean' array this.toArray() : // Return just the object ( num < 0 ? this[ this.length + num ] : this[ num ] ); },
如果没有传入参数,则调用.toArray()返回了包含有锁元素的数组;如果指定了参数index,则返回一个单独的元素,index从0开始计数,并且支持负数。
首先会判断num是否小于0,如果小于0,则用length+num重新计算下标,然后使用数组访问操作符([])获取指定位置的元素,这是支持下标为负数的一个小技巧;如果大于等于0,直接返回指定位置的元素。
eg()和get()使用详解:jquery常用方法及使用示例汇总
方法.each()用于遍历当前jQuery对象,并在每个元素上执行回调函数。方法.each()内部通过简单的调用静态方法jQuery.each()实现:
代码如下:
each: function( callback, args ) { return jQuery.each( this, callback, args ); },
回调函数是在当前元素为上下文的语境中触发的,即关键字this总是指向当前元素,在回调函数中return false 可以终止遍历。
方法.map()遍历当前jQuery对象,在每个元素上执行回调函数,并将回调函数的返回值放入一个新jQuery对象中。该方法常用于获取或设置DOM元素集合的值。
代码如下:
map: function( callback ) { return this.pushStack( jQuery.map(this, function( elem, i ) { return callback.call( elem, i, elem ); })); },
原型方法.pushStack()创建一个新的空jQuery对象,然后把DOM元素集合放入这个jQuery对象中,并保留对当前jQuery对象的引用。
原型方法.pushStack()是核心方法之一,它为以下方法提供支持:
jQuery对象遍历:.eq()、.first()、.last()、.slice()、.map()。
DOM查找、过滤:.find()、.not()、.filter()、.closest()、.add()、.andSelf()。
DOM遍历:.parent()、.parents()、.parentsUntil()、.next()、.prev()、.nextAll()、.prevAll()、.nextUnit()、.prevUnit()、.siblings()、.children()、.contents()。
DOM插入:jQuery.before()、jQuery.after()、jQuery.replaceWith()、.append()、.prepent()、.before()、.after()、.replaceWith()。
定义方法.push( elems, name, selector ),它接受3个参数:
参数elems:将放入新jQuery对象的元素数组(或类数组对象)。
参数name:产生元素数组elems的jQuery方法名。
参数selector:传给jQuery方法的参数,用于修正原型属性.selector。
方法.end()结束当前链条中最近的筛选操作,并将匹配元素还原为之前的状态
代码如下:
end: function() { return this.prevObject || this.constructor(null); },
返回前一个jQuery对象,如果属性prevObect不存在,则构建一个空的jQuery对象返回。方法.pushStack()用于入栈,方法.end()用于出栈
静态属性和方法
相关源码如下:
代码如下:
388 jQuery.extend({ 389 noConflict: function( deep ) {}, 402 isReady: false, 406 readyWait: 1, 409 holdReady: function( hold ) {}, 418 ready: function( wait ) {}, 444 bindReady: function() {}, 492 isFunction: function( obj ) {}, 496 isArray: Array.isArray || function( obj ) {}, 501 isWindow: function( obj ) {}, 505 isNumeric: function( obj ) {}, 509 type: function( obj ) {}, 515 isPlainObject: function( obj ) {}, 544 isEmptyObject: function( obj ) {}, 551 error: function( msg ) {}, 555 parseJSON: function( data ) {}, 581 parseXML: function( data ) {}, 601 noop: function() {}, 606 globalEval: function( data ) {}, 619 camelCase: function( string ) {}, 623 nodeName: function( elem, name ) {}, 628 each: function( object, callback, args ) {}, 669 trim: trim ? function( text ) {} : function( text ) {}, 684 makeArray: function( array, results ) {}, 702 inArray: function( elem, array, i ) {}, 724 merge: function( first, second ) {}, 744 grep: function( elems, callback, inv ) {}, 761 map: function( elems, callback, arg ) {}, 794 guid: 1, 798 proxy: function( fn, context ) {}, 825 access: function( elems, key, value, exec, fn, pass ) {}, 852 now: function() {}, 858 uaMatch: function( ua ) {}, 870 sub: function() {}, 891 browser: {} 892 });
未完待续、、、今天就先到这里了,下次补齐。别急哈小伙伴们
The above is the detailed content of Detailed examples of the overall architecture of jquery. For more information, please follow other related articles on the PHP Chinese website!