jQuery源码剖析学习笔记

jQuery源码剖析(一)

1. 沙箱的第二个参数undefined

(function( window, undefined ) {
//用一个函数域包起来,就是所谓的沙箱
//在这里边var定义的变量,属于这个函数域内的局部变量,避免污染全局
//把当前沙箱需要的外部变量通过函数参数引入进来
//只要保证参数对内提供的接口的一致性,你还可以随意替换传进来的这个参数
"use strict";
window.jQuery = window.$ = jQuery;
})( window );

作用1:压缩

(function( window, undefined ) {
var a = undefined;
if (a == undefined){blabla...}

....
if (c == undefined) return;
})
( window );

(function(w, u) {
var a = u;
if (a == u){blabla...}

....
if (c == u) return;
})
(w);

作用2:防止undefined值被篡改

在ECMAScript5之前undefined都是可写的,也就是undefined可以赋值的。jQuery作者这么做的目的还有防止2B程序员对undefined进行赋值后使得代码出现了不可预料的bug。、

2. 存储数组和对象方法的原因

class2type = {},
core_deletedIds = [],
core_version = "1.9.0",

//Save a reference to some core methods
core_concat = core_deletedIds.concat,
core_push = core_deletedIds.push,
core_slice = core_deletedIds.slice,
core_indexOf = core_deletedIds.indexOf,
core_toString = class2type.toString,
core_hasOwn = class2type.hasOwnProperty,
core_trim = core_version.trim,

//等同以下代码:
core_concat = Array.prototype.concat,
//文章一开始的介绍有稍微提到prototype
//core_deletedIds是一个数组实例
//core_deletedIds.concat方法就相当于调了Array类中的成员方法concat。

作用1:效率问题

调用实例arr的方法concat时,首先需要辨别当前实例arr的类型是Array,在内存空间中寻找Array的concat内存入口,把当前对象arr的指针和其他参数压入栈,跳转到concat地址开始执行。 当保存了concat方法的入口core_concat时,完全就可以省去前面两个步骤,从而提升一些性能。

作用2:防止报错

var obj = {}; 此时调用obj.concat是非法的,但是如果jQuery采用上边方式二或者三的话,能够解决这个问题。 也即是让类数组也能用到数组的方法(这就是call跟apply带来的另一种用法),尤其在jQuery里边引用一些DOM对象时,也能完美的用这个方法去解决

3. $.inArray

core_deletedIds = [],
core_indexOf = core_deletedIds.indexOf,
//相当于 core_indexOf = Array.indexOf;

//elem 规定需检索的值。
//arr 数组
//i 可选的整数参数。规定在数组中开始检索的位置。它的合法取值是 0 到 arr.length - 1。如省略该参数,则将从数组首元素开始检索。
inArray: function( elem, arr, i ) {
var len;

if ( arr ) {
//原生的Array对象支持indexOf方法,直接调用
if ( core_indexOf ) {
return core_indexOf.call( arr, elem, i );
}

len = arr.length;
//当i为负数的时候,从数组后边len+i的位置开始索引
i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;

for ( ; i < len; i++ ) {
// Skip accessing in sparse arrays
//jQuery这里的(i in arr)判断是为了跳过稀疏数组中的元素
//例如 var arr = []; arr[2] = 1;
//此时 arr == [undefined, undefined, 1]
//结果是 => (0 in arr == false) (1 in arr == false) (2 in arr == true)
//但是不解的是这里
//测试了一下 $.inArray(undefined, arr, 0)是返回-1的

//---------------------attention---------------------------
if ( i in arr && arr[ i ] === elem ) {
//---------------------attention---------------------------

return i;
}
}
}

//全部都不符合,返回-1
return -1;
},

稀疏数组指 var arr = []; arr[1] = 1; 这种,此时 arr[0] === undefinedtrue ,但是期望得到的结果是 -1

4. $.grep

callback(value, key) ,注意与 $.eachcallback(key, value) 相区别

5. $.map

  • callback(value, key) ,注意与 $.eachcallback(key, value) 相区别
  • return 的特殊写法:
core_deletedIds = [],
core_concat = core_deletedIds.concat,
// arg is for internal usage only
map: function( elems, callback, arg ) {
var value,
i = 0,
length = elems.length,
isArray = isArraylike( elems ),
ret = [];

// 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 ) {//如果返回值是null,则不加入结果中
ret[ ret.length ] = value;
}
}

// Go through every key on the object,
} else {
for ( i in elems ) {
value = callback( elems[ i ], i, arg );

if ( value != null ) {
ret[ ret.length ] = value;
}
}
}

// Flatten any nested arrays
//这里相当于 var a = [];a.concat(ret)

//---------------------attention---------------------------
return core_concat.apply( [], ret );
//---------------------attention---------------------------

},

return concat 的原因:

$.map( [0,1,2], function(n){
return [ n, n + 1 ];
});
//输出:[0, 1, 1, 2, 2, 3]
//如果是return ret的话,输出将会是:[[0,1], [1,2], [2,3]]

6. $.trim

core_version = "1.9.0",
core_trim = core_version.trim,
rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,

//---------------------attention---------------------------
trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
//---------------------attention---------------------------

function( text ) {
return text == null ?
"" :
core_trim.call( text );
} :

// Otherwise use our own trimming functionality
function( text ) {
return text == null ?
"" :
( text + "" ).replace( rtrim, "" );
}

var core_trim = String.prototype.trim; if (core_trim && !core_trim.call("\uFEFF\xA0")) 相当于: if (String.prototype.trim && "\uFEFF\xA0".trim() == "") 高级的浏览器已经支持原生的 Stringtrim 方法,但是jQuery还为了避免它没法解析全角空白,所以加多了一个判断:"\uFEFF\xA0".trim() == ""

\uFEFF是utf8的字节序标记, “\xA0”是全角空格 如果以上条件成立了,那就直接用原生的trim函数就好了

7. $.proxy

  • 用法:

返回一个新函数,并且这个函数始终保持了特定的作用域。
当有事件处理函数要附加到元素上,但他们的作用域实际是指向另一个对象时,这个方法最有用了。
此外,最妙的是,jQuery能够确保即便你绑定的函数是经过jQuery.proxy()处理过的函数,你依然可以传递原先的函数来准确无误地取消绑定。
这个函数还有另一种用法,jQuery.proxy( scope, name )。第一个参数是要设定的作用域对象。第二个参数是将要设置作用域的函数名(必须是第一个作用域对象的一个属性)。

var obj = {
name: "John",
test: function() {
alert( this.name );
$("#test").unbind("click", obj.test);
}
};

$("#test").click( jQuery.proxy( obj, "test" ) ); //弹出John

// 以下代码跟上面那句是等价的:
$("#test").click( jQuery.proxy( obj.test, obj ) );

// 可以与单独执行下面这句做个比较。
$("#test").click( obj.test ); // 弹出$("#test")的name
  • guid????

8. $.type

原生 typeoftoString

typeof 1 == 'number'
typeof {} == 'object'
typeof [] == 'object'
(1).toString() == "1"
({}).toString() == "[object Object]"
//再针对一些边界的测试,
typeof null == "object"
typeof undefined == "undefined"
(null).toString()//非法
(undefined).toString()//非法

//再看看很诡异的几个:
([]).toString() == ""
(new Error()).toString() == "Error"
//出现以上两个的结果的原因是,Array跟Error类重写了toSting方法
//如果用Object的toString方法的话,就是以下结果
Object.prototype.toString.call([]) == "[object Array]"
Object.prototype.toString.call(new Error()) == "[object Error]"

所以在判断变量类型时,用Object.prototype.toString.call()更靠谱些
【ps:直接用{}.toString.call() 会报错,但是var obj = {}; obj.toString.call() 就可以,不懂为什么?????】

9. isXXX系列

  • isWindow
isWindow: function( obj ) {
return obj != null && obj == obj.window;
},

为什么要先判断不为空??????

  • isNumeric
isNumeric: function( obj ) {
return !isNaN( parseFloat(obj) ) && isFinite( obj );
},

parseFloat:如果在解析过程中遇到了正负号(+或-),数字(0-9),小数点,或者科学记数法中的指数(e或E)以外的字符,则它会忽略该字符以及之后的所有字符,返回当前已经解析到的浮点数.同时参数字符串首位的空白符会被忽略.

如果参数字符串的第一个字符不能被解析成为数字,则parseFloat返回NaN.

parseFloat 也可转换和返回Infinity值.

  • 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
if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
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 );
},

to be continued。。。

不懂的地方,请指教

1. $.makeArray

问题见注释中的attention:

makeArray: function( arr, results ) {
var ret = results || [];//不由得不赞js这个技巧
//等同于:var ret = (!results) ? [] : results;

if ( arr != null ) {

//---------------------attention---------------------------
//此处不太懂,如果arr是一个字符串的话,为什么不直接走else push到结果中?而在此处大费周折呢?
if ( isArraylike( Object(arr) ) ) {
//如果arr是一个类数组对象,调用merge合到返回值
jQuery.merge( ret,
typeof arr === "string" ?
[ arr ] : arr
);
//---------------------attention---------------------------

} else {//如果不是数组,则将其放到返回数组末尾
//等同于ret.push(arr);
core_push.call( ret, arr );
}
}

return ret;
},

原文错误指正

  • 2.10 $.nodeName
    源码问题:
nodeName: function( elem, name ) {
//IE下,DOM节点的nodeName是大写的,例如DIV
//所以统一转成小写再判断
//这里不return elem.nodeName.toLowerCase();
//我认为原因是为了保持浏览器自身的对外的规则,避免所有引用nodeName都要做转换的动作
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
//可以看到这里的&&使用技巧,以上代码等同于:
//if (elem.nodeName) return elem.nodeName.toLowerCase() === name.toLowerCase();
//else return elem.nodeName;
//如此之简洁
},

这样如果elem.nodeName不为空,就会与name进行比较;为空,则返回空字符串,是无法获取节点名称的。

  • 2.1 $.trim

var core_trim = String.prototype.trim; if (core_trim && !core_trim.call("\uFEFF\xA0")) 相当于: if (String.prototype.trim && "\uFEFF\xA0".trim() !== "")
应该是"\uFEFF\xA0".trim() == ""

更多相关文章

  1. 创建一个未排序的数组,其中包含重复元素和唯一元素的总和
  2. js不使用jquery,调用ajax,传递数组,并接受,
  3. 如何将表列转换为数组?
  4. 如何修复JSON对象的假数组?
  5. 如何从SQL SELECT查询中的c#变量创建jQuery数组
  6. jQuery Ui Draggable在移动端浏览器不起作用解决方案
  7. 导入地址簿联系人,存储在数组中并保存到数据库
  8. 回发后,jquery datepicker ms ajax updatepanel不起作用
  9. jquery解析php通过ajax传过来的json二维数组对象

随机推荐

  1. 这被认为是正常形式的失败吗?
  2. 建议一种有效的查询方式
  3. Code First for Mysql 错误:未为提供程序
  4. sql语句,order by后加参数问题
  5. mybatis中 insert into select 批量生成u
  6. ORA-19909:当进行不完全恢复之后使用open
  7. sql 2005判断某个表或某个表中的列是否存
  8. MySQL中的类Decode用法
  9. 如何优化用于从表复制数据的oracle过程?
  10. SQL关键字转换大写核心算法实现