dollar symbol
TRANSCRIPT
Dollar Symbol 深⼊入淺出Aaron Huang
1
Dollar Symbol 深⼊入淺出Aaron Huang
2
只進不出
Aaron Huang• Sr. F2E @
• F2E Lead @ Faria Systems • Cloud Service Engineer @ Waveface
• Xing “⾏行” - Evernote DevCup 2013 Top 6
https://jquery.org/history/
• Aug 22, 2005 - John Resig first hints
• Jan 14, 2006 - jQuery announced
• Jan, 2006 - First jQuery Plugin
• Aug, 2006 - v1.0 released
• Jul, 2007 - jQuery UI announced
• Aug, 2008 - jQuery Conference
• Jan, 2009 - Sizzle.js
• Nov, 2011 - jQuery Mobile 1.0
• Jan, 2013 - jQuery 1.9, 2.0-beta, jquery migrate
歷史
https://jquery.org/history/
• Aug 22, 2005 - John Resig first hints
• Jan 14, 2006 - jQuery announced
• Jan, 2006 - First jQuery Plugin
• Aug, 2006 - v1.0 released
• Jul, 2007 - jQuery UI announced
• Aug, 2008 - jQuery Conference
• Jan, 2009 - Sizzle.js
• Nov, 2011 - jQuery Mobile 1.0
• Jan, 2013 - jQuery 1.9, 2.0-beta, jquery migrate
• April 18, 2013 - jQuery 2.0
• Sep 19, 2013 - jQuery 1.11 and 2.1 Beta 1 Released
歷史
https://jquery.org/history/
• Jul, 2007 - jQuery UI announced
• Aug, 2008 - jQuery Conference
• Jan, 2009 - Sizzle.js
• Nov, 2011 - jQuery Mobile 1.0
• Jan, 2013 - jQuery 1.9, 2.0-beta, jquery migrate
• April 18, 2013 - jQuery 2.0
• Sep 19, 2013 - jQuery 1.11 and 2.1 Beta 1 Released
變⾰革
1.9 release & 2.0-beta
jquery.migrate
http://code.jquery.com/jquery-migrate-1.0.0.js
Module Dependency
Module Dependency
File concat
Module Dependency
File concat
Module Dependency
File concat
Module Dependency
File concat RequireJS
Module Dependency
File concat RequireJS
Module Dependency
File Concat (before v1.11)
•intro.js •core.js •selector.js •… •… •outro.js
File Concat (before v1.11)
•intro.js •core.js •selector.js •… •… •outro.js
jQuery.jsConcat
RequireJS (After v1.11)•core/ •var/ •event/ •…./ •jquery.js •core.js •event.js •selector.js •… •…
RequireJS (After v1.11)•core/ •var/ •event/ •…./ •jquery.js •core.js •event.js •selector.js •… •…
dependency resolve
r.js
RequireJS (After v1.11)•core/ •var/ •event/ •…./ •jquery.js •core.js •event.js •selector.js •… •…
dependency resolve
r.js convert
wrap
output
RequireJS (After v1.11)•core/ •var/ •event/ •…./ •jquery.js •core.js •event.js •selector.js •… •…
dependency resolve
r.js convert
wrap
output
RequireJS (After v1.11)•core/ •var/ •event/ •…./ •jquery.js •core.js •event.js •selector.js •… •…
dependency resolve
r.js convert
wrap
output
jQuery.js
熱⾝身⼀一下來看看 jQuery.ready 在不同版本下的實作差異
注:$(document).ready 沒事不要⽤用但是很多⼈人都⽤用過,所以拿來熱⾝身
// $.fn.ready #1$(document).ready(function(){ // callback content.});
// $.fn.ready #2$(function(){ // callback content.});
// $.fn.ready #1$(document).ready(function(){ // callback content.});
Challenge
// Kick off the DOM ready check even if the user does notjQuery.ready.promise();
Challenge
// Kick off the DOM ready check even if the user does notjQuery.ready.promise();
http://macb.in/do3E
Challenge
Contribution
Contribution
License
http://blog.jquery.com/2012/09/10/jquery-licensing-changes/
License
http://blog.jquery.com/2012/09/10/jquery-licensing-changes/
License
http://blog.jquery.com/2012/09/10/jquery-licensing-changes/
MIT/GPL
License
http://blog.jquery.com/2012/09/10/jquery-licensing-changes/
MIT/GPL
License
http://blog.jquery.com/2012/09/10/jquery-licensing-changes/
MIT/GPL
License
http://blog.jquery.com/2012/09/10/jquery-licensing-changes/
MIT/GPL
MIT
Sign the CLA(Contributor License Agreement)
http://contribute.jquery.org/CLA/
⼯工具
• vim
• vim • ctags
• vim • ctags • sliver_search or ack
Or Just use your browser
讓你的 GitHub ⽤用起來更⽜牛⼀一點
熟記 shortcut
熟記 shortcut按 “t” 就對了
Octotreehttp://sobolev.us/octotree/
Sourcegraphhttps://sourcegraph.com/
Github-findhttp://goo.gl/DR86r1
Take a break
jQuery.fn.inita jQuery Object
$('div')$('#id')$('.class')$('<div/>')
$('div')$('#id')$('.class')$('<div/>')
$('div')$('#id')$('.class')$('<div/>')
console.log($('#id')); //[<div id="id"></id>]
window.jQuery = window.$ = jQuery;
https://github.com/jquery/jquery/blob/master/src/exports/global.js
src/exports/global.js
// Define a local copy of jQuery jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); },
各種狀況判斷
各種狀況判斷(防衛
)
// A central reference to the root jQuery(document)var rootjQuery,
// A simple way to check for HTML strings // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
init = jQuery.fn.init = function( selector, context ) { var match, elem;
// HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; }
// 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 ); }
// A central reference to the root jQuery(document)var rootjQuery,
// A simple way to check for HTML strings // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
init = jQuery.fn.init = function( selector, context ) { var match, elem;
// HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; }
// 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 ); }
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
Selector 以外的狀況
// HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; }
// HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { return typeof rootjQuery.ready !== "undefined" ? rootjQuery.ready( selector ) : // Execute immediately if ready is not present selector( jQuery ); }
Selector & HTML
// A central reference to the root jQuery(document)var rootjQuery,
// A simple way to check for HTML strings // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
init = jQuery.fn.init = function( selector, context ) { var match, elem;
// HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; }
// 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 ); }
// A central reference to the root jQuery(document)var rootjQuery,
// A simple way to check for HTML strings // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
init = jQuery.fn.init = function( selector, context ) { var match, elem;
// HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; }
// 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 ); }
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
$('#id')$('<div/>')
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
Non-capturing
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
Non-capturing Group 1
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
Non-capturing Group 1 Group 2
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
Non-capturing Group 1 Group 2
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
Non-capturing Group 1 Group 2
match = rquickExpr.exec( selector );
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
Non-capturing Group 1 Group 2
match = rquickExpr.exec( selector );
• match[0] = // Matched String
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
Non-capturing Group 1 Group 2
match = rquickExpr.exec( selector );
• match[0] = // Matched String
• match[1] = // Tag html
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
Non-capturing Group 1 Group 2
match = rquickExpr.exec( selector );
• match[0] = // Matched String
• match[1] = // Tag html
• match[2] = // ID name
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
Non-capturing Group 1 Group 2
match = rquickExpr.exec( selector );
• match[0] = // Matched String
• match[1] = // Tag html
• match[2] = // ID name $('#id')
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/
Non-capturing Group 1 Group 2
match = rquickExpr.exec( selector );
• match[0] = // Matched String
• match[1] = // Tag html
• match[2] = // ID name $('#id')
$('<div/>'
)
// 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 ); }
// 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 ) { // Properties of context are called as methods if possible if ( jQuery.isFunction( this[ match ] ) ) { this[ match ]( context[ match ] );
// ...and otherwise set as attributes } else { this.attr( match, context[ match ] ); }
// ...and otherwise set as attributes } else { this.attr( match, context[ match ] ); } } }
return this;
// 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; }
this.context = document; this.selector = selector; return this; }
// 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 ); }
jQuery.find = Sizzle;
Sizzle core
function Sizzle( selector, context, results, seed )
node typeName ValueELEMENT_NODE 1
ATTRIBUTE_NODE 2
TEXT_NODE 3
CDATA_SECTION_NODE 4
ENTITY_REFERENCE_NODE 5
ENTITY_NODE 6
PROCESSING_INSTRUCTION_NODE 7
COMMENT_NODE 8
DOCUMENT_NODE 9
DOCUMENT_TYPE_NODE 10
DOCUMENT_FRAGMENT_NODE 11
NOTATION_NODE 12
node typeName ValueELEMENT_NODE 1
ATTRIBUTE_NODE 2
TEXT_NODE 3
CDATA_SECTION_NODE 4
ENTITY_REFERENCE_NODE 5
ENTITY_NODE 6
PROCESSING_INSTRUCTION_NODE 7
COMMENT_NODE 8
DOCUMENT_NODE 9
DOCUMENT_TYPE_NODE 10
DOCUMENT_FRAGMENT_NODE 11
NOTATION_NODE 12
var match, elem, m, nodeType, // QSA vars 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; }
var match, elem, m, nodeType, // QSA vars 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 ) {
// Try to shortcut find operations when possible (e.g., not under DocumentFragment) if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { // Speed-up: Sizzle("#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 ( !seed && documentIsHTML ) {
// Try to shortcut find operations when possible (e.g., not under DocumentFragment) if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { // Speed-up: Sizzle("#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
rquickExpr
rquickExprSizzle version
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
Group 1
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
Group 1 Group 2
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
Group 1 Group 2 Group 3
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
Group 1 Group 2 Group 3
match = rquickExpr.exec( selector );
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
Group 1 Group 2 Group 3
match = rquickExpr.exec( selector );• match[0] = // Matched String
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
Group 1 Group 2 Group 3
match = rquickExpr.exec( selector );• match[0] = // Matched String
• match[1] = // ID name
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
Group 1 Group 2 Group 3
match = rquickExpr.exec( selector );• match[0] = // Matched String
• match[1] = // ID name
• match[2] = // TAG name
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
Group 1 Group 2 Group 3
match = rquickExpr.exec( selector );• match[0] = // Matched String
• match[1] = // ID name
• match[2] = // TAG name
• match[3] = // CLASS name
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
Group 1 Group 2 Group 3
match = rquickExpr.exec( selector );• match[0] = // Matched String
• match[1] = // ID name
• match[2] = // TAG name
• match[3] = // CLASS name $(‘.class'
)
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/
Group 1 Group 2 Group 3
match = rquickExpr.exec( selector );• match[0] = // Matched String
• match[1] = // ID name
• match[2] = // TAG name
• match[3] = // CLASS name $(‘.class'
)$('di
v')
if ( !seed && documentIsHTML ) {
// Try to shortcut find operations when possible (e.g., not under DocumentFragment) if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { // Speed-up: Sizzle("#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 ( !seed && documentIsHTML ) {
// Try to shortcut find operations when possible (e.g., not under DocumentFragment) if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { // Speed-up: Sizzle("#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
} 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 { // Context is not a document if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && contains( context, elem ) && elem.id === m ) { results.push( elem ); return results; } }
e.g.,$(‘body’).find(‘#id’)
// Speed-up: Sizzle("TAG") } 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; } }
// Speed-up: Sizzle("TAG") } 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; } }
var push = [].push, slice = [].slice;
Function.prototype.apply()fun.apply(thisArg[, argsArray])
Function.prototype.apply()fun.apply(thisArg[, argsArray])
Function.prototype.call()fun.call(thisArg[, arg1[, arg2[, ...]]])
document.querySelectAll
// QSA path if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { nid = old = expando; newContext = context; newSelector = nodeType !== 1 && selector;
// qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root // and working up from there (Thanks to Andrew Dupont for the technique) // IE 8 doesn't work on object elements if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { groups = tokenize( selector );
if ( (old = context.getAttribute("id")) ) { nid = old.replace( rescape, "\\$&" ); } else { context.setAttribute( "id", nid ); } nid = "[id='" + nid + "'] ";
i = groups.length; while ( i-- ) { groups[i] = nid + toSelector( groups[i] ); } newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; newSelector = groups.join(","); }
Challenge
// qSA works strangely on Element-rooted queries// We can work around this by specifying an extra ID on the root// and working up from there (Thanks to Andrew Dupont for the technique)// IE 8 doesn't work on object elements
http://macb.in/1fDSb
Challenge
// qSA works strangely on Element-rooted queries// We can work around this by specifying an extra ID on the root// and working up from there (Thanks to Andrew Dupont for the technique)// IE 8 doesn't work on object elements
// QSA path if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { nid = old = expando; newContext = context; newSelector = nodeType !== 1 && selector;
// qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root // and working up from there (Thanks to Andrew Dupont for the technique) // IE 8 doesn't work on object elements if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { groups = tokenize( selector );
if ( (old = context.getAttribute("id")) ) { nid = old.replace( rescape, "\\$&" ); } else { context.setAttribute( "id", nid ); } nid = "[id='" + nid + "'] ";
i = groups.length; while ( i-- ) { groups[i] = nid + toSelector( groups[i] ); } newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; newSelector = groups.join(","); }
if ( newSelector ) { try { push.apply( results, newContext.querySelectorAll( newSelector ) ); return results; } catch(qsaError) { } finally { if ( !old ) { context.removeAttribute("id"); } } } }
Take a break
Deferred⾯面試必備的知識
Async JavaScript好書推薦給你
• Callback
• Callback• Event
• Callback• Event• Promise
Callback
Callback
Callback
Callback
Callback Hell
Events
Promise patternhttp://wiki.commonjs.org/wiki/Promises
Promise patternhttp://wiki.commonjs.org/wiki/Promises
許你⼀一個承諾
Promise patternhttp://wiki.commonjs.org/wiki/Promises
許你⼀一個承諾
jQuery.Deferred()
• A constructor function that returns a chainable utility object with methods to register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function.
• jQuery Deferred is based on the CommonJS Promises/A design.
郭董說
$.when(OpenDataAt(172800)) .then(resumeWork) .fail(undefined);
郭董說
$.when(OpenDataAt(172800)) .then(resumeWork) .fail(undefined);
郭董說
48 ⼩小時內公開資料
$.when(OpenDataAt(172800)) .then(resumeWork) .fail(undefined);
郭董說
48 ⼩小時內公開資料
復⼯工
$.when(OpenDataAt(172800)) .then(resumeWork) .fail(undefined);
郭董說
48 ⼩小時內公開資料
復⼯工
不然他也不知道該怎麼辦
• .ready • .ajax • .animate
$.Deferred 解析
了解 Deferred
了解 Deferred
•⾏行為
了解 Deferred
•⾏行為
•假設
了解 Deferred
•⾏行為
•假設
•狀態
了解 Deferred
•⾏行為
•假設
•狀態
(resolve, reject, notify)
了解 Deferred
•⾏行為
•假設
•狀態
(resolve, reject, notify)
(done, fail, progress)
了解 Deferred
•⾏行為
•假設
•狀態
(resolve, reject, notify)
(done, fail, progress)
(resolved, rejected, pending)
• deferred.promise() -> state: pending
• deferred.promise() -> state: pending
• deferred.resolve() -> deferred.done_stack -> state: resolved
• deferred.promise() -> state: pending
• deferred.resolve() -> deferred.done_stack -> state: resolved
• deferred.reject() -> deferred.fail_stack -> state: rejected
Deferred 建構式
Deferred: function( func ) { var tuples = [ // action, add listener, listener list, final state [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], state = "pending", promise = { state: function() { return state; }, always: function() { deferred.done( arguments ).fail( arguments ); return this; }, then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return jQuery.Deferred(function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer deferred[ tuple[1] ](function() { var returned = fn && fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) {
var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ],
var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ],
⾏行為
var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ],
⾏行為 假設
var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ],
⾏行為 假設 狀態
var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ],
⾏行為 假設 狀態Callback list
var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ],
⾏行為 假設 狀態
0Callback list
var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ],
⾏行為 假設 狀態
0 1Callback list
var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ],
⾏行為 假設 狀態
0 1 2Callback list
var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ],
⾏行為 假設 狀態
0 1 2Callback list
3
想⼀一想
想⼀一想
• Deferred 會建構⼀一個全新並獨⽴立的 callbacks list
想⼀一想
• Deferred 會建構⼀一個全新並獨⽴立的 callbacks list
• 三個⾏行為定義的 function 幾乎⼀一樣
想⼀一想
• Deferred 會建構⼀一個全新並獨⽴立的 callbacks list
• 三個⾏行為定義的 function 幾乎⼀一樣
• Callbacks list 提供了所有的必要⽅方法, 包含 add, remove, fire, disable
想⼀一想
• Deferred 會建構⼀一個全新並獨⽴立的 callbacks list
• 三個⾏行為定義的 function 幾乎⼀一樣
• Callbacks list 提供了所有的必要⽅方法, 包含 add, remove, fire, disable
• Resolve 跟 Reject 是相對的
想⼀一想
• Deferred 會建構⼀一個全新並獨⽴立的 callbacks list
• 三個⾏行為定義的 function 幾乎⼀一樣
• Callbacks list 提供了所有的必要⽅方法, 包含 add, remove, fire, disable
• Resolve 跟 Reject 是相對的
• resolve() 與 reject() 其中⼀一⽅方作動, 另⼀一個的 callbacks 則觸發 .disabled()
deferred = {}; // Add list-specific methods jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 3 ];
// promise[ done | fail | progress ] = list.add promise[ tuple[1] ] = list.add;
// Handle state if ( stateString ) { list.add(function() { // state = [ resolved | rejected ] state = stateString;
// [ reject_list | resolve_list ].disable; progress_list.lock }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); }
// deferred[ resolve | reject | notify ] deferred[ tuple[0] ] = function() { deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); return this; }; deferred[ tuple[0] + "With" ] = list.fireWith; });
list.add(function() { // state = [ resolved | rejected ] state = stateString;
// [ reject_list | resolve_list ].disable; progress_list.lock }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); }
// deferred[ resolve | reject | notify ] deferred[ tuple[0] ] = function() { deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); return this; }; deferred[ tuple[0] + "With" ] = list.fireWith; }); // Make the deferred a promise promise.promise( deferred );
// Call given func if any if ( func ) { func.call( deferred, deferred ); }
// All done! return deferred; },
再回到建構式
Deferred: function( func ) { var tuples = [ // action, add listener, listener list, final state [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], state = "pending", promise = { state: function() { return state; }, always: function() { deferred.done( arguments ).fail( arguments ); return this; }, then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return jQuery.Deferred(function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer deferred[ tuple[1] ](function() { var returned = fn && fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise()
then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return jQuery.Deferred(function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer deferred[ tuple[1] ](function() { var returned = fn && fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() .done( newDefer.resolve ) .fail( newDefer.reject ) .progress( newDefer.notify ); } else { newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); } }); }); fns = null; }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; }
.fail( newDefer.reject ) .progress( newDefer.notify ); } else { newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); } }); }); fns = null; }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {};
done vs then
$.Deferred(function(dfd){ dfd .done(function(){ $('#foo').fadeIn(); }) .done(function(){ $('#bar').fadeOut(); });}).resolved();
$.Deferred(function(dfd){ dfd .then(function(){ $('#foo').fadeIn(); }) .then(function(){ $('#bar').fadeOut(); });}).resolved();
done
• deferred 對象始終是同⼀一個
• done 定義的 callback 都在同⼀一個 stack list
• resolve 之後 callbacks 是近乎同時呼叫
• 不應該預期 callbacks 會照順序執⾏行
then
• 每次都會建構⼀一個新的 deferred 物件
• then 定義的 callback 都是在⼀一個獨⽴立的 callbacks list
• 順序呼叫多次不同 deferred 物件的 resolved
• 預期是循序執⾏行
尾聲
再推薦兩本必須看完的書
JavaScript:The Good Parts
Eloquent JavaScript
2nd edition
⼈人不⼀一定要⾛走出⾃自⼰己的舒適圈
⼈人不⼀一定要⾛走出⾃自⼰己的舒適圈
或許也可以考慮擴⼤大⾃自⼰己的舒適圈
Question
前端⼯工程師 ⽕火熱加開中
http://jobs.kktix.cc