第五节:JQuery框架源码简析(1)

(转自老惠的博客

JQuery是一个应用广泛、非常优秀的JavaScript框架,其代码简洁而优雅,有很多值得我们学习的地方。这里仅仅对其代码结构做一个简单的分析,方便大家的理解和学习。

我们进行分析、分解的基准版本是jQuery1.7.1。

开始之前,请准备好以下素材和工具:

  1. jQuery源代码:jquery-1.7.1.js
  2. 文本编辑器:EditPlus,或者你喜欢的
  3. 参考书:《jQuery高级编程》、《jQuery技术内幕:深入解析jQuery架构设计与实现原理》、《JavaScript框架设计》

1、主体结构

 

jQuery的所有代码都在一个自调用匿名函数中,该函数构造一个jQuery对象,并初始化jQuery的各个模块,最后创建了$和jQuery命名空间,实现了框架代码和其他代码的隔离。

创建文件jquery.main.js,代码含义请看注释。

// 整体结构

(function(window,undefined){

var jQuery = (function(){

var jQuery = function(selector, context){

// 调用init方法创建jQuery对象

return new jQuery.prototype.init(selector, context,rootjQuery);

};

// 临时变量

var _$ = window.$;

var _jQuery = window.jQuery;

var rootjQuery;

var toString = Object.prototype.toString;

// 初始化jQuery对象

// 。。。

return jQuery;

})();

// 初始化jQuery各个模块

// 。。。

window.$ = window.jQuery = jQuery;

})(window);

创建测试文件jquery.test.html。

<!doctype html>

<html lang="en">

<head>

<meta charset="UTF-8">

<script src="jquery.main.js"></script>

<title>jquery demo</title>

</head>

<body>

<script>

alert($);

</script>

</body>

</html>

执行结果:

说明jQuery的命名空间$,或者jQuery(alert(jQuery))是个函数,该函数执行后,返回一个jQuery对象。现在我们执行测试代码$():

<!doctype html>

<html lang="en">

<head>

<meta charset="UTF-8">

<script src="jquery.main.js"></script>

<title>jquery demo</title>

</head>

<body>

<script>

$();

</script>

</body>

</html>

产生错误的原因是jQuery.prototype.init(selector, context,rootjQuery)方法不存在。

2、jQuery对象初始化

 

初始化代码定义了一些jQuery对象的原型属性和方法 。我们创建文件jquery.init.js,将jQuery初始化的代码放在这个文件中。

// 初始化

// 定义了一些jQuery对象的原型属性和方法

function jQuery_init(jQuery){

jQuery.fn = jQuery.prototype;

jQuery.fn.constructor = jQuery;

// Start with an empty selector

jQuery.fn.selector = "";

// The current version of jQuery being used

jQuery.fn.jquery = "1.7.1";

// The default length of a jQuery object is 0

jQuery.fn.length = 0;

jQuery.fn.init = init;

jQuery.fn.size = size;

jQuery.fn.toArray = toArray;

jQuery.fn.get = get;

jQuery.fn.pushStack = pushStack;

jQuery.fn.each = each;

jQuery.fn.ready = ready;

jQuery.fn.eq = eq;

jQuery.fn.first = first;

jQuery.fn.last = last;

jQuery.fn.slice = slice;

jQuery.fn.map = map;

jQuery.fn.end = end;

// For internal use only.

// Behaves like an Array‘s method, not like a jQuery method.

jQuery.fn.push = Array.prototype.push;

jQuery.fn.sort = [].sort;

jQuery.fn.splice = [].splice;

// jQuery对象初始化,并返回init子对象

function init( selector, context, rootjQuery ) {

// 检查selector是HTML字符串,还是#id的正则表达式

var quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;

// 检测 HTML 代码是否是单独标签

// 包含一个分组“ (\w+) ”,该分组中不包含左右尖括号、不能包含属性、

// 可以自关闭或不关闭;

// “ \1 ”指向匹配的第一个分组“ (\w+) ”

var rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/;

var match, elem, ret, doc;

// selector为空,比如$()

// 0、""、null、false、undefined、NaN 都会判定为 false ,而其他都为 true

if ( !selector ) {

return this;

}

//selector是 DOM 元素,比如$(document.body)

//selector如果是DOM元素,则包含属性nodeType

if ( selector.nodeType ) {

this.context = this[0] = selector;

this.length = 1;

return this;

}

// 文档树中只有一个body元素,所以要对body字符串查找优化

if ( selector === "body" && !context && document.body ) {

this.context = document;

this[0] = document.body;

this.selector = selector;

this.length = 1;

return this;

}

// selector是字符串

if ( typeof selector === "string" ) {

// selector是一个HTML字符串,还是#id

if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {

match = [null, selector, null];

}

else {

match = quickExpr.exec(selector);

// 正则 quickExpr 包含两个分组,依次匹配HTML代码和 id。

// 如果匹配成功,则数组match的第一个元素为参数selector,

// 第二个元素为匹配的 HTML代码或undefined,

// 第三个元素为匹配的id或undefined

}

// 这里面包含了对match[2]的判断,完整的表达式应该是

// if ( match && (match[1] || match[2] && !context) )

// 但是,如果match[1]不成立,那么match[2]必然成立,所以省略

if ( match && (match[1] || !context) ) {

// 处理参数 selector 是 HTML 代码的情况,先修正 context、 doc,

// 然后用正则 rsingleTag 检测 HTML 代码是否是单独标签,

// 匹配结果存放在数组 ret 中

if ( match[1] ) {

context = context instanceof jQuery ? context[0] : context;

doc = ( context ? context.ownerDocument || context : document );

// If a single string is passed in and it‘s a single tag

// just do a createElement and skip the rest

ret = rsingleTag.exec( selector );

if ( ret ) {

if ( jQuery.isPlainObject( context ) ) {

selector = [ document.createElement( ret[1] ) ];

jQuery.fn.attr.call( selector, context, true );

} else {

selector = [ doc.createElement( ret[1] ) ];

}

} else {

ret = jQuery.buildFragment( [ match[1] ], [ doc ] );

selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;

}

return jQuery.merge( this, selector );

// HANDLE: $("#id")

} else {

elem = document.getElementById( match[2] );

// Check parentNode to catch when Blackberry 4.6 returns

// nodes that are no longer in the document #6963

if ( elem && elem.parentNode ) {

// Handle the case where IE and Opera return items

// by name instead of ID

if ( elem.id !== match[2] ) {

return rootjQuery.find( selector );

}

// Otherwise, we inject the element directly into the jQuery object

this.length = 1;

this[0] = elem;

}

this.context = document;

this.selector = selector;

return this;

}

// HANDLE: $(expr, $(...))

} else if ( !context || context.jquery ) {

return ( context || rootjQuery ).find( selector );

// HANDLE: $(expr, context)

// (which is just equivalent to: $(context).find(expr)

} else {

return this.constructor( context ).find( selector );

}

// HANDLE: $(function)

// Shortcut for document ready

} else if ( jQuery.isFunction( selector ) ) {

return rootjQuery.ready( selector );

}

if ( selector.selector !== undefined ) {

this.selector = selector.selector;

this.context = selector.context;

}

return jQuery.makeArray( selector, this );

}

// The number of elements contained in the matched element set

function size() {

return this.length;

}

function toArray() {

return slice.call( this, 0 );

}

// Get the Nth element in the matched element set OR

// Get the whole matched element set as a clean array

function get( num ) {

return num == null ?

// Return a ‘clean‘ array

this.toArray() :

// Return just the object

( num < 0 ? this[ this.length + num ] : this[ num ] );

}

// Take an array of elements and push it onto the stack

// (returning the new matched element set)

function pushStack( elems, name, selector ) {

// Build a new jQuery matched element set

var ret = this.constructor();

if ( jQuery.isArray( elems ) ) {

Array.prototype.push.apply( ret, elems );

} else {

jQuery.merge( ret, elems );

}

// Add the old object onto the stack (as a reference)

ret.prevObject = this;

ret.context = this.context;

if ( name === "find" ) {

ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;

} else if ( name ) {

ret.selector = this.selector + "." + name + "(" + selector + ")";

}

// Return the newly-formed element set

return ret;

}

// Execute a callback for every element in the matched set.

// (You can seed the arguments with an array of args, but this is

// only used internally.)

function each( callback, args ) {

return jQuery.each( this, callback, args );

}

function ready( fn ) {

// Attach the listeners

jQuery.bindReady();

// Add the callback

readyList.add( fn );

return this;

}

function eq( i ) {

i = +i;

return i === -1 ?

this.slice( i ) :

this.slice( i, i + 1 );

}

function first(){

return this.eq( 0 );

}

function last() {

return this.eq( -1 );

}

function slice() {

var slice = Array.prototype.slice;

return this.pushStack( slice.apply( this, arguments ),

"slice", slice.call(arguments).join(",") );

}

function map( callback ) {

return this.pushStack( jQuery.map(this, function( elem, i ) {

return callback.call( elem, i, elem );

}));

}

function end() {

return this.prevObject || this.constructor(null);

}

// 这句代码很重要

// 让init子对象集成jQuery对象的属性和方法(通过原型链)

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

}

我们发现init方法返回的是init子对象,所以要有这句代码jQuery.fn.init.prototype = jQuery.fn,从而使init子对象和jQuery对象的属性和方法集成在一起。

修改文件jquery.main.js,添加对方法jQuery_init的调用。

修改jquery.test.html文件,添加对jquery.init.js的引用,并测试。

<!doctype html>

<html lang="en">

<head>

<meta charset="UTF-8">

<script src="jquery.init.js"></script>

<script src="jquery.main.js"></script>

<title>jquery demo</title>

</head>

<body>

<script>

var o = $();

alert(o.jquery);

</script>

</body>

</html>

测试结果说明,我们成功创建了jQuery对象,并显示了它的版本号属性:

 

3、extend方法定义

 

extend方法实现jQuery的功能扩展,用于合并两个或多个对象的静态属性和静态方法到第一个对象。我们把extend方法定义的代码放在文件jquery.extend.define.js中。

// extend方法定义,该方法实现功能扩展

// 用于合并两个或多个对象的属性和方法到第一个对象(静态属性和静态方法)

// 参数 [deep], target, object1 [, objectN]

// deep 是可选的布尔值,表示是否进行深度合并(即递归合并)。默认不递归。

// 参数 target 是目标对象;参数 object1 和 objectN 是源对象,包含了待合并的属性。如果

// 提供了两个或更多的对象,所有源对象的属性将会合并到目标对象;如果仅提供一个对象,

// 意味着参数 target 被忽略, jQuery 或 jQuery.fn 被当作目标对象,通过这种方式可以在 jQuery

// 或 jQuery.fn 上添加新的属性和方法, jQuery 的其他模块大都是这么实现的

function jQuery_extend_defined(jQuery){

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

var options, name, src, copy, copyIsArray, clone,

target = arguments[0] || {},

i = 1,

length = arguments.length,

deep = false;

// Handle a deep copy situation

if ( typeof target === "boolean" ) {

deep = target;

target = arguments[1] || {};

// skip the boolean and the target

i = 2;

}

// Handle case when target is a string or something (possible in deep copy)

if ( typeof target !== "object" && !jQuery.isFunction(target) ) {

target = {};

}

// extend jQuery itself if only one argument is passed

if ( length === i ) {

target = this;

--i;

}

for ( ; i < length; i++ ) {

// Only deal with non-null/undefined values

if ( (options = arguments[ i ]) != null ) {

// Extend the base object

for ( name in options ) {

src = target[ name ];

copy = options[ name ];

// Prevent never-ending loop

if ( target === copy ) {

continue;

}

// Recurse if we‘re merging plain objects or arrays

if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {

if ( copyIsArray ) {

copyIsArray = false;

clone = src && jQuery.isArray(src) ? src : [];

} else {

clone = src && jQuery.isPlainObject(src) ? src : {};

}

// Never move original objects, clone them

target[ name ] = jQuery.extend( deep, clone, copy );

// Don‘t bring in undefined values

} else if ( copy !== undefined ) {

target[ name ] = copy;

}

}

}

}

// Return the modified object

return target;

};

}

修改文件jquery.main.js,添加对方法jQuery_extend_defined的调用。

修改jquery.test.html文件,添加对jquery.extend.define.js的引用,并测试。

<!doctype html>

<html lang="en">

<head>

<meta charset="UTF-8">

<script src="jquery.init.js"></script>

<script src="jquery.extend.define.js"></script>

<script src="jquery.main.js"></script>

<title>jquery demo</title>

</head>

<body>

<script>

$.extend({

test_func: function() {

alert("扩展了一个测试方法");

return jQuery;

}

});

$.test_func();

</script>

</body>

</html>

我们发现通过jQuery对象的extend方法,我们成功地给jQuery对象扩展了一个测试方法:

 

4、核心方法

我们把jQuery对象核心方法定义的代码放在文件jquery.extend.core.js中,这些方法都是通过jQuery对象的extend方法实现的。

// jQuery对象的核心方法

function jQuery_extend_core(jQuery,_$,_jQuery,toString){

var trim = String.prototype.trim;

var class2type = {};

jQuery.extend({

noConflict: function( deep ) {

if ( window.$ === jQuery ) {

window.$ = _$;

}

if ( deep && window.jQuery === jQuery ) {

window.jQuery = _jQuery;

}

return jQuery;

},

// Is the DOM ready to be used? Set to true once it occurs.

isReady: false,

// A counter to track how many items to wait for before

// the ready event fires. See #6781

readyWait: 1,

// Hold (or release) the ready event

holdReady: function( hold ) {

if ( hold ) {

jQuery.readyWait++;

} else {

jQuery.ready( true );

}

},

// Handle when the DOM is ready

ready: function( wait ) {

// Either a released hold or an DOMready/load event and not yet ready

if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {

// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).

if ( !document.body ) {

return setTimeout( jQuery.ready, 1 );

}

// Remember that the DOM is ready

jQuery.isReady = true;

// If a normal DOM Ready event fired, decrement, and wait if need be

if ( wait !== true && --jQuery.readyWait > 0 ) {

return;

}

// If there are functions bound, to execute

readyList.fireWith( document, [ jQuery ] );

// Trigger any bound ready events

if ( jQuery.fn.trigger ) {

jQuery( document ).trigger( "ready" ).off( "ready" );

}

}

},

bindReady: function() {

if ( readyList ) {

return;

}

readyList = jQuery.Callbacks( "once memory" );

// Catch cases where $(document).ready() is called after the

// browser event has already occurred.

if ( document.readyState === "complete" ) {

// Handle it asynchronously to allow scripts the opportunity to delay ready

return setTimeout( jQuery.ready, 1 );

}

// Mozilla, Opera and webkit nightlies currently support this event

if ( document.addEventListener ) {

// Use the handy event callback

document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );

// A fallback to window. that will always work

window.addEventListener( "load", jQuery.ready, false );

// If IE event model is used

} else if ( document.attachEvent ) {

// ensure firing before onload,

// maybe late but safe also for iframes

document.attachEvent( "onreadystatechange", DOMContentLoaded );

// A fallback to window. that will always work

window.attachEvent( " jQuery.ready );

// If IE and not a frame

// continually check to see if the document is ready

var toplevel = false;

try {

toplevel = window.frameElement == null;

} catch(e) {}

if ( document.documentElement.doScroll && toplevel ) {

doScrollCheck();

}

}

},

// See test/unit/core.js for details concerning isFunction.

// Since version 1.3, DOM methods and functions like alert

// aren‘t supported. They return false on IE (#2968).

isFunction: function( obj ) {

return jQuery.type(obj) === "function";

},

isArray: Array.isArray || function( obj ) {

return jQuery.type(obj) === "array";

},

// A crude way of determining if an object is a window

isWindow: function( obj ) {

return obj && typeof obj === "object" && "setInterval" in obj;

},

isNumeric: function( obj ) {

return !isNaN( parseFloat(obj) ) && isFinite( obj );

},

type: function( obj ) {

return obj == null ?

String( obj ) :

class2type[ toString.call(obj) ] || "object";

},

isPlainObject: function( obj ) {

// Must be an Object.

// Because of IE, we also have to check the presence of the constructor property.

// Make sure that DOM nodes and window objects don‘t pass through, as well

if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {

return false;

}

try {

// Not own constructor property must be Object

if ( obj.constructor &&

!hasOwn.call(obj, "constructor") &&

!hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {

return false;

}

} catch ( e ) {

// IE8,9 Will throw exceptions on certain host objects #9897

return false;

}

// Own properties are enumerated firstly, so to speed up,

// if last one is own, then all properties are own.

var key;

for ( key in obj ) {}

return key === undefined || hasOwn.call( obj, key );

},

isEmptyObject: function( obj ) {

for ( var name in obj ) {

return false;

}

return true;

},

error: function( msg ) {

throw new Error( msg );

},

parseJSON: function( data ) {

if ( typeof data !== "string" || !data ) {

return null;

}

// Make sure leading/trailing whitespace is removed (IE can‘t handle it)

data = jQuery.trim( data );

// Attempt to parse using the native JSON parser first

if ( window.JSON && window.JSON.parse ) {

return window.JSON.parse( data );

}

// Make sure the incoming data is actual JSON

// Logic borrowed from http://json.org/json2.js

if ( rvalidchars.test( data.replace( rvalidescape, "@" )

.replace( rvalidtokens, "]" )

.replace( rvalidbraces, "")) ) {

return ( new Function( "return " + data ) )();

}

jQuery.error( "Invalid JSON: " + data );

},

// Cross-browser xml parsing

parseXML: function( data ) {

var xml, tmp;

try {

if ( window.DOMParser ) { // Standard

tmp = new DOMParser();

xml = tmp.parseFromString( data , "text/xml" );

} else { // IE

xml = new ActiveXObject( "Microsoft.XMLDOM" );

xml.async = "false";

xml.loadXML( data );

}

} catch( e ) {

xml = undefined;

}

if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {

jQuery.error( "Invalid XML: " + data );

}

return xml;

},

noop: function() {},

// Evaluates a script in a global context

// Workarounds based on findings by Jim Driscoll

// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context

globalEval: function( data ) {

if ( data && rnotwhite.test( data ) ) {

// We use execScript on Internet Explorer

// We use an anonymous function so that context is window

// rather than jQuery in Firefox

( window.execScript || function( data ) {

window[ "eval" ].call( window, data );

} )( data );

}

},

// Convert dashed to camelCase; used by the css and data modules

// Microsoft forgot to hump their vendor prefix (#9572)

camelCase: function( string ) {

return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );

},

nodeName: function( elem, name ) {

return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();

},

// args is for internal usage only

each: function( object, callback, args ) {

var name, i = 0,

length = object.length,

isObj = length === undefined || jQuery.isFunction( object );

if ( args ) {

if ( isObj ) {

for ( name in object ) {

if ( callback.apply( object[ name ], args ) === false ) {

break;

}

}

} else {

for ( ; i < length; ) {

if ( callback.apply( object[ i++ ], args ) === false ) {

break;

}

}

}

// A special, fast, case for the most common use of each

} else {

if ( isObj ) {

for ( name in object ) {

if ( callback.call( object[ name ], name, object[ name ] ) === false ) {

break;

}

}

} else {

for ( ; i < length; ) {

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

break;

}

}

}

}

return object;

},

// Use native String.trim function wherever possible

trim: trim ?

function( text ) {

return text == null ?

"" :

trim.call( text );

} :

// Otherwise use our own trimming functionality

function( text ) {

return text == null ?

"" :

text.toString().replace( trimLeft, "" ).replace( trimRight, "" );

},

// results is for internal usage only

makeArray: function( array, results ) {

var ret = results || [];

if ( array != null ) {

// The window, strings (and functions) also have ‘length‘

// Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930

var type = jQuery.type( array );

if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {

push.call( ret, array );

} else {

jQuery.merge( ret, array );

}

}

return ret;

},

inArray: function( elem, array, i ) {

var len;

if ( array ) {

if ( indexOf ) {

return indexOf.call( array, elem, i );

}

len = array.length;

i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;

for ( ; i < len; i++ ) {

// Skip accessing in sparse arrays

if ( i in array && array[ i ] === elem ) {

return i;

}

}

}

return -1;

},

merge: function( first, second ) {

var i = first.length,

j = 0;

if ( typeof second.length === "number" ) {

for ( var l = second.length; j < l; j++ ) {

first[ i++ ] = second[ j ];

}

} else {

while ( second[j] !== undefined ) {

first[ i++ ] = second[ j++ ];

}

}

first.length = i;

return first;

},

grep: function( elems, callback, inv ) {

var ret = [], retVal;

inv = !!inv;

// Go through the array, only saving the items

// that pass the validator function

for ( var i = 0, length = elems.length; i < length; i++ ) {

retVal = !!callback( elems[ i ], i );

if ( inv !== retVal ) {

ret.push( elems[ i ] );

}

}

return ret;

},

// arg is for internal usage only

map: function( elems, callback, arg ) {

var value, key, ret = [],

i = 0,

length = elems.length,

// jquery objects are treated as arrays

isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;

// Go through the array, translating each of the items to their

if ( isArray ) {

for ( ; i < length; i++ ) {

value = callback( elems[ i ], i, arg );

if ( value != null ) {

ret[ ret.length ] = value;

}

}

// Go through every key on the object,

} else {

for ( key in elems ) {

value = callback( elems[ key ], key, arg );

if ( value != null ) {

ret[ ret.length ] = value;

}

}

}

// Flatten any nested arrays

return ret.concat.apply( [], ret );

},

// A global GUID counter for objects

guid: 1,

// Bind a function to a context, optionally partially applying any

// arguments.

proxy: function( fn, context ) {

if ( typeof context === "string" ) {

var tmp = fn[ context ];

context = fn;

fn = tmp;

}

// Quick check to determine if target is callable, in the spec

// this throws a TypeError, but we will just return undefined.

if ( !jQuery.isFunction( fn ) ) {

return undefined;

}

// Simulated bind

var args = slice.call( arguments, 2 ),

proxy = function() {

return fn.apply( context, args.concat( slice.call( arguments ) ) );

};

// Set the guid of unique handler to the same of original handler, so it can be removed

proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;

return proxy;

},

// Mutifunctional method to get and set values to a collection

// The value/s can optionally be executed if it‘s a function

access: function( elems, key, value, exec, fn, pass ) {

var length = elems.length;

// Setting many attributes

if ( typeof key === "object" ) {

for ( var k in key ) {

jQuery.access( elems, k, key[k], exec, fn, value );

}

return elems;

}

// Setting one attribute

if ( value !== undefined ) {

// Optionally, function values get executed if exec is true

exec = !pass && exec && jQuery.isFunction(value);

for ( var i = 0; i < length; i++ ) {

fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );

}

return elems;

}

// Getting an attribute

return length ? fn( elems[0], key ) : undefined;

},

now: function() {

return ( new Date() ).getTime();

},

// Use of jQuery.browser is frowned upon.

// More details: http://docs.jquery.com/Utilities/jQuery.browser

uaMatch: function( ua ) {

ua = ua.toLowerCase();

var match = rwebkit.exec( ua ) ||

ropera.exec( ua ) ||

rmsie.exec( ua ) ||

ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||

[];

return { browser: match[1] || "", version: match[2] || "0" };

},

sub: function() {

function jQuerySub( selector, context ) {

return new jQuerySub.fn.init( selector, context );

}

jQuery.extend( true, jQuerySub, this );

jQuerySub.superclass = this;

jQuerySub.fn = jQuerySub.prototype = this();

jQuerySub.fn.constructor = jQuerySub;

jQuerySub.sub = this.sub;

jQuerySub.fn.init = function init( selector, context ) {

if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {

context = jQuerySub( context );

}

return jQuery.fn.init.call( this, selector, context, rootjQuerySub );

};

jQuerySub.fn.init.prototype = jQuerySub.fn;

var rootjQuerySub = jQuerySub(document);

return jQuerySub;

},

browser: {}

});

}

其中,noConflict方法实现了jQuery和其他框架的多库共存。我们可以测试一下。当然,测试之前,要修改文件jquery.main.js,添加对方法jQuery_extend_core的调用。

<!doctype html>

<html lang="en">

<head>

<meta charset="UTF-8">

<script>$="假设这是另一个库的命名空间";</script>

<script src="jquery.init.js"></script>

<script src="jquery.extend.define.js"></script>

<script src="jquery.extend.core.js"></script>

<script src="jquery.main.js"></script>

<title>jquery demo</title>

</head>

<body>

<script>

alert($);

</script>

</body>

</html>

测试文件中<script>$="假设这是另一个库的命名空间";</script>,是假设我们引用了其他的库,而且这个库的命名空间同样是$。测试结果是:

显示的是jQuery对象的构造函数,说明我们引入jQuery库的时候,jQuery框架的命名空间$覆盖了之前那个库的命名空间。现在我们用方法noConflict实现多库共存。

<!doctype html>

<html lang="en">

<head>

<meta charset="UTF-8">

<script>$="假设这是另一个库的命名空间";</script>

<script src="jquery.init.js"></script>

<script src="jquery.extend.define.js"></script>

<script src="jquery.extend.core.js"></script>

<script src="jquery.main.js"></script>

<title>jquery demo</title>

</head>

<body>

<script>

var myQuery = $.noConflict();

alert($);

alert(myQuery);

</script>

</body>

</html>

这样以来,jQuery对象就释放了命名空间$,而使用新的命名空间myQuery。

     

(待续)

时间: 2024-10-14 10:19:15

第五节:JQuery框架源码简析(1)的相关文章

第五节:JQuery框架源码简析(3)

(续2) 9.DOM 我们将jQuery有关DOM模块的代码放在文件jquery.extend.dom.js中. function jQuery_extend_dom(jQuery){ jQuery.fn.extend({ text: function( text ) { if ( jQuery.isFunction(text) ) { return this.each(function(i) { var self = jQuery( this ); self.text( text.call(t

第五节:JQuery框架源码简析(2)

(续1) 5.选择器Sizzle 我们把选择器Sizzle实现的代码放在文件jquery.sizzle.js中.关于选择器Sizzle的说明,请看<jQuery技术内幕:深入解析jQuery架构设计与实现原理>,这里不再赘述了. /*! * Sizzle CSS Selector Engine *  Copyright 2011, The Dojo Foundation *  Released under the MIT, BSD, and GPL Licenses. *  More info

SpringMVC学习——概念、流程图、源码简析(一)

学习资料:开涛的<跟我学SpringMVC.pdf> 众所周知,springMVC是比较常用的web框架,通常整合spring使用.这里抛开spring,单纯的对springMVC做一下总结. 概念 HandlerMapping:处理器映射,对请求的URL进行映射为具体的处理器(如果有拦截器也包含拦截器,会将Handler和多个HandlerInterceptor封装为HandlerExecutionChain对象) HandlerAdapter:处理器适配器,适配不同类型的处理器,如Cont

并发工具-CyclicBarrier源码简析

CyclicBarrier是循环栅栏的意思,循环的等待多个线程执行任务: <1> 示例代码如下: public class CyclicBarrierTest { public static CyclicBarrier cb = new CyclicBarrier(3, () -> System.out.println("-------开始点名-------")); public static void main(String[] args) { System.out

JDK源码简析--java.lang包中的基础类库

题记 JDK,Java Development Kit. 我们必须先认识到,JDK只是,仅仅是一套Java基础类库而已,是Sun公司开发的基础类库,仅此而已,JDK本身和我们自行书写总结的类库,从技术含量来说,还是在一个层级上,它们都是需要被编译成字节码,在JRE中运行的,JDK编译后的结果就是jre/lib下得rt.jar,我们学习使用它的目的是加深对Java的理解,提高我们的Java编码水平. 本系列所有文章基于的JDK版本都是1.7.16. 本节内容 在本节中,简析java.lang包所包

JDK源码简析--java.util包中的工具类库

题记 JDK,Java Development Kit. 我们必须先认识到,JDK只是,仅仅是一套Java基础类库而已,是Sun公司开发的基础类库,仅此而已,JDK本身和我们自行书写总结的类库,从技术含量来说,还是在一个层级上,它们都是需要被编译成字节码,在JRE中运行的,JDK编译后的结果就是jre/lib下得rt.jar,我们学习使用它的目的是加深对Java的理解,提高我们的Java编码水平. 本系列所有文章基于的JDK版本都是1.7.16. 本节内容 在本节中,简析java.util包所包

0002 - Spring MVC 拦截器源码简析:拦截器加载与执行

1.概述 Spring MVC中的拦截器(Interceptor)类似于Servlet中的过滤器(Filter),它主要用于拦截用户请求并作相应的处理.例如通过拦截器可以进行权限验证.记录请求信息的日志.判断用户是否登录等. 2.简单示例 2.1.继承 HandlerInterceptorAdapter 抽象类实现一个拦截器.代码如下: public class DemoInterceptor extends HandlerInterceptorAdapter { @Override    pu

[tomcat]源码简析 异步/非阻塞和请求构成

提出疑惑 SpringFramework5.0又新增加了一个功能Webflux(响应式编程),是一个典型非阻塞异步的框架.我们知道servlet3.0实现异步(AsyncContext),servlet3.1又提出了非阻塞IO.对此我一直有两点疑惑:1.tomcat8底层已经默认使用NIO了,不是已经是IO非阻塞了吗,怎么又说servlet3.1解决了非阻塞.2.关于异步,如果开发者在serlvet中开一个业务线程来实现,也算异步,为什么3.0还提供了一个组件来解决,那么这种方式和开发者自己开个

Linux Hugetlbfs内核源码简析-----(一)Hugetlbfs初始化

一.引言 为了实现虚拟内存管理机制,操作系统对内存实行分页管理.自内存“分页机制”提出之始,内存页面的默认大小便被设置为 4096 字节(4KB),虽然原则上内存页面大小是可配置的,但绝大多数的操作系统实现中仍然采用默认的 4KB 页面.当某些应用的需要使用的内存达到几G.甚至几十G的时候,4KB的内存页面将严重制约程序的性能. CPU缓存中有一组缓存专门用于缓存TLB,但其大小是有限的.当采用的默认页面大小为 4KB,其产生的TLB较大,因而将会产生较多 TLB Miss 和缺页中断,从而大大