O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

Dollar symbol

863 visualizações

Publicada em

Dollar Symbol 深入淺出 2015/1 @ 5xRuby

Publicada em: Internet
  • Seja o primeiro a comentar

Dollar symbol

  1. 1. Dollar Symbol 深⼊入淺出 Aaron Huang 1
  2. 2. Dollar Symbol 深⼊入淺出 Aaron Huang 2 只進不出
  3. 3. Aaron Huang • Sr. F2E @ • F2E Lead @ Faria Systems • Cloud Service Engineer @ Waveface • Xing “⾏行” - Evernote DevCup 2013 Top 6
  4. 4. 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 • 歷史
  5. 5. 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 歷史
  6. 6. 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
  7. 7. 變⾰革
  8. 8. 1.9 release & 2.0-beta
  9. 9. jquery.migrate http://code.jquery.com/jquery-migrate-1.0.0.js
  10. 10. Module Dependency
  11. 11. Module Dependency
  12. 12. File concat Module Dependency
  13. 13. File concat Module Dependency
  14. 14. File concat Module Dependency
  15. 15. File concat RequireJS Module Dependency
  16. 16. File concat RequireJS Module Dependency
  17. 17. File Concat (before v1.11) •intro.js •core.js •selector.js •… •… •outro.js
  18. 18. File Concat (before v1.11) •intro.js •core.js •selector.js •… •… •outro.js jQuery.jsConcat
  19. 19. RequireJS (After v1.11) •core/ •var/ •event/ •…./ •jquery.js •core.js •event.js •selector.js •… •…
  20. 20. RequireJS (After v1.11) •core/ •var/ •event/ •…./ •jquery.js •core.js •event.js •selector.js •… •… dependency resolve r.js
  21. 21. RequireJS (After v1.11) •core/ •var/ •event/ •…./ •jquery.js •core.js •event.js •selector.js •… •… dependency resolve r.js convert wrap output
  22. 22. RequireJS (After v1.11) •core/ •var/ •event/ •…./ •jquery.js •core.js •event.js •selector.js •… •… dependency resolve r.js convert wrap output
  23. 23. RequireJS (After v1.11) •core/ •var/ •event/ •…./ •jquery.js •core.js •event.js •selector.js •… •… dependency resolve r.js convert wrap output jQuery.js
  24. 24. 熱⾝身⼀一下 來看看 jQuery.ready 在不同版本下的實作差異
  25. 25. 注:$(document).ready 沒事不要⽤用 但是很多⼈人都⽤用過,所以拿來熱⾝身
  26. 26. // $.fn.ready #1 $(document).ready(function(){ // callback content. });
  27. 27. // $.fn.ready #2 $(function(){ // callback content. }); // $.fn.ready #1 $(document).ready(function(){ // callback content. });
  28. 28. Challenge
  29. 29. // Kick off the DOM ready check even if the user does not jQuery.ready.promise(); Challenge
  30. 30. // Kick off the DOM ready check even if the user does not jQuery.ready.promise(); http://macb.in/do3E Challenge
  31. 31. Contribution
  32. 32. Contribution
  33. 33. License http://blog.jquery.com/2012/09/10/jquery-licensing-changes/
  34. 34. License http://blog.jquery.com/2012/09/10/jquery-licensing-changes/
  35. 35. License http://blog.jquery.com/2012/09/10/jquery-licensing-changes/ MIT/GPL
  36. 36. License http://blog.jquery.com/2012/09/10/jquery-licensing-changes/ MIT/GPL
  37. 37. License http://blog.jquery.com/2012/09/10/jquery-licensing-changes/ MIT/GPL
  38. 38. License http://blog.jquery.com/2012/09/10/jquery-licensing-changes/ MIT/GPL MIT
  39. 39. Sign the CLA (Contributor License Agreement) http://contribute.jquery.org/CLA/
  40. 40. ⼯工具
  41. 41. • vim
  42. 42. • vim • ctags
  43. 43. • vim • ctags • sliver_search or ack
  44. 44. Or Just use your browser
  45. 45. 讓你的 GitHub ⽤用起來更⽜牛⼀一點
  46. 46. 熟記 shortcut
  47. 47. 熟記 shortcut 按 “t” 就對了
  48. 48. Octotree http://sobolev.us/octotree/
  49. 49. Sourcegraph https://sourcegraph.com/
  50. 50. Github-find http://goo.gl/DR86r1
  51. 51. Take a break
  52. 52. jQuery.fn.init a jQuery Object
  53. 53. $('div') $('#id') $('.class') $('<div/>')
  54. 54. $('div') $('#id') $('.class') $('<div/>')
  55. 55. $('div') $('#id') $('.class') $('<div/>') console.log($('#id')); //[<div id="id"></id>]
  56. 56. window.jQuery = window.$ = jQuery; https://github.com/jquery/jquery/blob/master/src/exports/global.js src/exports/global.js
  57. 57. // Define a local copy of jQuery jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); },
  58. 58. 各種狀況判斷
  59. 59. 各種狀況判斷 (防衛)
  60. 60. // 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 ); }
  61. 61. // 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-]*))$/
  62. 62. 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 ); }
  63. 63. Selector & HTML
  64. 64. // 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 ); }
  65. 65. // 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-]*))$/
  66. 66. $('#id') $('<div/>')
  67. 67. rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/
  68. 68. rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/ Non-capturing
  69. 69. rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/ Non-capturing Group 1
  70. 70. rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/ Non-capturing Group 1 Group 2
  71. 71. rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/ Non-capturing Group 1 Group 2
  72. 72. rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/ Non-capturing Group 1 Group 2 match = rquickExpr.exec( selector );
  73. 73. rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/ Non-capturing Group 1 Group 2 match = rquickExpr.exec( selector ); • match[0] = // Matched String
  74. 74. rquickExpr = /^(?:s*(<[wW]+>)[^>]*|#([w-]*))$/ Non-capturing Group 1 Group 2 match = rquickExpr.exec( selector ); • match[0] = // Matched String • match[1] = // Tag html
  75. 75. 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
  76. 76. 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')
  77. 77. 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/>')
  78. 78. // 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 ); }
  79. 79. // 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 ] );
  80. 80. // ...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; }
  81. 81. 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 ); }
  82. 82. jQuery.find = Sizzle;
  83. 83. Sizzle core
  84. 84. function Sizzle( selector, context, results, seed )
  85. 85. 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
  86. 86. 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
  87. 87. 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; }
  88. 88. 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; }
  89. 89. 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
  90. 90. 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
  91. 91. rquickExpr
  92. 92. rquickExpr Sizzle version
  93. 93. rquickExpr = /^(?:#([w-]+)|(w+)|.([w-]+))$/
  94. 94. rquickExpr = /^(?:#([w-]+)|(w+)|.([w-]+))$/
  95. 95. rquickExpr = /^(?:#([w-]+)|(w+)|.([w-]+))$/ Group 1
  96. 96. rquickExpr = /^(?:#([w-]+)|(w+)|.([w-]+))$/ Group 1 Group 2
  97. 97. rquickExpr = /^(?:#([w-]+)|(w+)|.([w-]+))$/ Group 1 Group 2 Group 3
  98. 98. rquickExpr = /^(?:#([w-]+)|(w+)|.([w-]+))$/ Group 1 Group 2 Group 3 match = rquickExpr.exec( selector );
  99. 99. rquickExpr = /^(?:#([w-]+)|(w+)|.([w-]+))$/ Group 1 Group 2 Group 3 match = rquickExpr.exec( selector ); • match[0] = // Matched String
  100. 100. rquickExpr = /^(?:#([w-]+)|(w+)|.([w-]+))$/ Group 1 Group 2 Group 3 match = rquickExpr.exec( selector ); • match[0] = // Matched String • match[1] = // ID name
  101. 101. 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
  102. 102. 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
  103. 103. 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')
  104. 104. 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')
  105. 105. 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
  106. 106. 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
  107. 107. } 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; } }
  108. 108. } 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’)
  109. 109. // 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; } }
  110. 110. // 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; } }
  111. 111. var push = [].push, slice = [].slice;
  112. 112. Function.prototype.apply() fun.apply(thisArg[, argsArray])
  113. 113. Function.prototype.apply() fun.apply(thisArg[, argsArray]) Function.prototype.call() fun.call(thisArg[, arg1[, arg2[, ...]]])
  114. 114. document.querySelectAll
  115. 115. // 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(","); }
  116. 116. 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
  117. 117. 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
  118. 118. // 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(","); }
  119. 119. if ( newSelector ) { try { push.apply( results, newContext.querySelectorAll( newSelector ) ); return results; } catch(qsaError) { } finally { if ( !old ) { context.removeAttribute("id"); } } } }
  120. 120. Take a break
  121. 121. Deferred ⾯面試必備的知識
  122. 122. Async JavaScript 好書推薦給你
  123. 123. • Callback
  124. 124. • Callback • Event
  125. 125. • Callback • Event • Promise
  126. 126. Callback
  127. 127. Callback
  128. 128. Callback
  129. 129. Callback Callback Hell
  130. 130. Events
  131. 131. Promise pattern http://wiki.commonjs.org/wiki/Promises
  132. 132. Promise pattern http://wiki.commonjs.org/wiki/Promises 許你⼀一個承諾
  133. 133. Promise pattern http://wiki.commonjs.org/wiki/Promises 許你⼀一個承諾
  134. 134. 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.
  135. 135. 郭董說
  136. 136. $.when(OpenDataAt(172800)) .then(resumeWork) .fail(undefined); 郭董說
  137. 137. $.when(OpenDataAt(172800)) .then(resumeWork) .fail(undefined); 郭董說 48 ⼩小時內公開資料
  138. 138. $.when(OpenDataAt(172800)) .then(resumeWork) .fail(undefined); 郭董說 48 ⼩小時內公開資料 復⼯工
  139. 139. $.when(OpenDataAt(172800)) .then(resumeWork) .fail(undefined); 郭董說 48 ⼩小時內公開資料 復⼯工 不然他也不知道該怎麼辦
  140. 140. • .ready • .ajax • .animate
  141. 141. $.Deferred 解析
  142. 142. 了解 Deferred
  143. 143. 了解 Deferred • ⾏行為
  144. 144. 了解 Deferred • ⾏行為 • 假設
  145. 145. 了解 Deferred • ⾏行為 • 假設 • 狀態
  146. 146. 了解 Deferred • ⾏行為 • 假設 • 狀態 (resolve, reject, notify)
  147. 147. 了解 Deferred • ⾏行為 • 假設 • 狀態 (resolve, reject, notify) (done, fail, progress)
  148. 148. 了解 Deferred • ⾏行為 • 假設 • 狀態 (resolve, reject, notify) (done, fail, progress) (resolved, rejected, pending)
  149. 149. • deferred.promise() -> state: pending
  150. 150. • deferred.promise() -> state: pending • deferred.resolve() -> deferred.done_stack -> state: resolved
  151. 151. • deferred.promise() -> state: pending • deferred.resolve() -> deferred.done_stack -> state: resolved • deferred.reject() -> deferred.fail_stack -> state: rejected
  152. 152. Deferred 建構式
  153. 153. 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 );
  154. 154. var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ],
  155. 155. var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], ⾏行為
  156. 156. var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], ⾏行為 假設
  157. 157. var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], ⾏行為 假設 狀態
  158. 158. var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], ⾏行為 假設 狀態Callback list
  159. 159. var tuples = [ [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], ⾏行為 假設 狀態 0 Callback list
  160. 160. 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
  161. 161. 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
  162. 162. 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
  163. 163. 想⼀一想
  164. 164. 想⼀一想 • Deferred 會建構⼀一個全新並獨⽴立的 callbacks list
  165. 165. 想⼀一想 • Deferred 會建構⼀一個全新並獨⽴立的 callbacks list • 三個⾏行為定義的 function 幾乎⼀一樣
  166. 166. 想⼀一想 • Deferred 會建構⼀一個全新並獨⽴立的 callbacks list • 三個⾏行為定義的 function 幾乎⼀一樣 • Callbacks list 提供了所有的必要⽅方法, 包含 add, remove, fire, disable
  167. 167. 想⼀一想 • Deferred 會建構⼀一個全新並獨⽴立的 callbacks list • 三個⾏行為定義的 function 幾乎⼀一樣 • Callbacks list 提供了所有的必要⽅方法, 包含 add, remove, fire, disable • Resolve 跟 Reject 是相對的
  168. 168. 想⼀一想 • Deferred 會建構⼀一個全新並獨⽴立的 callbacks list • 三個⾏行為定義的 function 幾乎⼀一樣 • Callbacks list 提供了所有的必要⽅方法, 包含 add, remove, fire, disable • Resolve 跟 Reject 是相對的 • resolve() 與 reject() 其中⼀一⽅方作動, 另⼀一個的 callbacks 則觸發 .disabled()
  169. 169. 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;
  170. 170. 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; },
  171. 171. 再回到建構式
  172. 172. 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 ) ) {
  173. 173. 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;
  174. 174. .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 = {};
  175. 175. done vs then
  176. 176. $.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();
  177. 177. done • deferred 對象始終是同⼀一個 • done 定義的 callback 都在同⼀一個 stack list • resolve 之後 callbacks 是近乎同時呼叫 • 不應該預期 callbacks 會照順序執⾏行
  178. 178. then • 每次都會建構⼀一個新的 deferred 物件 • then 定義的 callback 都是在⼀一個獨⽴立的 callbacks list • 順序呼叫多次不同 deferred 物件的 resolved • 預期是循序執⾏行
  179. 179. 尾聲
  180. 180. 再推薦兩本必須看完的書
  181. 181. JavaScript: The Good Parts
  182. 182. Eloquent JavaScript 2nd edition
  183. 183. ⼈人不⼀一定要⾛走出⾃自⼰己的舒適圈
  184. 184. ⼈人不⼀一定要⾛走出⾃自⼰己的舒適圈 或許也可以考慮擴⼤大⾃自⼰己的舒適圈
  185. 185. Question
  186. 186. 前端⼯工程師 ⽕火熱加開中 http://jobs.kktix.cc

×