jQuery.extend({

 // 遍历obj的所有值
// args 这参数只能内部调用的会用到
// 注意到,如果回调函数调用失败会直接跳出并中止遍历
// 当有args数组时,使用apply调用,否则使用call调用
each: function( obj, callback, args ) {
var value,
i = 0,
length = obj.length,
isArray = isArraylike( obj );
if ( args ) { // 内部调用时才会有args
if ( isArray ) { // obj是Array for ( ; i < length; i++ ) {
value = callback.apply( obj[ i ], args );
if ( value === false ) {
break;
}
}
} else { // obj是Object for ( i in obj ) { value = callback.apply( obj[ i ], args ); if ( value === false ) { break; } } }
// 最常用的each使用方式
} else {
if ( isArray ) { // obj是Array
for ( ; i < length; i++ ) {
value = callback.call( obj[ i ], i, obj[ i ] ); // 回调函数会获取i 和 对应的属性
if ( value === false ) {
break;
}
}
} else { // obj是Object
for ( i in obj ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) {
break;
}
}
}
}
return obj;
}, // 支持: Android<4.1, IE<9
// rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g
// 删除 BOM and NBSP
trim: function( text ) {
return text == null ?
"" :
( text + "" ).replace( rtrim, "" );
}, // results参数仅限内部调用
// 将arr变成一个Array
// 如果有results,arr会合并到results后面
// 如果arr是'string',则会转换为[arr]然后合并到Array上
makeArray: function( arr, results ) {
var ret = results || [];
if ( arr != null ) {
if ( isArraylike( Object(arr) ) ) {
jQuery.merge( ret,
typeof arr === "string" ?
[ arr ] : arr
);
} else {
push.call( ret, arr );
}
}
return ret;
}, // 判断elem是否在arr这个数组上
// i是检索的起始index
inArray: function( elem, arr, i ) {
var len;
if ( arr ) {
if ( indexOf ) {
return indexOf.call( arr, elem, i ); // 调用Array.indexOf
}
len = arr.length;
i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; // 如果i<0,则设i为len+i
for ( ; i < len; i++ ) {
// 跳过对稀疏数组的访问?
// Skip accessing in sparse arrays
if ( i in arr && arr[ i ] === elem ) {
return i;
}
}
}
return -1;
}, // 把second合并到first上
merge: function( first, second ) {
var len = +second.length,
j = 0,
i = first.length;
while ( j < len ) {
first[ i++ ] = second[ j++ ];
}
// 支持: IE<9
// 如果类数组对象没有length,因此.length不是一个数字,例如NodeLists
if ( len !== len ) {
while ( second[j] !== undefined ) {
first[ i++ ] = second[ j++ ];
}
}
first.length = i;
return first;
}, // 筛选遍历数组
// callback用于判断是否符合
// invert表示与callback的结果相反,即希望保留callback返回值为false的元素
grep: function( elems, callback, invert ) {
var callbackInverse,
matches = [],
i = 0,
length = elems.length,
callbackExpect = !invert; // invert == true, 表示希望callback返回false
// 遍历数组,只保留通过筛选的元素
for ( ; i < length; i++ ) {
callbackInverse = !callback( elems[ i ], i );
if ( callbackInverse !== callbackExpect ) {
matches.push( elems[ i ] );
}
}
return matches;
}, // arg参数只在内部调用时使用
map: function( elems, callback, arg ) {
var value,
i = 0,
length = elems.length,
isArray = isArraylike( elems ),
ret = [];
// 遍历数组,将其转换成新的值并放到ret数组中
if ( isArray ) {
for ( ; i < length; i++ ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret.push( value );
}
}
// 遍历对象的属性,将其转换成新的值并放到ret数组中
} else {
for ( i in elems ) {
value = callback( elems[ i ], i, arg );
if ( value != null ) {
ret.push( value );
}
}
}
// 如果存在嵌套的数组,将其展开
return concat.apply( [], ret );
}, // 对象的全局GUID计数器
guid: 1, // Bind a function to a context, optionally partially applying any arguments.
// 为一个function绑定一个上下文环境,
proxy: function( fn, context ) {
var args, proxy, tmp;
// 处理: $.proxy(context, name)
if ( typeof context === "string" ) {
tmp = fn[ context ]; // 从参数context提取一个function
context = fn; // 将上下文设置成参数context
fn = tmp; // 将绑定function对象设置为conext[nam]
} // 确保fn是一个可调用对象
// 如果不可调用,返回undefined
if ( !jQuery.isFunction( fn ) ) {
return undefined;
}
// 模拟上下文绑定
args = slice.call( arguments, 2 ); // 截取fn所需要的参数
proxy = function() {
return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
};
// 为唯一的句柄设置guid,这个guid应该与原理fn的guid一样,使得它可以被移除
proxy.guid = fn.guid = fn.guid || jQuery.guid++;
return proxy;
}, // 新建一个时间对象并返回
now: function() {
return +( new Date() );
}, // jQuery不会在内核中使用,但其他项目会将一些属性设置到support中
support: support });
 
 
总结:
  • jQuery.extend在进行扩展的时候,是使用到了深拷贝,这是教科书式的用法,对Object和Array对象进行递归合并,将其中所有的属性都作拷贝。这样做的原因是在JavaScript里,对右值为Object和Array的赋值操作是执行引用而非拷贝,因此必须遍历Object和Array的属性以实现深拷贝;
  • 方法重载的技巧再次出现,如jQuery.proxy。重载的实现实际上要对参数类型、参数数量进行判断,以进行不同的处理;
  • 为了绑定函数方法的上下文环境,我们可以使用jQuery.proxy,而.proxy的实现使用到了function.apply(conext, args);
  • jQuery.grep的实现也是比较巧妙,添加一个形参invert来进行反向选择,即可以利用一个callback作出双向选择,这也是一个简单而巧妙的技巧。

jQuery 源码分析6: jQuery 基本静态方法(二)的更多相关文章

  1. jQuery源码分析之=>jQuery的定义

    最近写前段的代码比较多,jQuery是用的最多的一个对象,但是之前几次看了源码,都没搞清楚jQuery是怎么定义的,今天终于看明白怎么回事了.记录下来,算是一个新的开始吧. (文中源码都是jQuery ...

  2. jQuery 源码分析4: jQuery.extend

    jQuery.extend是jQuery最重要的方法之一,下面看看jQuery是怎样实现扩展操作的 // 如果传入一个对象,这个对象的属性会被添加到jQuery对象中 // 如果传入两个或多个对象,所 ...

  3. jQuery 源码分析3: jQuery.fn/ jQuery.prototype

    // 建立方法实例,提高方法访问的速度(避免在原型链上搜索) var deletedIds = []; var slice = deletedIds.slice; var concat = delet ...

  4. jquery源码分析(七)——事件模块 event(二)

    上一章节探讨了事件的一些概念,接下来看下jQuery的事件模块. jQuery对事件的绑定分别有几个API:.bind()/.live()/.delegate()/.on()/click(), 不管是 ...

  5. jQuery 源码分析5: jQuery 基本静态方法(一)

    jQuery在初始化过程中会为自己扩展一些基本的静态方法和属性,以下是jQuery 1.11.3版本 239 ~ 564行间所扩展的静态属性和方法   jQuery.extend({ // 为每个jQ ...

  6. jQuery 源码分析2: jQuery.fn.init

    //jQuery.fn.intit 中使用到的外部变量: // 判断是否为HTML标签或#id rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w ...

  7. 六.jQuery源码分析之jQuery原型属性和方法

    97 jQuery.fn = jQuery.prototype = { 98 constructor: jQuery, 99 init: function( selector, context, ro ...

  8. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  9. [转] jQuery源码分析-如何做jQuery源码分析

    jQuery源码分析系列(持续更新) jQuery的源码有些晦涩难懂,本文分享一些我看源码的方法,每一个模块我基本按照这样的顺序去学习. 当我读到难度的书或者源码时,会和<如何阅读一本书> ...

随机推荐

  1. [五]SpringMvc学习-Restful风格实现

    1.Restful风格的资源URL 无后缀资源的访问(csdn用法) 2.SpringMvc对Rest风格的支持 2.1将 /*.do改为/ 2.2 3.@PathVariable获取Url变量 @R ...

  2. android模拟器访问localhost或127.0.0.1报错

    在一般的Java Web程序开发中,我们通常使用localhost或者127.0.0.1来访问本机的Web服务,但是如果我们在Android模拟器中也采用同样的地址来访问,Android模拟器将无法正 ...

  3. CSS滤镜

    Filter属性介绍 Alpha滤镜的使用 Blur滤镜的使用 Filph.Filpv滤镜 DropShadow滤镜 Glow 滤镜 Gray ,Invert,Xray 滤镜 Shadow滤镜 19. ...

  4. 使用poi将word转换为html

    使用poi将word转换为html,支持doc,docx,转换后可以保持文字.表格.图片.样式 演示地址: https://www.xiaoyun.studio/app/preview.html 完整 ...

  5. android studio里的build.gradle基本属性

    //声明是android 程序 apply plugin: 'com.android.application' android { //编译SDK版本 compileSdkVersion 23 // ...

  6. ThinkPHP函数详解:C方法

    C方法是ThinkPHP用于设置.获取,以及保存配置参数的方法,使用频率较高.了解C方法需要首先了解下ThinkPHP的配置,因为C方法的所有操作都是围绕配置相关的.ThinkPHP的配置文件采用PH ...

  7. Codeforces Round #321 (Div. 2) B. Kefa and Company 二分

    B. Kefa and Company Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/580/pr ...

  8. UVa 131 - The Psychic Poker Player

    题目:手里有五张牌,桌上有一堆牌(五张).你能够弃掉手中的k张牌,然后从牌堆中取最上面的k个. 比較规则例如以下:(按优先级排序) 1.straight-flush:同花顺.牌面为T(10) - A, ...

  9. Android 图片三级缓存之内存缓存(告别软引用(SoftRefrerence)和弱引用(WeakReference))

    因为之前项目同事使用了图片三级缓存,今天整理项目的时候发现同事还是使用了软引用(SoftRefrerence)和弱引用(WeakReference),来管理在内存中的缓存.看到这个我就感觉不对了.脑海 ...

  10. 应聘.net开发工程师常见的面试题(一)(转载)

    1. 简述 private. protected. public. internal 修饰符的访问权限. 答 . private : 私有成员, 在类的内部才可以访问. protected : 保护成 ...