本文是专栏文章《Js编程技巧之jQuery源码》的第四篇。继续介绍jQuery里的代码技巧(不会对细节面面俱到,欢迎留言讨论)
jQuery里很多工具函数的参数设计的非常巧妙:有些参数是可选的,有些参数是多种类型的。
使用者只需关心暴露的API,使用起来非常的便捷。接下来以源码里的三个模块为例介绍一下.
该模块属于Utilities
类别,用于将一个对象的成员扩展到另一个对象上。
看文档里介绍,有三种入参方式:
有四个需要关注的点:
arguments.length
。jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
//默认第一个参数为目标
target = arguments[ 0 ] || {
},
//i表示源头的索引号,默认从第二个开始
i = 1,
length = arguments.length,
//默认浅拷贝
deep = false;
// Handle a deep copy situation
//如第一个参数为boolean类型,则将第二参数视为拷贝目标
if ( typeof target === "boolean" ) {
deep = target;
// Skip the boolean and the target
target = arguments[ i ] || {
};
i++;
}
//...
// 当只有一个参数时,扩展自己(target 指向 this;第一个参数为源头),
if ( i === length ) {
target = this;
i--;
}
//依次遍历,将每个源头拷贝到目标
for ( ; i < length; i++ ) {
//...
//递归拷贝
}
// Return the modified object
return target;
};
jQuery.proxy
功能与es5 Function.prototype.bind
相近,参数更加灵活。
支持直接指定context
和function
,也支持plainObject+key的方式间接指定context
和function
。
jQuery.proxy = function( fn, context ) {
var tmp, args, proxy;
//当第二个参数为字符串,则视为第二种入参方式:
//参数fn实际上是plainObject,参数context为对象的key
if ( typeof context === "string" ) {
tmp = fn[ context ];
context = fn;
fn = tmp;
}
//...
};
jQuery源码里的正则有两种表示方式:
RegExp
构造函数+正则字符串实例化简单固定的正则以字面量形式表达,如:
var rhtml = /HTML$/i;
var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
复杂的正则可根据特点,拆分为单个可复用的子串,经过组合后由构造函数RegExp
实例化。
比如:CSS里有一个标识符
的概念,ID属性值、Class属性值、TAG属性值等都视为标识符
。jQuery编写了一个字符串实体
统一了这种标识符
,即可实例化也可以与其他模式字符串组合使用:
//identifier作为一个可复用的标识单元子串
var identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+";
// 复用了identifier单元子串
var attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
// Operator (capture 2)
"*([*^$|!~]?=)" + whitespace +
// "Attribute values must be CSS identifiers [capture 5]
// or strings [capture 3 or capture 4]"
"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
whitespace + "*\\]";
//通过RegExp实例化
var matchExpr = {
"ID": new RegExp( "^#(" + identifier + ")" ),
"CLASS": new RegExp( "^\\.(" + identifier + ")" ),
"TAG": new RegExp( "^(" + identifier + "|[*])" ),
"ATTR": new RegExp( "^" + attributes )
//...
}
这样既提高了编写正则的效率,也让代码结构更加的清晰。
上文已经提到extend方法用来扩展对象,并且二者指向的是同一个函数。
并且当参数只有一个时,target为this
。
源码里有些方法是以jQuery.extend(obj)调用的,有些是通过jQuery.fn.extend(obj)调用的:
但二者的意义是不同的:
jQuery.extend(obj)
将obj
扩展到jQuery本身jQuery.fn.extend(obj)
将obj
扩展到jQuery.fn
(jQuery原型对象)前者扩展的方法属于Utilities
类别(工具方法)。
而后者则可以通过jQuery实例直接使用,比如$('body').on('click',fn)