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
•
歷史
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/
• 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 not
jQuery.ready.promise();
Challenge
// Kick off the DOM ready check even if the user does not
jQuery.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” 就對了
Octotree
http://sobolev.us/octotree/
Sourcegraph
https://sourcegraph.com/
Github-find
http://goo.gl/DR86r1
Take a break
jQuery.fn.init
a 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*(<[wW]+>)[^>]*|#([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*(<[wW]+>)[^>]*|#([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*(<[wW]+>)[^>]*|#([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*(<[wW]+>)[^>]*|#([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*(<[wW]+>)[^>]*|#([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*(<[wW]+>)[^>]*|#([w-]*))$/
$('#id')
$('<div/>')
rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/
rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/
Non-capturing
rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/
Non-capturing Group 1
rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/
Non-capturing Group 1 Group 2
rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/
Non-capturing Group 1 Group 2
rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/
Non-capturing Group 1 Group 2
match = rquickExpr.exec( selector );
rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/
Non-capturing Group 1 Group 2
match = rquickExpr.exec( selector );
• match[0] = // Matched String
rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/
Non-capturing Group 1 Group 2
match = rquickExpr.exec( selector );
• match[0] = // Matched String
• match[1] = // Tag html
rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([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*(<[wW]+>)[^>]*|#([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*(<[wW]+>)[^>]*|#([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 type
Name Value
ELEMENT_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 type
Name Value
ELEMENT_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
rquickExpr
Sizzle 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')
$('div')
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 pattern
http://wiki.commonjs.org/wiki/Promises
Promise pattern
http://wiki.commonjs.org/wiki/Promises
許你⼀一個承諾
Promise pattern
http://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 );
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") ]
],
⾏行為 假設 狀態
0
Callback list
var tuples = [
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],
⾏行為 假設 狀態
0 1
Callback list
var tuples = [
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],
⾏行為 假設 狀態
0 1 2
Callback list
var tuples = [
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],
⾏行為 假設 狀態
0 1 2
Callback 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 ) ) {
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

Dollar symbol