jQuery技术内幕:深入解析jQuery架构设计与实现原理. 2.8 静态属性和方法

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介:

2.8 静态属性和方法

在构造jQuery对象模块中还定义了一些重要的静态属性和方法,它们是其他模块实现的基础。其整体源码结构如代码清单2-3所示。

代码清单2-3 静态属性和方法

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 });

jQuery.isReady、jQuery.readyWait、jQuery.holdReady()、jQuery.ready()、jQuery.bindReady()用于支持ready事件,将在9.11节对它们进行介绍和分析;jQuery.each()和jQuery.map()已经在2.7.3节和2.7.4节介绍和分析过了;jQuery.sub()在jQuery源码中没有用到,并且不推荐使用,这里不做分析;下面对其他的静态属性和方法逐一介绍和分析。

2.8.1 jQuery.noConflict( [removeAll] )

方法jQuery.noConflict( [removeAll] )用于释放jQuery对全局变量$的控制权,可选的参数removeAll指示是否释放对全局变量jQuery的控制权。$仅仅是jQuery的别名,所有的功能没有$也能使用。

很多JavaScript库使用美元符$作为函数名或变量名,在使用jQuery的同时,如果需要使用另一个JavaScript库,可以调用$.noConflict()返回$给其他库。如果有必要(例如,在一个页面中使用多个版本的jQuery库,但很少有这样的必要),也可以释放全局变量jQuery的控制权,只需要给这个方法传入参数true即可。

方法jQuery.noConflict( [removeAll] )相关代码如下所示:

  30     // Map over jQuery in case of overwrite

  31     _jQuery = window.jQuery,

  32

  33     // Map over the $ in case of overwrite

  34     _$ = window.$,

 

 388 jQuery.extend({

 389     noConflict: function( deep ) {

 390         if ( window.$ === jQuery ) {

 391             window.$ = _$;

 392         }

 393

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

 395             window.jQuery = _jQuery;

 396         }

 397

 398         return jQuery;

 399     },

 

9245 // Expose jQuery to the global object

9246 window.jQuery = window.$ = jQuery;

第30~34行:jQuery初始化时,把可能存在的window.jQuery和window.$备份到局部变量_jQuery和_$。

第390~392行:如果window.$ === jQuery,则设置window.$为初始化时备份的_$。也就是说,只有在当前jQuery库持有全局变量$的情况下,才会释放$的控制权给前一个JavaScript库。

从jQuery 1.6开始增加了对window.$ === jQuery的检测。如果不检测,则每次调用jQuery.noConflict()时都会释放$给前一个JavaScript库,当页面中有两个以上定义了$的 JavaScript库时,对$的管理将会变得混乱。

第394~396行:如果参数deep为true,并且window.jQuery === jQuery,则设置window.jQuery为初始化时备份的_jQuery。也就是说,如果参数deep为true,只有在当前jQuery库持有全局变量jQuery的情况下,才会释放jQuery的控制权给前一个JavaScript库。

从jQuery 1.6开始增加了对window.$ === jQuery的检测。如果不检测,则每次调用jQuery.noConflict( true )时都会释放jQuery给前一个JavaScript库,当页面中有两个以上定义了jQuery的JavaScript库时,对jQuery的管理将会变得混乱。

2.8.2 类型检测:jQuery.isFunction( obj )、jQuery.isArray( obj )、jQuery

   .isWindow( obj )、jQuery.isNumeric( value )、jQuery.type( obj )、

   jQuery.isPlainObject( object )、jQuery.isEmptyObject( object )

1.?jQuery.isFunction( obj )、jQuery.isArray( obj )

方法jQuery.isFunction( obj )用于判断传入的参数是否是函数;方法jQuery.isArray( obj )用于判断传入的参数是否是数组。这两个方法的实现依赖于方法jQuery.type( obj ),通过判断 jQuery.type( obj )返回值是否是“function”和“array”来实现。相关代码如下所示:

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

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

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

492     isFunction: function( obj ) {

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

494     },

495

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

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

498     },

499

2.?jQuery.type( obj )

方法jQuery.type( obj )用于判断参数的内建JavaScript类型。如果参数是undefined或null,返回“undefined”或“null”;如果参数是JavaScript内部对象,则返回对应的字符串名称;其他情况一律返回“object”。相关代码如下所示:

509     type: function( obj ) {

510         return obj == null ?

511            String( obj ) :

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

513     },

第510~511行:如果参数obj是undefined或null,通过String( obj )转换为对应的原始字符串“undefined”或“null”。

第512行:先借用Object的原型方法toString()获取obj的字符串表示,返回值的形式为[object class],其中的class是内部对象类,例如,Object.prototype.toString.call( true )会返回

[object Boolean];然后从对象class2type中取出[object class]对应的小写字符串并返回;如果未取到则一律返回“object”。原型方法toString()和对象class2type的定义和初始化源码如下所示:

 87    toString = Object.prototype.toString,

 

 94    // [[Class]] -> type pairs

 95    class2type = {};

 

894 // Ppulate the class2type map

895 jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {

896    class2type[ "[object " + name + "]" ] = name.toLowerCase();

897 });

对象class2type初始化后的结构为:

{

   "[object Array]": "array"

   "[object Boolean]": "boolean"

   "[object Date]": "date"

   "[object Function]": "function"

   "[object Number]": "number"

   "[object Object]": "object"

   "[object RegExp]": "regexp"

   "[object String]": "string"

}

3.?jQuery.isWindow( obj )

方法jQuery.isWindow( obj )用于判断传入的参数是否是window对象,通过检测是否存在特征属性setInterval来实现,相关代码如下所示:

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

501     isWindow: function( obj ) {

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

503     },

在本书写作时发布的jQuery 1.7.2中,该方法改为检测特征属性window,该属性是对窗口自身的引用,相关代码如下所示:

// 1.7.2

isWindow: function( obj ) {

   return obj != null && obj == obj.window;

},

4.?jQuery.isNumeric( value )

方法jQuery.isNumeric( value )用于判断传入的参数是否是数字,或者看起来是否像数字,相关代码如下所示:

505     isNumeric: function( obj ) {

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

507     },

第506行:先执行parseFloat( obj )尝试把参数obj解析为数字,然后用isNaN()判断解析结果是否是合法数字,并用isFinite()判断参数obj是否是有限的。如果parseFloat( obj )的解析结果是合法数字,并且参数obj是有限数字,则返回true;否则返回false。

方法parseFloat( string )用于对字符串参数进行解析,并返回字符串中的第一个数字。在解析过程中,如果遇到了不是有效数字的字符,解析就会停止并返回解析结果;如果字符串没有以一个有效的数字开头,则返回NaN;如果传入的参数是对象,则自动调用该对象的方法toString(),得到该对象的字符串表示,然后再执行解析过程。

方法isNaN( x )用于判断参数是否为非数字值,常用于检测方法parseFloat()和parseInt()的解析结果。

方法isFinite( number )用于判断一个数字是否是有限的。

5.?jQuery.isPlainObject( object )

方法jQuery.isPlainObject( object )用于判断传入的参数是否是“纯粹”的对象,即是否是用对象直接量{}或new Object()创建的对象。相关代码如下所示:

515     isPlainObject: function( obj ) {

516         // Must be an Object.

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

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

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

520             return false;

521         }

522

523         try {

524             // Not own constructor property must be Object

525             if ( obj.constructor &&

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

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

528                 return false;

529             }

530         } catch ( e ) {

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

532             return false;

533         }

534

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

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

537

538         var key;

539         for ( key in obj ) {}

540

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

542     },

第519~521行:如果参数obj满足以下条件之一,则返回false:

参数obj可以转换为false。

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

参数obj是DOM元素。

参数obj是window对象。

如果参数obj不满足以上所有条件,则至少可以确定参数obj是对象。

第523~533行:检查对象obj是否由构造函数Object()创建。如果对象obj满足以下所有条件,则认为不是由构造函数Object()创建,而是由自定义构造函数创建,返回false:

对象obj含有属性constructor。由构造函数创建的对象都有一个constructor属性,默认引用了该对象的构造函数。如果对象obj没有属性constructor,则说明该对象必然是通过对象字面量{}创建的。

对象obj的属性constructor是非继承属性。默认情况下,属性constructor继承自构造函数的原型对象。如果属性constructor是非继承属性,说明该属性已经在自定义构造函数中被覆盖。

对象obj的原型对象中没有属性isPrototypeOf。属性isPrototypeOf是Object原型对象的特有属性,如果对象obj的原型对象中没有,说明不是由构造函数Object()创建,而是由自定义构造函数创建。

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

函数hasOwn()指向Object.prototype.hasOwnProperty( property ),用于检查对象是否含有执行名称的非继承属性。

第539~541行:检查对象obj的属性是否都是非继承属性。如果没有属性,或者所有属性都是非继承属性,则返回true。如果含有继承属性,则返回false。

第539行:执行for-in循环时,JavaScript会先枚举非继承属性,再枚举从原型对象继承的属性。

第541行:如果对象obj的最后一个属性是非继承属性,则认为所有属性都是非继承属性,返回true;如果最后一个属性是继承属性,即含有继承属性,则返回false。

6.?jQuery.isEmptyObject( object )

方法jQuery.isEmptyObject( object )用于检测对象是否是空的(即不包含属性)。例如:

jQuery.isEmptyObject( {} )                           // true

jQuery.isEmptyObject( new Object() )               // true

jQuery.isEmptyObject( { foo: "bar" } )               // false

方法jQuery.isEmptyObject( object )的相关代码如下所示:

544     isEmptyObject: function( obj ) {

545         for ( var name in obj ) {

546             return false;

547         }

548         return true;

549     },

第545~548行:for-in循环会同时枚举非继承属性和从原型对象继承的属性,如果有,则立即返回false,否则默认返回true。

2.8.3 解析JSON和XML:jQuery.parseJSON( data )、jQuery.parseXML( data )

1.?jQuery.parseJSON( data )

方法jQuery.parseJSON( data )接受一个格式良好的JSON字符串,返回解析后的JavaScript对象。如果传入残缺的JSON字符串可能导致程序抛出异常;如果不传入参数,或者传入空字符串、null、undefined,则返回null。

如果浏览器提供了原生方法JSON.parse(),则使用该方法解析JSON字符串;否则使用

( new Function( "return"+ data ) )()解析JSON字符串。

方法jQuery.parseJSON( data )的相关代码如下所示:

555      parseJSON: function( data ) {

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

557             return null;

558         }

559

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

561         data = jQuery.trim( data );

562

563         // Attempt to parse using the native JSON parser first

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

565             return window.JSON.parse( data );

566         }

567

568         // Make sure the incoming data is actual JSON

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

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

571             .replace( rvalidtokens, "]" )

572             .replace( rvalidbraces, "")) ) {

573

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

575

576         }

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

578      },

第556~561行:对于非法参数一律返回null。如果参数data不是字符串,或者可以转换为false,则返回null。

第561行:移除开头和末尾的空白符。在IE 6/7中,如果不移除就不能正确的解析,例如:

typeof ( new Function( 'return ' + '\n{}' ) )();

// 返回"undefined"

第564~566行:尝试使用原生方法JSON.parse()解析JSON字符串,并返回。

JSON对象含有两个方法:JSON.parse()和JSON.stringify(),用于JSON字符串和JavaScript对象之间的相互转换。下面是它们的语法和使用示例。

JSON.parse()解析JSON字符串为 JSON对象,其语法如下:

JSON.parse(text[, reviver])

// text 待解析为 JSON 对象的字符串

// reviver 可选。在返回解析结果前,对解析结果中的属性值进行修改

JSON.parse()的使用示例如下所示:

JSON.parse( '{ "abc": 123 }' );

// {"abc": 123 }

 

JSON.parse( '{ "abc": 123 }', function( key, value ){

   if( key === '' ) return value;

   return value * 2;

} );

// {"abc": 246 }

JSON.stringify()转换JSON对象为JSON字符串,其语法如下:

JSON.stringify( value[, replacer [, space]] )

// value 待转换为 JSON 字符串的对象

// replacer 可选。如果 replacer 是函数,转换前先执行 replacer 改变属性值,如果函数 replacer 返回 undefined,则对应的属性不会出现在结果中;如果 replacer 是数组,指定最终字符串中包含的属性集合,不在数组 replacer 中的属性不会出现在结果中

// space 增加转换后的 JSON 字符串的可读性

JSON.stringify()的使用示例如下所示:

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

// '{"a":1,"b":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}'

 

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

// '{"b":2}'

 

JSON.stringify( { a: 1, b: 2 }, null, 4 );

// '{\n    "a": 1,\n    "b": 2\n}'

JSON对象、JSON.parse()、JSON.stringify()在ECMAScript 5中被标准化,IE 8以下的浏览器不支持。关于JSON规范和浏览器实现的更多信息请访问以下地址:

http://json.org/json-zh.html

http://www.ecma-international.org/publications/standards/Ecma-262.htm(ECMAScript 5第15.12节)

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/JSON

下面回到对方法jQuery.parseJSON()的分析中来。

第570~576行:在不支持JSON.parse()的浏览器中,先检查字符串是否合法,如何合法,才会执行( new Function("return"+ data) )()并返回执行结果。检查字符串是否合法的正则和逻辑来自开源JSON解析库json2.js(https://github.com/douglascrockford/JSON-js),检测过程分为4步,用到了4个正则表达式:rvalidchars、rvalidescape、rvalidtokens、rvalidbraces,相关代码如下:

 53     // JSON RegExp

 54     rvalidchars = /^[\],:{}\s]*$/,

 55     rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,

 56     rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,

 57     rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,

 

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

571             .replace( rvalidtokens, "]" )

572             .replace( rvalidbraces, "")) ) {

第54~57行:正则rvalidescape用于匹配转义字符;正则rvalidtokens用于匹配有效值(字符串、true、false、null、数值);正则rvalidbraces用于匹配正确的左方括号“[”;正则rvalidchars用于检查字符串是否只含有指定的字符(“]”、“,”、“:”、“{”、“}”、“\s”)。

第570~572行:先利用正则rvalidescape把转义字符替换为“@”,为进行下一步替换做准备;再利用正则rvalidtokens把字符串、true、false、null、数值替换为“]”;然后利用rvalidbraces删除正确的左方括号;最后检查剩余字符是否只包含“]”、“,”、“:”、“{”、“}”、“\s”,如果只包含这些字符,那么认为JSON字符串是合法的。

第574行:通过构造函数Function()创建函数对象,然后执行。构造函数Function()的语法如下:

new Function( arguments_names..., body)

// 参见arguments_names...:任意多个字符串参数,每个字符串命名一个或多个要创建的 Function 对象的参数

// 参见body:一个字符串,指定函数的主体,可以含有任意多条 JavaScript 语句,这些语句之间用分号隔开,可以给该构造函数引用前面的参数设置的任何参数名

// 返回新创建的 Function 对象。调用该函数,将执行 body 指定的 JavaScript 代码

第577行:如果浏览器不支持JSON.parse(),并且JSON字符串不合法,则在最后抛出一个异常。

2.?jQuery.parseXML( data )

方法jQuery.parseXML( data )接受一个格式良好的XML字符串,返回解析后的XML文档。

方法jQuery.parseXML()使用浏览器原生的XML解析函数实现。在IE 9+和其他浏览器中,会使用DOMParser对象解析;在IE 9以下的浏览器中,则使用ActiveXObject对象解析。相关代码如下所示:

580     // Cross-browser xml parsing

581     parseXML: function( data ) {

582         var xml, tmp;

583         try {

584             if ( window.DOMParser ) { // Sandard

585                 tmp = new DOMParser();

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

587             } else { //IE

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

589                 xml.async = "false";

590                 xml.loadXML( data );

591             }

592         } catch( e ) {

593             xml = undefined;

594         }

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

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

597         }

598         return xml;

599     },

第584~586行:尝试用标准解析器DOMParser解析。DOMParser在HTML5中标准化,可以将XML或HTML字符串解析为一个DOM文档。解析时,首先要创建一个DOMParser对象,然后使用它的方法parseFromString()来解析XML或HTML字符串。方法parseFromString()的语法如下:

DOMParser.parseFromString( DOMString str, SupportedType type )

// 参数str:待解析的 XML 或 HTML 字符串

// 参数type:支持的类型有"text/html"、"text/xml"、"application/xml"、"application/xhtml+xml"、"image/svg+xml"

// 返回一个解析后的新创建的文档对象

在IE以外的浏览器中,如果解析失败,方法parseFromString()不会抛出任何异常,只会返回一个包含了错误信息的文档对象,如下所示:

<parsererror xmlns="http:// www.mozilla.org/newlayout/xml/parsererror.xml">

   (error description)

   <sourcetext>(a snippet of the source XML)</sourcetext>

</parsererror>

因此,在第595行需要检查解析后的文档中是否包含<parsererror>节点,如果包含则表示解析失败,抛出一个更易读的异常。

在IE 9+中,如果解析失败,则会抛出异常。如果抛出异常,在catch块中设置xml为undefined,然后抛出一个更易读的异常。

可以运行下面的测试代码来验证上述内容:

new DOMParser().parseFromString("<a>hello" , "text/xml")

在Chrome中返回一个包含了错误信息的文档对象:

<a>

   <parsererror style="display: block; white-space: pre; border: 2px solid #c77;

   padding: 0 1em 0 1em; margin: 1em; background-color: #fdd; color: black">

       <h3>This page contains the following errors:</h3>

       <div style="font-family:monospace;font-size:12px">

           error on line 1 at column 9: Extra content at the end of the document

       </div>

       <h3>Below is a rendering of the page up to the first error.</h3>

   </parsererror>hello

</a>

在IE 9中则抛出异常:

"DOM Exception: SYNTAX_ERR (12)"

XML5602: 输入意外结束。

, 行1 字符9

下面回到对方法jQuery.parseXML()源码的分析中来。

第588~594行:IE 9以下的浏览器不支持DOMParser,需要使用微软的XML解析器Microsoft.XMLDOM解析。解析步骤依次为:创建一个空的XML文档对象,设该文档对象为同步加载,调用方法loadXML()解析XML字符串。

如果解析成功,方法loadXML()会返回true,解析结果存放在创建的XML文档对象中;如果解析失败,方法loadXML()会返回false(不会抛出异常),并设置文档根节点documentElement为null。

第595~597行:如果解析失败,则抛出一个更易读的异常。如果满足以下条件之一,则认为解析失败:

在IE 9+中,通过标准XML解析器DOMParser解析失败,此时!xml为true。

在IE 9以下的浏览器中,通过微软的XML解析器Microsoft.XMLDOM解析失败,此时!xml.documentElement为true。

在其他浏览器中,通过标准XML解析器DOMParser解析失败,此时xml.getElementsByTagName("parsererror").length可以转换为true。

第598行:如果解析成功,则返回解析结果。

下面是一些扩展阅读:

XML文档属性和方法:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms763798(v=vs.85).aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/ms757828(v=vs.85).aspx

解析XML文档:

http://www.w3schools.com/xml/xml_parser.asp

HTML5规范中的XML序列化:

http://html5.org/specs/dom-parsing.html#xmlserializer      

2.8.4 jQuery.globalEval( code )

方法jQuery.globalEval( code )用于在全局作用域中执行JavaScript代码。很多时候我们希望JavaScript代码是在全局作用域中执行,例如,当动态加载并执行JavaScript代码时。

在IE中,可以调用方法execScript()让JavaScript代码在全局作用域中执行;在其他浏览器中,则需要在一个自调用匿名函数中调用eval()执行JavaScript代码,自调用匿名函数确保了执行环境是全局作用域。相关代码如下所示:

603     // Evaluates a script in a global context

604     // Workarounds based on findings by Jim Driscoll

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

606     globalEval: function( data ) {

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

608             // We use execScript on Internet Explorer

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

610             // rather than jQuery in Firefox

611             ( window.execScript || function( data ) {

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

613             } )( data );

614         }

615     },

方法execScript()在全局作用域中按照指定的脚本语言执行脚本代码,默认语言是Jscript,没有返回值。该方法的语法如下:

execScript( code, language )

// 参数code:待执行的脚本代码

// 参数language:脚本语言,可选值有JavaScript、JavaScript1.1、JavaScript1.2、JavaScript1.3、

Jscript、VBS、VBScript,默认是 Jscript

Chrome的早期版本曾支持方法execScript(),现已不支持。

方法eval()在调用它的作用域中计算或执行JavaScript代码。如果JavaScript代码是一条表达式,则计算并返回计算结果;如果含有一条或多条JavaScript语句,则执行这些语句,如果最后一条语句有返回值,则返回这个值,否则返回undefined。该方法的语法如下:

eval( code )

// 参数code:待执行的 JavaScript 表达式或语句

第611~613行:如果支持方法execScript(),则执行execScript( data );否则创建一个自调用匿名函数,在函数内部执行“window[ "eval" ].call( window, data );”,读者可以访问源码注释第605行的网页地址查看如此书写的原因。

2.8.5 jQuery.camelCase( string )

方法jQuery.camelCase( string )转换连字符式的字符串为驼峰式,用于CSS模块和数据缓存模块。例如:

jQuery.camelCase( 'background-color' );

// "backgroundColor"

方法jQuery.camelCase( string )的相关代码如下所示:

 65     // Matches dashed string for camelizing

 66     rdashAlpha = /-([a-z]|[0-9])/ig,

 67     rmsPrefix = /^-ms-/,

 68

 69     // Used by jQuery.camelCase as callback to replace()

 70     fcamelCase = function( all, letter ) {

 71         return ( letter + "" ).toUpperCase();

 72     },

 

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

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

619     camelCase: function( string ) {

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

621     },

第66行:正则rdashAlpha用于匹配字符串中连字符“-”和其后的第一个字母或数字。如果连字符“-”后是字母,则匹配部分会被替换为对应的大写字母;如果连字符“-”后是数字,则会删掉连字符“-”,保留数字。

第67行:正则rmsPrefix用于匹配字符串中前缀“-ms-”,匹配部分会被替换为“ms-”。这么做是因为在IE中,连字符式的样式名前缀“-ms-”对应小写的“ms”,而不是驼峰式的“Ms”。例如,“-ms-transform”对应“msTransform”而不是“MsTransform”。在IE以外的浏览器中,连字符式的样式名则可以正确地转换为驼峰式,例如,“-moz-transform”对应“MozTransform”。

第70~72行:函数fcamelCase()负责把连字符后的字母转换为大写并返回。

第619~621行:在方法jQuery.camelCase()中,先用正则rmsPrefix匹配前缀“-ms-”,如果有则修正为“ms-”;然后用正则rdashAlpha匹配连字符“-”和其后的第一个字母或数字,并用字符串方法replace()和函数fcamelCase()把匹配部分替换为对应的大写字母或数字。

2.8.6 jQuery.nodeName( elem, name )

方法jQuery.nodeName( elem, name )用于检查DOM元素的节点名称(即属性nodeName)与指定的值是否相等,检查时忽略大小写。

DOM元素的属性nodeName返回该元素的节点名称;对于HTML文档,始终返回其大写形式;对于XML文档,因为XML文档区分大小写,所以返回值与源代码中的形式一致。在方法jQuery.nodeName( elem, name )中会把属性nodeName和参数name转换为大写形式后再做比较,即忽略大小写。相关代码如下所示:

623     nodeName: function( elem, name ) {

624        return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpper

Case();

625     },

第624行:把属性elem.nodeName和参数name都转换为大写再做比较。在执行elem.nodeName.toUpperCase()前先检查elem.nodeName是否存在,可以有效地避免参数elem不是DOM元素,或者参数elem没有属性nodeName导致的错误。

2.8.7 jQuery.trim( str )

方法jQuery.trim( str )用于移除字符串开头和结尾的空白符。如果传入的参数是null或undefined,则返回空字符串;如果传入的参数是对象,则先获取对象的字符串表示,然后移除开头和结尾的空白符,并返回。相关代码如下所示:

 43     // Check if a string has a non-whitespace character in it

 44     rnotwhite = /\S/,

 45

 46     // Used for trimming whitespace

 47     trimLeft = /^\s+/,

 48     trimRight = /\s+$/,

 

 91     trim = String.prototype.trim,

 

910 // IE doesn't match non-breaking spaces with \s

911 if ( rnotwhite.test( "\xA0" ) ) {

912     trimLeft = /^[\s\xA0]+/;

913     trimRight = /[\s\xA0]+$/;

914 }

 

668     // Use native String.trim function wherever possible

669     trim: trim ?

670         function( text ) {

671             return text == null ?

672                 "" :

673                 trim.call( text );

674         } :

675

676         // Otherwise use our own trimming functionality

677         function( text ) {

678             return text == null ?

679                 "" :

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

681         },

第47~48行:正则trimLeft用于匹配字符串开头的空白符;trimRight用于匹配字符串结尾的空白符。

第911~914行:在IE 9以下的浏览器中,\s不匹配不间断空格\xA0,需要为正则trimLeft和trimRight加上“\xA0”。

第669~681行:如果浏览器支持String.prototype.trim()则“借鸡生蛋”,String.prototype.

trim()是ECMAScript 5新增的String原型方法;如果不支持,则先调用方法toString()得到参数text的字符串表示,然后调用方法replace()把正则trimLeft和trimRight匹配到的空白符替换为空字符串。如果参数是null或undefined,则返回空字符串。

2.8.8 数组操作方法:jQuery.makeArray( obj )、jQuery.inArray( value,

   array [, fromIndex] )、jQuery.merge( first, second )、jQuery.

   grep( array, function(elementOfArray, indexInArray) [, invert] )

1.?jQuery.makeArray( obj )

方法jQuery.makeArray( obj )可以将一个类数组对象转换为真正的数组。

在jQuery内部,还可以为方法jQuery.makeArray()传入第二个参数,这样,第一个参数中的元素将被合并入第二个参数,最后会返回第二个参数,此时返回值的类型不一定是真正的数组。

方法jQuery.makeArray( obj )的源码如下:

 89     push = Array.prototype.push,

 

683     // results is for internal usage only

684     makeArray: function( array, results ) {

685        var ret = results || [];

686

687        if ( array != null ) {

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

689            // Theaked logic slightly to handle Blackberry 4.7 RegExp issues #6930

690            var type = jQuery.type( array );

691

692            if ( array.length == null

                     || type === "string"

                     || type === "function"

                     || type === "regexp"

                     || jQuery.isWindow( array ) ) {

693                push.call( ret, array );

694            } else {

695               jQuery.merge( ret, array );

696            }

697        }

698

699         return ret;

700     },

第684行:定义方法jQuery.makeArray( array, results ),它接受2个参数:

参数array:待转换对象,可以是任何类型。

参数results:仅在jQuery内部使用。如果传入参数results,则在该参数上添加元素。

第685行:定义返回值ret。如果传入了参数results则把该参数作为返回值,否则新建一个空数组作为返回值。

第687行:过滤参数array是null、undefined的情况。

第690~693行:如果参数array满足以下条件之一,则认为该参数不是数组,也不是类数组对象,调用数组方法push()把该参数插入返回值ret的末尾:

参数array没有属性length。

参数array是字符串,属性length返回字符串中的字符个数。

参数array是函数,属性length返回函数声明时的参数个数。

参数array是window对象,属性length返回窗口中的框架(frame、iframe)个数。

参数array是正则对象,在Blackberry(黑莓)4.7中,正则对象也有length属性。

注意,第693行插入元素时执行的是push.call( ret,array ),而不是ret.push( array ),这是因为返回值ret不一定是真正的数组。如果只传入参数array,则返回值ret是真正的数组;如果还传入了第二个参数result,则返回值ret的类型取决于该参数的类型。

第694~696行:否则认为参数array是数组或类数组对象,调用方法jQuery.merge()把该参数合并到返回值ret中。

第699行:最后返回ret。

2.?jQuery.inArray( value, array[, fromIndex] )

方法jQuery.inArray( value, array[, fromIndex] )在数组中查找指定的元素并返回其下标,未找到则返回-1。相关代码如下所示:

702     inArray: function( elem, array, i ) {

703         var len;

704

705         if ( array ) {

706             if ( indexOf ) {

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

708             }

709

710             len = array.length;

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

712

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

714                 // Sip accessing in sparse arrays

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

716                     return i;

717                 }

718             }

719         }

720

721         return -1;

722     },

第702行:定义方法jQuery.inArray( elem, array, i ),它接受3个参数:

参数elem:要查找的值。

参数array:数组,将遍历这个数组来查找参数value在其中的下标。

参数i:指定开始查找的位置,默认是0即查找整个数组。

第705行:过滤array可以转换为false的情况。

第706~708行:如果浏览器支持数组方法indexOf(),则调用它并返回下标。该方法在ECMAScript 5中被标准化。

第711行:修正参数i。如果未指定参数i,则初始化为0,表示默认从头开始遍历;如果i小于0,则加上数组长度len,即从数组末尾开始计算,例如,-1表示最后一个元素,-2表示倒数第二个元素,以此类推。注意,这里调用了Math.max()方法在0和len+i之间取最大值,即如果len+i依然小于0,则把i修正为0,仍然从头开始遍历。

第713~718行:从指定位置开始遍历数组,查找与指定值elem相等的元素,并返回其下标。先检测i in array,如果结果是false,说明数组array的下标是不连续的,也不需要与指定值elem比较;然后检测array[i]=== elem,如果结果是true,则返回当前下标。这里使用等同运算符( === )来避免类型转换。

第721行:如果没有找到与指定值相等的元素,则默认返回-1。从方法jQuery.inArray()的命名来看,这个方法应该返回true或false,而它实际上返回的却是下标,因此把方法名改为indexOf或许更合适些,但是这个方法从jQuery 1.2就一直存在,如果修改则会导致严重的向后兼容问题,所以返回值和方法名都不宜修改。

通常我们会比较jQuery.inArray()的返回值是否大于0,来判断某个指定的元素是否是数组中的元素,就像下面这样:

if( jQuery.inArray( elem, array ) > 0 ){

   // elem 是 array 中的元素

}

上面的写法比较繁琐,特别是当if语句检测的条件是复合布尔表达式时,可读性会很差;可以用按位非运算符(~)简化上面的代码:

if( ~jQuery.inArray( elem, array ) ){

   // elem 是 array 中的元素

}

按位非运算符(~)会将运算数的所有位取反,相当于改变它的符号并且减1,例如:

~-1 == 0; // true

~0 == -1; // true

~1 == -2; // true

~2 == -3; // true

更进一步,可以结合使用按位非运算符(~)和逻辑非运算符(!)把jQuery.inArray()的返回值转换为布尔型:

!!~jQuery.inArray( elem, array )

// 如果elem 可以匹配 array 中的某个元素,则该表达式的值为 true

// 如果elem 匹配不到 array 中的元素,则该表达式的值为 false

3.?jQuery.merge( first, second )

方法jQuery.merge( first, second )用于合并两个数组的元素到第一个数组中。事实上,第一个参数可以是数组或类数组对象,即必须含有整型(或可以转换为整型)属性length;第二个参数则可以是数组、类数组对象或任何含有连续整型属性的对象。

方法jQuery.merge()的合并行为是破坏性的,将第二个数组中的元素添加到第一个数组中后,第一个数组就被改变了。如果希望原来的第一个数组不被改变,可以在调用jQuery.merge()之前创建一份第一个数组的副本:

var newArray = $.merge([], oldArray);

方法jQuery.merge( first, second )的相关代码如下所示:

724     merge: function( first, second ) {

725         var i = first.length,

726             j = 0;

727

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

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

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

731             }

732

733         } else {

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

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

736             }

737         }

738

739         first.length = i;

740

741         return first;

742     },

第724行:定义方法jQuery.merge( first, second ),它接受2个参数:

参数first:数组或类数组对象,必须含有整型(或可以转换为整型)属性length,第二个数组second中的元素会被合并到该参数中。

参数second:数组、类数组对象或任何含有连续整型属性的对象,其中的元素会被合并到第一个参数first中。

第725行:初始化变量i为first.length,该变量指示了插入新元素时的下标。first.length必须是整型或可以转换为整型,否则后面执行i++时会返回NaN。

第728~731行:如果参数second的属性length是数值类型,则把该参数当作数组处理,把其中的所有元素都添加到参数first中。

第733~737行:如果参数second没有属性length,或者属性length不是数值类型,则把该参数当作含有连续整型(或可以转换为整型)属性的对象,例如,{ 0:'a', 1:'b'},把其中的非undefined元素逐个插入参数first中。

第739行:修正first.length。因为参数first可能不是真正的数组,所以需要手动维护属性length的值。

第741行:返回改变后的参数first。

4.?jQuery.grep( array, function( elementOfArray, indexInArray )[, invert] )

方法jQuery.grep( array, function( elementOfArray, indexInArray )[, invert] )用于查找数组中满足过滤函数的元素,原数组不会受影响。

如果参数invert未传入或是false,元素只有在过滤函数返回true,或者返回值可以转换为true时,才会被保存在最终的结果数组中,即返回一个满足回调函数的元素数组;如果参数invert是true,则情况正好相反,返回的是一个不满足回调函数的元素数组。

该方法的相关代码如下所示:

744     grep: function( elems, callback, inv ) {

745         var ret = [], retVal;

746         inv = !!inv;

747

748         // Go through the array, only saving the items

749         // that pass the validator function

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

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

752             if ( inv !== retVal ) {

753                 ret.push( elems[ i ] );

754             }

755         }

756

757         return ret;

758     },

第744行:定义方法jQuery.grep( elems, callback, inv ),它接受3个参数:

参数array:待遍历查找的数组。

参数callback:过滤每个元素的函数,执行时被传入两个参数:当前元素和它的下标。该函数应该返回一个布尔值。

参数inv:如果参数inv是false或未传入,方法jQuery.grep()会返回一个满足回调函数的元素数组;如果参数inv是true,则返回一个不满足回调函数的元素数组。

第746行:遍历数组elems,为每个元素执行过滤函数。如果参数inv为true,把执行结果为false的元素放入结果数组ret;如果inv为false,则把执行结果为true的元素放入结果数组ret。

第757行:最后返回结果数组ret。

2.8.9 jQuery.guid、jQuery.proxy( function, context )

1.?jQuery.guid

属性jQuery.guid是一个全局计数器,用于jQuery事件模块和缓存模块。在jQuery事件模块中,每个事件监听函数会被设置一个guid属性,用来唯一标识这个函数;在缓存模块中,通过在DOM元素上附加一个唯一标识,来关联该元素和该元素对应的缓存。属性jQuery.guid初始值为1,使用时自增1,相关代码如下所示:

793     // A global GUID counter for objects

794     guid: 1,

 

// jQuery.data( elem, name, data, pvt /* Internal Use Only */ )

1679                 elem[ internalKey ] = id = ++jQuery.uuid;

 

// jQuery.event.add: function( elem, types, handler, data, selector )

2861             handler.guid = jQuery.guid++;

2.?jQuery.proxy( function, context )

方法jQuery.proxy( function, context )接受一个函数,返回一个新函数,新函数总是持有特定的上下文。这个方法有两种用法:

(1)jQuery.proxy(  function, context )

参数function是将被改变上下文的函数,参数context是上下文。指定参数function的上下文始终为参数content。

(2)jQuery.proxy( context, name )

参数name是参数context的属性。指定参数name对应的函数的上下文始终为参数context。

该方法的相关代码如下所示:

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

797     // aguments.

798     proxy: function( fn, context ) {

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

800             var tmp = fn[ context ];

801             context = fn;

802             fn = tmp;

803         }

804

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

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

807         if ( !jQuery.isFunction( fn ) ) {

808             return undefined;

809         }

810

811         // Simulated bind

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

813             proxy = function() {

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

815             };

816

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

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

819

820         return proxy;

821     },

第798行:定义方法jQuery.proxy( fn, context ),参数有两种格式:

jQuery.proxy( fn, context )

jQuery.proxy( context, name )

第799行:修正参数fn和context。如果第二个参数是字符串,说明参数格式是jQuery.proxy( context, name ),修正为jQuery.proxy( fn, context )。

第807~809行:如果参数fn不是函数,则返回undefined。

第812行:收集多余参数。如果调用jQuery.proxy()时,除了传入参数fn、context之外,还传入了其他参数,那么在调用函数fn时,这些多余的参数将会优先传入。这里借用数组方法slice()来获取参数对象arguments中fn、context后的其他参数。下面的例子测试了传入多余参数的情况:

var proxied = $.proxy( function(){

   console.log( this );               // Object

   console.log( arguments ); // [1, 2, 3]

}, {}, 1, 2, 3 );

proxied(4, 5);

// 在控制台依次打印:

// Object

// [1, 2, 3, 4, 5]

第813~815行:创建一个代理函数,在代理函数中调用原始函数fn,调用时通过方法apply()指定上下文。代理函数通过闭包机制引用context、args、slice。

第818行:为代理函数设置与原始函数相同的唯一标识guid。如果原始函数没有,则重新分配一个。

相同的唯一标识将代理函数和原始函数关联了起来。例如,在jQuery事件系统中,如果为DOM元素绑定了事件监听函数的代理函数,当移除事件时,即使传入的是原始函数,jQuery也能通过唯一标识guid移除正确的函数。

第820行:最后返回创建的代理函数。

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

方法jQuery.access( elems, key, value, exec, fn( elem, key, value ), pass )可以为集合中的元素设置一个或多个属性值,或者读取第一个元素的属性值。如果设置的属性值是函数,并且参数exec是true时,还会执行函数并取其返回值作为属性值。

方法jQuery.access()为.attr()、.prop()、.css()提供支持,这三个方法在调用jQuery.access()时,参数exec为true,参数fn是同时支持读取和设置属性的函数(例jQuery.attr()、jQuery.prop()),相关代码如下所示:

// .attr()

2166     attr: function( name, value ) {

2167         return jQuery.access( this, name, value, true, jQuery.attr );

2168     },

 

// p.rop()

2176     prop: function( name, value ) {

2177         return jQuery.access( this, name, value, true, jQuery.prop );

2178     },

 

// c.ss()

6460 jQuery.fn.css = function( name, value ) {

6461     // ...

6466     return jQuery.access( this, name, value, true, function( elem, name, value ) {

6467         return value !== undefined ?

6468            jQuery.style( elem, name, value ) :

6469            jQuery.css( elem, name );

6470     });

6471 };

方法jQuery.access( elems, key, value, exec, fn, pass )的相关代码如下所示:

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

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

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

826         var length = elems.length;

827

828         // Setting many attributes

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

830             for ( var k in key ) {

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

832             }

833             return elems;

834         }

835

836         // Setting one attribute

837         if ( value !== undefined ) {

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

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

840

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

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

843             }

844

845             return elems;

846         }

847

848         // Getting an attribute

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

850     },

第825行:定义方法jQuery.access( elems, key, value, exec, fn, pass ),它接受 6 个参数:

参数elems:元素集合,通常是jQuery对象。

参数key:属性名或含有键值对的对象。

参数value:属性值或函数。当参数key是对象时,该参数为undefined。

参数exec:布尔值,当属性值是函数时,该参数指示了是否执行函数。

参数fn:回调函数,同时支持读取和设置属性。

参数pass:布尔值,该参数在功能上与参数exec重叠,并且用法相当繁琐,可以忽略这个参数。

第829~834行:如果参数key是对象,表示要设置多个属性,则遍历参数key,为每个属性递归调用方法jQuery.access(),遍历完后返回元素集合elems。

第837~846行:如果参数value不是undefined,表示要设置单个属性,则遍历元素集合elems,为每个元素调用回调函数fn,遍历完后返回元素集合elems。如果参数exec为true,并且参数value是函数,则执行参数value,并取其返回值作为属性值。

第849行:读取一个属性。如果元素集合elems不为空,则为第一个元素调用回调函数fn,读取参数key对应的属性值;否则返回undefined。

2.8.11 jQuery.error( message )、jQuery.noop()、jQuery.now()

方法jQuery.error( message )接受一个字符串,抛出一个包含了该字符串的异常。开发插件时可以覆盖这个方法,用来显示更有用或更多的错误提示消息。

方法jQuery.noop()表示一个空函数。当希望传递一个什么也不做的函数时,可以使用这个空函数。开发插件时,这个方法可以作为可选回调函数的默认值,如果没有提供回调函数,则执行jQuery.noop()。

方法jQuery.now()返回当前时间的毫秒表示,是( newDate() ).getTime()的简写。

上面3个方法的相关代码如下所示:

551     error: function( msg ) {

552         throw new Error( msg );

553     },

 

601     noop: function() {},

 

852     now: function() {

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

854     },

2.8.12 浏览器嗅探:jQuery.uaMatch( ua )、jQuery.browser

属性jQuery.browser提供了访问当前页面的浏览器的信息,其中包含最流行的4种浏览器类型(IE、Mozilla、Webkit、Opera)和版本信息,属性值的结构如下:

// jQuery.browser

{

  webkit/opera/msie/mozilla: true,

  version: '版本号'

}

Chrome和Safari使用Webkit作为内核引擎,因此如果jQuery.browser.webkit为true则表示浏览器是Chrome或Safari;如果jQuery.browser.mozilla为true,则表示浏览器是Mozilla Firefox。

jQuery.browser通过解析navigator.userAgent来获取浏览器类型和版本号,这种技术也称为浏览器嗅探技术,用于解决浏览器不兼容问题;navigator是全局对象window的属性,指向一个Navigator对象,包含了正在使用的浏览器的信息;navigator.userAgent包含了浏览器用于HTTP请求的用户代理头(User-Agent)的值。

应该避免编写基于特定浏览器类型或版本号的代码,因为这会导致代码与特定的浏览器类型或版本紧密绑定在一起,另外,用户或浏览器也可以修改navigator.userAgent,欺骗脚本和服务器端;解决浏览器不兼容问题的更好做法是基于浏览器功能测试编写代码,具体请参阅第7章。

对navigator.userAgent的解析由方法jQuery.uaMatch( ua )实现,相关代码如下所示:

 59     // Useragent RegExp

 60     rwebkit = /(webkit)[ \/]([\w.]+)/,

 61     ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,

 62     rmsie = /(msie) ([\w.]+)/,

 63     rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,

 

 74     // Keep a UserAgent string for use with jQuery.browser

 75     userAgent = navigator.userAgent,

 

856     // Use of jQuery.browser is frowned upon.

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

858     uaMatch: function( ua ) {

859         ua = ua.toLowerCase();

860

861         var match = rwebkit.exec( ua ) ||

862             ropera.exec( ua ) ||

863             rmsie.exec( ua ) ||

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

865             [];

866

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

868     },

 

891     browser: {}

892 });

 

899 browserMatch = jQuery.uaMatch( userAgent );

900 if ( browserMatch.browser ) {

901     jQuery.browser[ browserMatch.browser ] = true;

902     jQuery.browser.version = browserMatch.version;

903 }

904

905 // Dprecated, use jQuery.browser.webkit instead

906 if ( jQuery.browser.webkit ) {

907     jQuery.browser.safari = true;

908 }

第60~63行:定义用于解析用户代理navigator.userAgent的4个正则表达式:rwebkit、ropera、rmsie、rmozilla。每个正则包含两个分组:浏览器类型特征字符和浏览器版本特征字符。

第858~868行:定义方法jQuery.uaMatch( ua ),用于解析当前浏览器的类型和版本号。在方法jQuery.uaMatch( ua )中,依次尝试用4个正则表达式匹配用户代理navigator.userAgent,并返回一个包含了匹配结果的对象,对象的结构是:

{

   browser: 分组 1 或空字符串,

   version: 分组 2 或字符串"0"

}

第899~903行:调用方法jQuery.uaMatch( ua )解析用户代理navigator.userAgent,并把解析结果重新封装为jQuery.browser。

2.8.13 小结

构造jQuery对象模块的静态属性和方法总结如图2-9所示。

相关文章
|
4天前
|
存储 缓存 算法
HashMap深度解析:从原理到实战
HashMap,作为Java集合框架中的一个核心组件,以其高效的键值对存储和检索机制,在软件开发中扮演着举足轻重的角色。作为一名资深的AI工程师,深入理解HashMap的原理、历史、业务场景以及实战应用,对于提升数据处理和算法实现的效率至关重要。本文将通过手绘结构图、流程图,结合Java代码示例,全方位解析HashMap,帮助读者从理论到实践全面掌握这一关键技术。
32 13
|
5天前
|
机器学习/深度学习 人工智能 自然语言处理
AI技术深度解析:从基础到应用的全面介绍
人工智能(AI)技术的迅猛发展,正在深刻改变着我们的生活和工作方式。从自然语言处理(NLP)到机器学习,从神经网络到大型语言模型(LLM),AI技术的每一次进步都带来了前所未有的机遇和挑战。本文将从背景、历史、业务场景、Python代码示例、流程图以及如何上手等多个方面,对AI技术中的关键组件进行深度解析,为读者呈现一个全面而深入的AI技术世界。
52 10
|
5天前
|
存储 SQL 关系型数据库
MySQL进阶突击系列(03) MySQL架构原理solo九魂17环连问 | 给大厂面试官的一封信
本文介绍了MySQL架构原理、存储引擎和索引的相关知识点,涵盖查询和更新SQL的执行过程、MySQL各组件的作用、存储引擎的类型及特性、索引的建立和使用原则,以及二叉树、平衡二叉树和B树的区别。通过这些内容,帮助读者深入了解MySQL的工作机制,提高数据库管理和优化能力。
|
19天前
|
人工智能 前端开发 编译器
【AI系统】LLVM 架构设计和原理
本文介绍了LLVM的诞生背景及其与GCC的区别,重点阐述了LLVM的架构特点,包括其组件独立性、中间表示(IR)的优势及整体架构。通过Clang+LLVM的实际编译案例,展示了从C代码到可执行文件的全过程,突显了LLVM在编译器领域的创新与优势。
40 3
|
14天前
|
运维 监控 持续交付
微服务架构解析:跨越传统架构的技术革命
微服务架构(Microservices Architecture)是一种软件架构风格,它将一个大型的单体应用拆分为多个小而独立的服务,每个服务都可以独立开发、部署和扩展。
138 36
微服务架构解析:跨越传统架构的技术革命
|
12天前
|
机器学习/深度学习 人工智能 自然语言处理
秒级响应 + 99.9%准确率:法律行业文本比对技术解析
本工具基于先进AI技术,采用自然语言处理和语义匹配算法,支持PDF、Word等格式,实现法律文本的智能化比对。具备高精度语义匹配、多格式兼容、高性能架构及智能化标注与可视化等特点,有效解决文本复杂性和法规更新难题,提升法律行业工作效率。
|
9天前
|
数据采集 存储 JavaScript
网页爬虫技术全解析:从基础到实战
在信息爆炸的时代,网页爬虫作为数据采集的重要工具,已成为数据科学家、研究人员和开发者不可或缺的技术。本文全面解析网页爬虫的基础概念、工作原理、技术栈与工具,以及实战案例,探讨其合法性与道德问题,分享爬虫设计与实现的详细步骤,介绍优化与维护的方法,应对反爬虫机制、动态内容加载等挑战,旨在帮助读者深入理解并合理运用网页爬虫技术。
|
19天前
|
存储 Linux API
深入探索Android系统架构:从内核到应用层的全面解析
本文旨在为读者提供一份详尽的Android系统架构分析,从底层的Linux内核到顶层的应用程序框架。我们将探讨Android系统的模块化设计、各层之间的交互机制以及它们如何共同协作以支持丰富多样的应用生态。通过本篇文章,开发者和爱好者可以更深入理解Android平台的工作原理,从而优化开发流程和提升应用性能。
|
15天前
|
机器学习/深度学习 自然语言处理 监控
智能客服系统集成技术解析和价值点梳理
在 2024 年的智能客服系统领域,合力亿捷等服务商凭借其卓越的技术实力引领潮流,它们均积极应用最新的大模型技术,推动智能客服的进步。
50 7
|
21天前
|
弹性计算 持续交付 API
构建高效后端服务:微服务架构的深度解析与实践
在当今快速发展的软件行业中,构建高效、可扩展且易于维护的后端服务是每个技术团队的追求。本文将深入探讨微服务架构的核心概念、设计原则及其在实际项目中的应用,通过具体案例分析,展示如何利用微服务架构解决传统单体应用面临的挑战,提升系统的灵活性和响应速度。我们将从微服务的拆分策略、通信机制、服务发现、配置管理、以及持续集成/持续部署(CI/CD)等方面进行全面剖析,旨在为读者提供一套实用的微服务实施指南。

推荐镜像

更多
下一篇
DataWorks