Dollar symbol

Preview:

Citation preview

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

Recommended