计划按例如以下顺序完毕这篇笔记:
- Java程序猿的JavaScript学习笔记(1——理念)
- Java程序猿的JavaScript学习笔记(2——属性复制和继承)
- Java程序猿的JavaScript学习笔记(3——this/call/apply)
- Java程序猿的JavaScript学习笔记(4——this/闭包/getter/setter)
- Java程序猿的JavaScript学习笔记(5——prototype)
- Java程序猿的JavaScript学习笔记(6——面向对象模拟)
- Java程序猿的JavaScript学习笔记(7——jQuery基本机制)
- Java程序猿的JavaScript学习笔记(8——jQuery选择器)
- Java程序猿的JavaScript学习笔记(9——jQuery工具方法)
- Java程序猿的JavaScript学习笔记(10——jQuery-在“类”层面扩展)
- Java程序猿的JavaScript学习笔记(11——jQuery-在“对象”层面扩展)
- Java程序猿的JavaScript学习笔记(12——jQuery-扩展选择器)
- Java程序猿的JavaScript学习笔记(13——jQuery UI)
- Java程序猿的JavaScript学习笔记(14——扩展jQuery UI)
这是笔记的第9篇。从jQuery源代码的角度,聊聊jQuery的工具方法。
作者博客:http://blog.csdn.net/stationxp
作者微博:http://weibo.com/liuhailong2008
转载请取得作者允许
1、先看几个工具方法怎样使用:
var t = $.trim(' >>_<< ');
var ps = $.param({x:15,y:16});
jQuery.type(function(){}) === "function"
jQuery的工具方法。相对jQuery本身,是相对独立的。
2、这些方法是怎样定义的呢?
我们推測一下,试试看。
我们知道jQuery(即$)是全局变量,这些方法应该是jQuery变量的属性。
我们能够通过例如以下语法加入:
jQuery.xx = function(a,b){
return a + b;
}
// try
var r1 = jQuery.xx(2,3);
console.log(r1);// output : 5
var r2 = $.xx(3,3);
console.log(r2);// output : 6
上面代码,说明$和jQuery引用了同样变量,他们是一样一样的。
这些代码达到了定义工具方法的目的,但jQuery是这样做的吗?
3、
查看jQuery源码,jQuery通过例如以下语法定义工具方法:
jQuery.extend({
noConflict: function( deep ) {
if ( window.$ === jQuery ) {
window.$ = _$;
}
if ( deep && window.jQuery === jQuery ) {
window.jQuery = _jQuery;
}
return jQuery;
}
//, ...
});
extend方法定义例如以下:
//定义了一个方法,赋值给jQuery.fn.extend和jQuery.extend.
//jQuery.fn.extend和jQuery.extend尽管定义同样,但调用者不同,从而方法中this指向不同,从而实现的功能不同。
//没有显式声明參数,而是通过arguments动态获得,从而支持更丰富的功能。
jQuery.extend = jQuery.fn.extend = function() {
var src, copyIsArray, copy, name, options, clone,
// 将第一个參数作为目标对象
target = arguments[0] || {},
// i初始化为1,临时还不知道做什么用
i = 1,
// length表示參数的个数
length = arguments.length,
// deep 应该表示是否深层拷贝。默觉得否。
deep = false;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
//假设第一个參数是布尔类型,则第二个參数为目标对象
//形如:jQuery.extend(false,UiObject,...);
deep = target;
target = arguments[1] || {};
// i 是当前參数的数组下标,从0開始
i = 2;
}
//目标对象是能是函数或者对象。传入其它參数,target默觉得空对象。 if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
// 眼下,假设第一个參数不是布尔,i值为1。length也为1,即extend(obj)的情况
// 假设是布尔,i值为2,length也为2,即extend(true,obj)的情况
// 总之。没有传入 src
if ( length === i ) {
// jQuery 把传入參数收了
// 參数下标又一次指向传入的这个參数,即此时指向了src
target = this;
--i;
}
// 如今i指向了target后面某个參数。应该是src
// 把后面传入的每一个參数都当src
for ( ;i < length; i++ ) {
// Only deal with non-null/undefined values
// 这样过滤非空啊,假设是undefined会被过滤掉吗?
// 不做 typeof src 是否 "object"、"function"的推断吗?
if ( (options = arguments[ i ]) != null ) {
// 赋值给变量 options
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ]; // 思虑万全:假设已经是自家人,就别嘚瑟了
// Prevent never-ending loop
if ( target === copy ) {
continue;
} // 对普通对象和数组,考虑递归深层拷贝
// 问题来了。什么叫普通对象? function 算不算?
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 : {};
}
//递归调
//假设写成 jQuery.extend( deep, clone, copy ) 是否一样
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
// Don't bring in undefined values
// 对 undefined 和 != null 还要加深理解
} else if ( copy !== undefined ) {
// 临门一脚。假设是 jQuery.extend({xx,xfasf});的情况,扩展的是this,即调用者。 // 对target直接改动。也没创建clone啥的
target[ name ] = copy;
}
}
}
}
// Return the modified object
return target;
};
4、再看看jQuery.isPlainObject怎样定义的
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
// 翻译:
// 必须是一个对象。
// 由于IE的缘故,我们还必须检查是否有constructor属性。 // 确认别放过Dom节点和window对象,他们不是普通对象。
if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
// 下面不通过空、undefined、0、jQuery.type返回不是object的、dom对象、window对象
// 所以 typeof 为string、number、function、正则、date的都不成
return false;
}
try {
// Not own constructor property must be Object
if ( obj.constructor &&
!core_hasOwn.call(obj, "constructor") &&
!core_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 || core_hasOwn.call( obj, key );
}
5、
试试看
jQuery.extend({
<span style="white-space:pre"> </span>sayHi:function(you){
<span style="white-space:pre"> </span>console.log('hi',you);
<span style="white-space:pre"> </span>}
});
$.sayHi('Stanley'); // output : hi Stanley
还不错。假设想覆盖jQuery已有的方法呢?
jQuery.extend({
isPlainObject:function(obj){
return 'baby, I am not sure.';
}
});
var r = $.isPlainObject({});
console.log(r); // output : baby, I am not sure.
这...好吧。能够得逞。
想想别的办法吧,不能这样。
再玩儿:
jQuery.extend({
extend:function(obj){
// do nothing
return 'who am i?';
}
});
var r = $.extend({x:function(){ alert();}});
console.log(r); // output : who am i?
jQuery.x();// error : function not be defined
又得逞。这样,再没人能够通过extend扩展jQuery的方法了。
但能够有更直接的方法。