Comments (5)
关于jQuery.extend 和 jQuery.fn.extend
jQuery.extend = jQuery.fn.extend = function(){
//对jQuery.fn的扩展
};
jQuery.extend({
//对jQuery本身的扩展
})
jQuery.extend 和 jQuery.fn.extend的区别
啊,实际上没什么区别,你没看见那个 = 号码?
只是你在用的时候,jQuery.extend是对jQuery本身的扩展,而jQuery.fn.extend是对jQuery.fn的扩展。
jQuery.extend是怎么实现的?
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
定义一堆待会可能用的变量~提前定义是为了避免变量提升哟
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
// Skip the boolean and the target
target = arguments[ i ] || {};
i++;
}
target初始值为arguments[0],即深赋值还是浅赋值。如果传值,则修正target的值为arguments[1];
i 的初始值为1,表示期望源对象从第2个元素开始,当第一个参数为布尔值时,就期望源对象从第3个元素开始了!
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
如果目标对象不是对象,不是函数(也就是有可能传进字符串或者其他的基本类型,而基本类型上设置非原生属性是无效的。参考红宝书P118-119)则统统设置为{}
// Extend jQuery itself if only one argument is passed
if ( i === length ) {
target = this;
i--;
}
i表示源对象开始的下标,length表示arguments的个数,如果他们值相等,则说明源对象并没有传进来。则把jQuery或者jQuery.fn作为目标对象,并且把传进来的对象当做源对象。
出现这种情况当然是1、传进来一个参数:extend(object);2传进来两个参数:extend(deep,object) //deep为布尔值
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;
}
第一个if语句中将赋值和判断都做了,jQuery果然精炼
遍历optons时,判断了源对象和目标对象是否相等,如果相等为了避免深度遍历时死循环,因此continue了。
// Recurse if we're merging plain objects or arrays
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 : {};
}
如果copy是数组,则将src也应该为数组[],如果copy是对象,则src也应该为对象{}
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
先把copy递归合并到clone中,然后覆盖目标对象的同名属性。
// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
如果不是深度合并,并且copy不是undefined,则直接覆盖目标对象的同名属性
// Return the modified object
return target;
};
from candywxt.
jQuery.fn.init(selector, context, rootjQuery)
构造函数jQuery.fn.init负责解析参数selector和context类型,并执行相应的逻辑,最后返会jQuery.fn.init的实例。
init = jQuery.fn.init = function( selector, context){
var match, elem;
if(!selector) {
return this;
}
if(typeof selector === 'string'){...}
else if( selector.nodeType ) {...}
else if( jQuery.isFunction( selector )) {...}
if( selector.selector !== undefined ) {...}
return jQuery.makeArray( selector,this )
}
这是init函数的大致结构,很明显,如果selector不存在,则直接返回this
接下来我们看简单的几种情况
selector是DOM元素时(即有nodeType时)
用法示例 $(document.body)
else if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
}
手动设置第一个元素和属性context指向该DOM元素,返回包含了该DOM元素引用的jQuery对象
selector是String时
// Handle HTML strings
if ( typeof selector === "string" ) {
if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) {
// Assume that strings that start and end with <> are HTML and skip the regex check
match = [ null, selector, null ];
} else {
match = rquickExpr.exec( selector );
}
如果selector<div>
样子的,则跳过rquickExpr检查,暂且将match设置为一个头尾为null的数组,否则将检查它
rquickExpr
是这个样子的:
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
这个正则表达式匹配#id
时返回的结果为["#id",undefined,"id"]
,所以如果匹配该正则,会用到document.getElementById(match[2]);
回到第一种情况,如果match为[null, selector, null]
时,即match[1]存在,context存在与否无关
// Match html or make sure no context is specified for #id
if ( match && (match[1] || !context) ) {
// HANDLE: $(html) -> $(array)
if ( match[1] ) {
context = context instanceof jQuery ? context[0] : context;
// Option to run scripts is true for back-compat
// Intentionally let the error be thrown if parseHTML is not present
jQuery.merge( this, jQuery.parseHTML(
match[1],
context && context.nodeType ? context.ownerDocument || context : document,
true
) );
// HANDLE: $(html, props)
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
for ( match in context ) {
注意:在这里match改变了!不再是数组,而是context里的key !而此时的this是$(match[1])
// Properties of context are called as methods if possible
if ( jQuery.isFunction( this[ match ] ) ) {
this[ match ]( context[ match ] );
如果$(match[1])[context[key]]是function,则执行他
// ...and otherwise set as attributes
} else {
this.attr( match, context[ match ] );
}
}
}
return this;
如果不是function,则认为是attr,则添加attr
jQuery.attr稍后再看!
如果match此时为 ["#id",undefined,"id"]
// HANDLE: $(#id)
} else {
elem = document.getElementById( match[2] );
// Support: Blackberry 4.6
// gEBID returns nodes no longer in the document (#6963)
if ( elem && elem.parentNode ) {
// Inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;
}
this.context = document;
this.selector = selector;
return this;
}
没错,此时调用了getElementById(match[2]);
selector是选择器表达式
用法示例:$('div p')
此时分支依然在字符串中,参数selector不是单独标签,复杂HTML代码,#id,而是选择器表达式。如果没有指定上下文,则执行rootjQuery.find(selector);如果指定了上下文,且上下文是jQuery对象,则执行context.find( selector ),如果指定了上下文但上下文不是jQuery对象,则执行this.constructor( context ).find( selector ),即先创建一个包含了context的jQuery对象,然后在该对象上调用方法.find()
// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
return ( context || rootjQuery ).find( selector );
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return this.constructor( context ).find( selector );
}
selector 是function的情况下
用法示例:$(function(){...})
} else if ( jQuery.isFunction( selector ) ) {
return typeof rootjQuery.ready !== "undefined" ?
rootjQuery.ready( selector ) :
selector( jQuery );
}
如果selector是函数,则认为是绑定了ready事件。也就是$(function)是$(document).ready(function)的简写~
selector 是jQuery对象
if ( selector.selector !== undefined ) {
this.selector = selector.selector;
this.context = selector.context;
}
如果参数selector含有属性selector则认为他是jQuery对象。
return jQuery.makeArray( selector, this );
};
最后返回jQuery对象
makeArray是做什么的
看函数名知道是将selector变成Array,这也就是为什么jQuery对象上返回的都是数组类型了。
from candywxt.
数组操作方法
不知道你还记不记得jQuery.merge()和jQuery.makeArray()先说这个吧!
jQuery.merge
merge: function( first, second ) {
var len = +second.length,
j = 0,
i = first.length;
for ( ; j < len; j++ ) {
first[ i++ ] = second[ j ];
}
first.length = i;
return first;
}
很巧妙的将i设置为second.length,+是将其类型转换为number。
jQuery.makeArray
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;
}
jQuery.indexOf
indexOf = function( list, elem ) {
var i = 0,
len = list.length;
for ( ; i < len; i++ ) {
if ( list[i] === elem ) {
return i;
}
}
return -1;
}
jQuery.inarray
inArray: function( elem, arr, i ) {
return arr == null ? -1 : indexOf.call( arr, elem, i );
}
其他的数组方法我们等用到的时候再说~~
下面来看下Sizzle~
from candywxt.
Sizzle
Sizzle 恐怕是jQuery里面精华部分之一了。新手喜欢用jQuery也是因为它强大的选择器。所以现在就来解刨它。看看究竟长什么样。
总体结构
function Sizzle(selector, context, results, seed) {
var match, elem, m, nodeType,
i, groups, old, nid, newContext, newSelector;
if( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
setDocument( context );
}
context = context || document;
results = results || [];
nodeType = context.nodeType;
if ( typeof selector !== 'string' || !selector ||
nodeType !== 1 && nodeType !== 9 && nodeType !== 11) {
return results;
}
if (!seed && documentIsHTML) {
//something
}
return select( selector.replace( rtrim, '$1' ), context, results, seed );
}
大概看了一眼,如果上下文存在但是不是document,那么将它上下文变成document,如果selector不是string,不是element,document,fragment那么返回results
来看一眼select
select
select: {
get: function( elem ) {
var value, option,
options = elem.options,
index = elem.selectedIndex,
one = elem.type === "select-one" || index < 0,
values = one ? null : [],
max = one ? index + 1 : options.length,
i = index < 0 ?
max :
one ? index : 0;
// Loop through all the selected options
for ( ; i < max; i++ ) {
option = options[ i ];
// IE6-9 doesn't update selected after form reset (#2551)
if ( ( option.selected || i === index ) &&
// Don't return options that are disabled or in a disabled optgroup
( support.optDisabled ? !option.disabled :
option.getAttribute( "disabled" ) === null ) &&
( !option.parentNode.disabled ||
!jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
// Get the specific value for the option
value = jQuery( option ).val();
// We don't need an array for one selects
if ( one ) {
return value;
}
// Multi-Selects return an array
values.push( value );
}
}
return values;
}
好像没什么卵用
我们还是看if( !seed && documentIsHTML)
里面是什么吧
if (!seed && documentIsHTML) {
if( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
if( (m = match[1]) ){
} else if(match[2]){
} else if(m = match[3] && support.getElementsByClassName ) {
}
}
if (support.qsa && (!rbuggyQSA || rbuggyQSA.test( selector )) ){
}
}
两个大分支,三个小分支。
给出rquickExpr
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
match[1] -> id match[2] -> tag match[3] -> class
看第一个分支的ID情况
if ( (m = match[1]) ) {
if ( nodeType === 9 ) {
elem = context.getElementById( m );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document (jQuery #6963)
if ( elem && elem.parentNode ) {
// Handle the case where IE, Opera, and Webkit return items
// by name instead of ID
if ( elem.id === m ) {
results.push( elem );
return results;
}
} else {
return results;
}
} else {
// Context is not a document
if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
contains( context, elem ) && elem.id === m ) {
results.push( elem );
return results;
}
}
else if ( match[2] ) {
push.apply( results, context.getElementsByTagName( selector ) );
return results;
// Speed-up: Sizzle(".CLASS")
}
else if ( (m = match[3]) && support.getElementsByClassName ) {
push.apply( results, context.getElementsByClassName( m ) );
return results;
}
from candywxt.
异步队列Defferred Object
总体结构
jQuery.Callbacks = function( options ) {...}
jQuery.extend({
Deferred: function( func ) {...},
when: function( subordinate ) {...}
})
Callbcks( options ),返回一个链式工具对象,用于管理一组回调函数。回调函数列表支持添加,移除,触发,锁定和禁用回调函数。为jQuery.ajax(),jQuery.Deferred()和ready事件提供基础功能。
jQuery.Callbacks( options )
jQuery.Callbacks = function( options ) {
options = typeof options === "string" ?
( optionsCache[ options ] || createOptions( options ) ) :
jQuery.extend( {}, options );
var // Last fire value (for non-forgettable lists)
memory,
// Flag to know if list was already fired
fired,
// Flag to know if list is currently firing
firing,
// First callback to fire (used internally by add and fireWith)
firingStart,
// End of the loop when firing
firingLength,
// Index of currently firing callback (modified by remove if needed)
firingIndex,
// Actual callback list
list = [],
// Stack of fire calls for repeatable lists
stack = !options.once && [],
// Fire callbacks
fire = function (data) {...},
self = {...};
return self
}
self为Callbacks返回的对象。来看一下self是什么
self = {
add:
disable:
disabled:
empty:
fire:
fireWith:
fired:
has:
lock:
locked:
remove:
}
self内部有一个add函数,用于向list中添加回调函数。工具函数fire()使用指定的上下文context,和参数args调用数组list中的回调函数。该函数通过闭包机制引用数组list。
fire函数
fire = function( data ) {
memory = options.memory && data;
fired = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
memory = false; // To prevent further calls using add
break;
}
}
firing = false;
if ( list ) {
if ( stack ) {
if ( stack.length ) {
fire( stack.shift() );
}
} else if ( memory ) {
list = [];
} else {
self.disable();
}
}
}
参数data[0]用于指定回调函数执行时的上下文,即关键字this所引用的对象。data[1]用于指定调用回调函数时传入的参数。
变量memory的用法如下
可能值 | 回调函数列表状态 | 含义和场景 |
---|---|---|
undefined | 未被触发或已被禁用 | |
[context, args] | 已被触发 | 以下场景之一:1 memory + 非stopOnFalse模式 2:memory + stopOnFalse模式 + 回调函数的返回值不是false |
true | 已被触发 | 以下场景之一:1 非memory 模式 2 stopOnFalse模式 + 某个回调函数的返回值是false |
jQuery.Deferred( func )
jQuery.when( deferreds )
异步队列在jQuery中的应用
from candywxt.
Related Issues (12)
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from candywxt.