这篇文章比较繁杂,主要就是把jQuery源码从上到下列出来,看我的注释就好了。
jQuery源码对各种加载器做了处理。
//阅读这个源码是请先了解一下概念,即时函数,工厂模式
(function( global, factory ) {
//这里之所以这样处理,是为了考虑CommonJS的环境,所以先检测是否有CommonJS的环境。
if ( typeof module === "object" && typeof module.exports === "object" ) {
// For CommonJS and CommonJS-like environments where a proper window is present,
// execute the factory and get jQuery
// For environments that do not inherently posses a window with a document
// (such as Node.js), expose a jQuery-making factory as module.exports
// This accentuates the need for the creation of a real window
// e.g. var jQuery = require("jquery")(window);
// See ticket #14549 for more info
//有CommonJS环境则输出一个方法
module.exports = global.document ?
//若全局环境有document,则工厂函数调用时传入第二个参数
factory( global, true ) :
//若全局环境没有document,则返回给CommonJS加载器一个留待以后调用的函数。
function( w ) {
if ( !w.document ) {
throw new Error( "jQuery requires a window with a document" );
}
return factory( w );
};
} else {
//如果没有CommonJS环境,则直接执行函数获得全局jQuery方法。
factory( global );
}
// Pass this if window is not defined yet
//若全局环境里没有window(可能为NodeJS),则调用上下文传入this。
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
上面最后的匿名函数相当于初始化函数,下面是该初始化函数的内容
// Can't do this because several apps including ASP.NET trace
// the stack via arguments.caller.callee and Firefox dies if
// you try to trace through "use strict" call chains. (#13335)
// Support: Firefox 18+
//这里我没看懂,补充链接
//http://developer.zdnet.com.cn/2007/0212/377947.shtml
//http://blog.csdn.net/arwindgao/article/details/6592357 //缓存各个方法到当前作用域,因为避免了每次调用slice等函数时要进行更深层次的查找,这样可以提升性能,更多方式可看《高性能Javascript》
var deletedIds = []; var slice = deletedIds.slice; var concat = deletedIds.concat; var push = deletedIds.push; var indexOf = deletedIds.indexOf;
//这个对象会在后面进行初始化,主要用来判断类型
var class2type = {}; var toString = class2type.toString; var hasOwn = class2type.hasOwnProperty; var trim = "".trim;
//这个后面用来保存支持性信息
var support = {}; var
version = "1.11.0", // Define a local copy of jQuery
//jQuery函数即是$函数,第一个参数是选择符,第二个参数是使用选择符查找的上下文环境
// 比如要在父DOM里查找某子元素,$('body',document)
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
}, // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
//此处的BOM是Byte Order Mark的缩写,字节序标记,为\uFEFF,必须去除
//\xA0则是latin01编码中的nbsp;
rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, // Matches dashed string for camelizing
//后面这里主要负责把dash格式的变量名(webkit-transform)转换为驼峰格式(webkitTransform)
rmsPrefix = /^-ms-/,
rdashAlpha = /-([\da-z])/gi, // Used by jQuery.camelCase as callback to replace()
//用来真正替换用的回调函数,后面真正用的时候的代码如下
//camelCase: function( string ) {
// return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
//},
fcamelCase = function( all, letter ) {
return letter.toUpperCase();
};
//须了解概念,原型和原型链。
//此处将jQuery工厂函数的原型设置为一个包含制定属性的对象。
jQuery.fn = jQuery.prototype = {
// The current version of jQuery being used
jquery: version,
//覆盖工厂函数的原型时必须保证constructor属性依旧指向原工厂函数
constructor: jQuery,
//最开始选择符为空
// Start with an empty selector
selector: "", // The default length of a jQuery object is 0
length: 0,
//调用数组的slice方法可以把this转换为数组。
//为什么要用这个方法?添加了自定义属性的jQuery对象还是数组,可以试试Array.isArray($('body'))
toArray: function() {
return slice.call( this );
}, // Get the Nth element in the matched element set OR
// Get the whole matched element set as a clean array
//传数区间为[-length,length]
//和后面eq的区别是,get返回DOM元素,eq返回jQuery对象
get: function( num ) {
return num != null ? // Return a 'clean' array
//如果传入的num为负数,则变为取从队尾开始数第N个元素
( num < 0 ? this[ num + this.length ] : this[ num ] ) : // Return just the object
//之所以不调用toArray,是因为同样只需要一行代码,减轻耦合以及函数调用的开销
slice.call( this );
}, // Take an array of elements and push it onto the stack
// (returning the new matched element set)
//压栈函数,因为后面由部分工具需要改变 当前插找DOM元素的上下文,所以需要保存当前的环境。
//后续函数实现依赖于此函数,凡是需要改变当前匹配元素集的操作,都需要进行压栈操作
pushStack: function( elems ) { // Build a new jQuery matched element set
//生成新的jQuery对象。
//为什么要用这种奇葩的方式生成,不能直接$(elems)生成?
var ret = jQuery.merge( this.constructor(), elems ); // Add the old object onto the stack (as a reference)
//prevObject指向之前的jQuery对象
ret.prevObject = this;
ret.context = this.context; // Return the newly-formed element set
return ret;
}, // Execute a callback for every element in the matched set.
// (You can seed the arguments with an array of args, but this is
// only used internally.)
//此处必须区分两个函数,jQuery.prototype.each和jQuery.each,
//前者给由jQuery工厂函数生成的对象来使用,后者是以jQuery工厂函数为命名空间,把工具函数each绑定到jQuery工厂函数上,避免了全局污染。
//前者通过调用后者来实现。
//后面的很多函数实现都是采用这种方式。
each: function( callback, args ) {
return jQuery.each( this, callback, args );
}, map: function( callback ) {
//因为map会生成新的jQuery对象,所以要pushStack
return this.pushStack( jQuery.map(this, function( elem, i ) {
return callback.call( elem, i, elem );
}));
}, slice: function() {
return this.pushStack( slice.apply( this, arguments ) );
}, first: function() {
return this.eq( 0 );
}, last: function() {
return this.eq( -1 );
},
//eq取第几个元素然后生成jQuery对象
eq: function( i ) {
var len = this.length,
j = +i + ( i < 0 ? len : 0 ); //j = +i < 0 ? +i + len:+i 原写法比较好的是减少一次隐式转换
return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
},
//对于本次jQuery对象的操作结束,返回压栈前的jQuery对象。
end: function() {
return this.prevObject || this.constructor(null);
}, // For internal use only.
// Behaves like an Array's method, not like a jQuery method.
push: push,
sort: deletedIds.sort,
splice: deletedIds.splice
};
//扩展对象函数
//接受三个参数,是否为深复制(可选),目标对象,源对象。
//之所以把深复制参数放在最前面,是因为方便增加源对象的个数。
jQuery.extend = jQuery.fn.extend = function() {
var src, copyIsArray, copy, name, options, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
//前面这几部分主要是为了复用,可以学习一下这种复用方式
// Handle a deep copy situation
//当传入的第一个参数是bool型
if ( typeof target === "boolean" ) {
deep = target; // skip the boolean and the target
//跳过深复制标识符,重新获得新的目标对象。
target = arguments[ i ] || {};
i++;
} // Handle case when target is a string or something (possible in deep copy)
//为什么target不能是Function?因为有可能覆盖Function的一些属性
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
} // extend jQuery itself if only one argument is passed
//如果只传进来一个对象,则说明目标对象为本jQuery对象。
if ( i === length ) {
target = this;
i--;
}
//真正的复制开始了
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null ) {
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];//待复制属性 // Prevent never-ending loop
//如果待复制属性为目标对象,则不进行此次复制。
if ( target === copy ) {
continue;
} // Recurse if we're merging plain objects or arrays
//如果进行深复制且待复制属性是朴素对象或队列
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
//深复制的时候要注意区分待复制的属性是对象还是数组
if ( copyIsArray ) {
copyIsArray = false; //使用此变量的目的是为了减少一次isArray的调用 clone = src && jQuery.isArray(src) ? src : []; } else {
clone = src && jQuery.isPlainObject(src) ? src : {};
} // Never move original objects, clone them
//不是要增加一个引用到待复制对象,而是克隆它们。
target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
} // Return the modified object
return target;
};
//定义完扩展函数,就可以使用它了,添加一堆工具函数
jQuery.extend({
// Unique for each copy of jQuery on the page
expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), // Assume jQuery is ready without the ready module
isReady: true, error: function( msg ) {
throw new Error( msg );
}, noop: function() {}, // See test/unit/core.js for details concerning isFunction.
// Since version 1.3, DOM methods and functions like alert
// aren't supported. They return false on IE (#2968).
//这个写法是处理之前遗留的bug
isFunction: function( obj ) {
return jQuery.type(obj) === "function";
},
//先检测是否有原生的isArray方法。
isArray: Array.isArray || function( obj ) {
return jQuery.type(obj) === "array";
},
//通过检测对象下是否有window属性。。这个可以伪造。。
isWindow: function( obj ) {
/* jshint eqeqeq: false */
return obj != null && obj == obj.window;
},
//是否为数字
isNumeric: function( obj ) {
// parseFloat NaNs numeric-cast false positives (null|true|false|"")
// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
// subtraction forces infinities to NaN
//当我们用typeof 来判断时,NaN和infinity都为number
//这种方法可以检测字符串是否可以转换为数字。
return obj - parseFloat( obj ) >= 0;
},
//检测空对象的方法
isEmptyObject: function( obj ) {
var name;
for ( name in obj ) {
return false;
}
return true;
},
//检测是否为朴素对象,不能为DOM和window,不能为由工厂函数生产出来的对象
//这里用了多种检测方式,值得学习
isPlainObject: function( obj ) {
var key; // 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 &&
!hasOwn.call(obj, "constructor") &&
!hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
return false;
}
} catch ( e ) {
// IE8,9 Will throw exceptions on certain host objects #9897
// In some very specific cases (ex: IE accessing window.location of another window)
return false;
} // Support: IE 0 && ( length - 1 ) in obj;
}
jQuery1.11源码分析(7)-----jQuery一些基本的API的更多相关文章
-
jQuery1.11源码分析(6)-----jQuery结构总揽
(在看以下内容之前请先对原型链有一定的了解,比如:prototype是对象还是函数?) 在看jQuery的其他源码之前,必须对jQuery的数据结构有一定的了解. jQuery的核心很简单,jQuer ...
-
jQuery1.11源码分析(8)-----jQuery调用Sizzle引擎的相关API
之所以把这部分放在这里,是因为这里用到了一些基本API,前一篇介绍过后才能使用. //jQuery通过find方法调用Sizzle引擎 //jQuery通过find方法调用Sizzle引擎 jQuer ...
-
jQuery1.11源码分析(1)-----Sizzle源码概览[原创]
最近在啃jQuery1.11源码,上来就遇到Sizzle这个jQuery的大核心,虽然已经清楚了Sizzle的用途,先绕过去也没事,但明知山有虎偏向虎山行才是我们要做的. 本文面向的阅读对象:正在学习 ...
-
jQuery1.11源码分析(5)-----Sizzle编译和过滤阶段[原创]
在上一章中,我们说到在之前的查找阶段我们已经获得了待选集seed,那么这一章我们就来讲如何将seed待选集过滤,以获得我们最终要用的元素. 其实思路本质上还是不停地根据token过滤,但compile ...
-
jQuery1.11源码分析(2)-----Sizzle源码中的正则表达式[原创]
看完了上篇,对Sizzle有了一个大致的了解,我们接下来就可以正式开始啃Sizzle的源码了.上来就讲matcher难度太大,先来点开胃菜,讲讲Sizzle中的各个正则表达式的作用吧(本来还想讲初始化 ...
-
jQuery1.11源码分析(9)-----初始化jQuery对象的函数和关联节点获取函数
这篇也没什么好说的,初始化jQuery对象的函数要处理多种情况,已经被寒冬吐槽烂了.关联节点获取函数主要基于两个工具函数dir和sibling,前者基于指定的方向遍历,后者则遍历兄弟节点(真的不能合并 ...
-
jQuery1.11源码分析(3)-----Sizzle源码中的浏览器兼容性检测和处理[原创]
上一章讲了正则表达式,这一章继续我们的前菜,浏览器兼容性处理. 先介绍一个简单的沙盒测试函数. /** * Support testing using an element * @param {Fun ...
-
jQuery1.11源码分析(10)-----Callbacks模块
Callbacks模块实质上就是一个回调函数队列(当然吹得很牛逼..比如“提供了一种强大的方法来管理回调函数队列”),之所以介绍它是因为后面的Derferred模块基于它. Callbacks生成时接 ...
-
jQuery1.11源码分析(4)-----Sizzle工厂函数[原创]
在用前两篇讲述完正则表达式.初始化.特性检测之后,终于到了我们的正餐——Sizzle工厂函数! Sizzle工厂函数有四个参数, selector:选择符 context:查找上下文 results: ...
随机推荐
-
Elasticsearch5.1.1+ik分词器+HEAD插件安装小记
一.安装elasticsearch 1.首先需要安装好java,并配置好环境变量,详细教程请看 http://tecadmin.net/install-java-8-on-centos-rhel-an ...
-
(String)151. Reverse Words in a String
Given an input string, reverse the string word by word. For example,Given s = "the sky is blue& ...
-
种类并查集(POJ 1703)
1703 -- Find them, Catch them http://poj.org/problem?id=1703 题目大意:有2个敌对帮派,输入D a b表示a,b在不同帮派,输入A a b表 ...
-
Unity3D 之UGUI 滚动条
先上效果图. 这里来说明下UGUI 滚动条,不涉及到代码. 主要用到的控件Scroll Rect ,Mask,Scrollbar. 第一步,建立一个Image,然后绑定一个滑动块的组件,添加一个mas ...
-
使用uWSGI+nginx部署Django项目
最近使用django写了一些项目,不过部署到服务器上碰到一些问题,还有静态文件什么的一堆问题,这里总结一下碰到的问题和解决方案,总体思路是按照官方文档走的. 原文地址:http://uwsgi-doc ...
-
github + SourceTree管理自己的库并上传到cocoapods及各种坑的解决办法
一.上传写好的库到github(我这里使用SourceTree客户端) 1.在github上创建一个仓库 2.将仓库拉倒本地 复制仓库地址 将刚才复制的地址粘贴到这里 3.上传项目到github 将写 ...
-
lpc1788IO口模拟IIC
#ifndef __MYIIC_H_ #define __MYIIC_H_ #include "common.h" #include "delay.h" #in ...
-
ASP.NET Core Web API 与 SSL
SSL 一直没有真正研究过SSL,不知道下面的理解是否正确. SSL是Secure Sockets Layer的缩写,它用来保护服务器和客户端之前的通信.它是基于信任+加密的概念. 在介绍SSL的原理 ...
-
Mac使用Xcode配置openGL
Mac使用Xcode配置openGL 博主这学期有图形学课要用到OpenGL,于是首先就开始配置开发环境了.应该说网上Windows上配置OpenGL教程比较多,Mac版的比较少.博主特来分享配置过程 ...
-
Linux删除文件名中包含“-”的文件
背景: 练习用shell的一些特殊符号,输出了一个 cat test.txt > -n,结果创建了一个叫做“-n”的文件 问题: 使用rm -f -n删除不了“-n"文件 解决 ...