Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Functional JavaScript with Lo-Dash.js

Functional JavaScript with Lo-Dash.js

2013/5/25に開催されたFrontrend Vol.5のFunctional JavaScript with Lo-Dash.jsのセッションの資料です。

0d605a3350dd7e91b8136aecffd5ceeb?s=128

Shogo Sensui

May 25, 2013
Tweet

Transcript

  1. None
  2. Functional JavaScript with Lo-Dash.js Frontrend Vol.5 ઘਫᠳޗ / @1000ch CyberAgent,

    Inc.
  3. About me @1000ch WebDeveloper@CyberAgent 1೥લ·ͰProgrammer

  4. Output  http://1000ch.net  @1000ch  http://github.com/1000ch  http://github.com/enja-oss

  5. Agenda  Object-Oriented & Functional  Underscore.js & Lo-Dash.js 

    Conclusion!
  6. Object-Oriented AND Functional

  7. Functional ???

  8. Functional Programming  ʰ਺ֶతͳؔ਺ͷධՁΛߦ͍ɺঢ়ଶ΍σʔλͷม ߋΛߦΘͳ͍ϓϩάϥϛϯάύϥμΠϜʱ via Wikipedia  ؔ਺ܕͱݴΘΕͯ΋Πϝʔδ͠ʹ͍͘…ɻ

  9. ԡ͠دͤΔFunctional  Haskellͱ͔Clojureͱ͔ɻ  Twitter͸ScalaΛ࠾༻ɻ  JavaScript΋Functionalʁ

  10. ఩ֶతͳ࿩Λ͢Δؾ͸ ͍͟͝·ͤΜʂ FunctionalͳΞϓϩʔν΋ͯ͠ΈͯͶ͍ͬͯ͏࿩

  11. Object-Oriented ???

  12. Object-Oriented Programming  ʰΦϒδΣΫτಉ࢜ͷ૬ޓ࡞༻ͱͯ͠ɺγεςϜ ͷৼΔ෣͍Λଊ͑Δߟ͑ํʱ via Wikipedia  ͭ·ΓʰϓϩάϥϜͰ࣮ݱ͍ͨ͠ϞσϧΛந৅Խ ͨ͠΋ͷʱͰ͋Δɻ

  13. OOPͷྫ͑͹…

  14. ೣΛOOPͰදݱ͢Δ var Animal = function(word) { this.word = word; };

    Animal.prototype.cry = function() { console.log(this.word); }; var Cat = function() { Animal.call(this, "Nya-"); }; Cat.prototype = Object.create(Animal.prototype); Cat.prototype.constructor = Animal; new Cat().cry(); //=>Nya-
  15. jQuery΋OOP? var $headline = $(".headline"); $headline.css({ "font-size": "24px" }); var

    $paragraph = $("p"); $paragraph.css({ "background": "#000", "color": "#fff" }).addClass("breakAll"); var $button = $("button"); $button.on("click", function() { console.log("button is clicked!"); });
  16. ΦϒδΣΫτࢦ޲ͷ໰୊఺

  17.  ΦϒδΣΫτಉ࢜ͷؔ܎͕ີʹͳΓ͕ͪ  ໾ׂ͕େ͖͘ͳΓ͗ͯ͢Ϋϥε͕ෳࡶʹͳΓ͕ͪ  ܧঝ͞Ε͗ͯ͢มߋͰ͖ͳ͘ͳΓ͕ͪ OOP๊͕͕͑ͪͳ໰୊

  18. “ίʔυͷ໾ׂ෼୲͕ෳࡶͰɺίʔυͷ ໾ׂ͕ີ઀ͳؔ܎Λ͍࣋ͬͯΔͱ ςετΛߦ͏͜ͱ͕೉͘͠ͳΔɻ” - @cssradar at Frontrend Vol.4 https://speakerdeck.com/studiomohawk/testable-javascript

  19. ͭ·Δͱ͜Ζɺ อकੑΛҡ࣋͠ʹ͍͘ɻ

  20. jQueryͷอक͠Ζͬͯ ݴΘΕͯ΋ɺͰ͖·ͤΜɻ

  21. FunctionalͳΞϓϩʔν͕ ղܾͯ͘͠ΕΔʂ ʢ͔΋͠Εͳ͍ʂʣ

  22. Funtional Function  Ҿ਺͕ಉ͡Ͱ͋Ε͹ग़ྗ΋ಉ͡Ͱ͋Δ  Ռͨ͢໾ׂ͕؆ܿͰ෼ׂ͞Ε͍ͯΔ͜ͱ

  23. Funtional Function  Ҿ਺͕ಉ͡Ͱ͋Ε͹ग़ྗ΋ಉ͡Ͱ͋Δ  Ռͨ͢໾ׂ͕؆ܿͰ෼ׂ͞Ε͍ͯΔ͜ͱ

  24. This is bad... var sortedString = function(array) { array.sort(); return

    array.join(); }; var array1 = [1, 6, 8, 4, 9, 0, 3, 5, 2, 7]; console.log(sortedString(array1)); //=>0,1,2,3,4,5,6,7,8,9 console.log(array1); //=>[0,1,2,3,4,5,6,7,8,9] //Ҿ਺͕มߋ͞Εͯ͠·͍ͬͯΔ…ɻ
  25. This is better !!! var sortedString = function(array) { var

    buffer = array.slice(); buffer.sort(); return buffer.join(); }; var array1 = [1, 6, 8, 4, 9, 0, 3, 5, 2, 7]; console.log(sortedString(array1)); //=>0,1,2,3,4,5,6,7,8,9 console.log(array1); //=>[1,6,8,4,9,0,3,5,2,7] //OK!
  26. Funtional Function  Ҿ਺͕ಉ͡Ͱ͋Ε͹ग़ྗ΋ಉ͡Ͱ͋Δ  Ռͨ͢໾ׂ͕؆ܿͰ෼ׂ͞Ε͍ͯΔ͜ͱ

  27. This is bad... var setElement = function() { $(".hoge").addClass("decorationClass").animate({...}); };

    var initMainPage = function() { window.scrollTo(1, 0); setElement(); }; window.onload = function() { initMainPage(); };
  28. This is better !!! var setElement = function(element) { $(element).addClass("decorationClass").animate({...});

    }; var hideAddressBar = function() { window.scrollTo(1, 0); }; window.onload = function() { hideAddressBar(); setElement(document.getElementsByClassName(".hoge")); };
  29. FunctionalͳΞϓϩʔνΛ ࢼͯ͠ΈΔɻ

  30. jQueryͬΆ͍αϯϓϧ࣮૷ var $ = function(selector) { return { list: document.querySelectorAll(selector),

    each: function(callback) { for(var i = 0, l = this.list.length;i < l;i++) { callback(this.list[i]); } return this; }, addClass: function(className) { return this.each(function(element) { element.classList.add(className); }); } }; };
  31. ࢖͍ํΠϝʔδ //$ίϯετϥΫλʹCSSηϨΫλΛ౉͠ɺཁૉʹΫϥεΛ௥Ճɻ $(".parentClass .childClass").addClass("hoge");

  32. ͜ͷ࣮૷ͷ໰୊఺  each()΋addClass()΋ίϯετϥΫλͷ querySelectorAllʹґଘ͍ͯ͠Δɻ  ࠶ར༻ग़དྷͳ͍ɻ  ΫϥεΛ֦ு͢Δͨͼʹςετͷݟ௚͠ɻ

  33. Functionalʹॻ͖௚͢ var _ = {}; _.qsa = function(selector) { return

    document.querySelectorAll(selector); }; _.each = function(targetObject, callback) { for(var i = 0, len = targetObject.length;i < len;i++) { callback(targetObject[i]); } }; _.addClass = function(targetElementList, className) { _.each(targetElementList, function(element) { element.classList.add(className); }); };
  34. ࢖͍ํΠϝʔδ //CSSηϨΫλΛ౉͠ɺཁૉΛબ୒͢Δɻ var elementList = _.qsa(".parentClass .childClass"); //औಘͨ͠ཁૉʹΫϥεΛ௥Ճ͢Δɻ _.addClass(elementList, "hoge");

  35. ղܾ͞Εͨ఺  _.qsa()΋ɺ_.addClass()΋ޓ͍ʹґଘ͍ͯ͠ͳ͍ ͷͰɺ࠶ར༻Ͱ͖Δɻ  ςετέʔεͷݟ௚͕͠ൃੜ͠ʹ͍͘ɻ

  36. FunctionalͳΞϓϩʔν͕ ΋ͨΒ͢ϝϦοτ

  37. Simplicity ϝιουؒͷґଘੑ͕ૄʹͳΔ͜ͱͰɺ ίʔυ͕γϯϓϧʹͳΔɻ

  38. Testablity ؔ਺ͷ໾ׂ͕؆ܿʹͳΔ͜ͱͰɺ ςετ͕ॻ͖΍͘͢ͳΔɻ

  39. Maintainablity SimplicityͱTestablityʹΑͬͯɺ อकੑͷ޲্͕ظ଴Ͱ͖Δɻ

  40. Underscore.js AND Lo-Dash.js

  41. FunctionalͳΞϓϩʔνͷ ϝϦοτ͸Θ͔ͬͨʂ

  42. ϢʔςΟϦςΟΛͭͬͨ͘Γ อक͢Δίετ͕…ɻ

  43. ༏लͳϥΠϒϥϦ͕͋Γ· ͢ɻ࢖͍·͠ΐ͏ɻ ʢ※΋ͪΖΜࣗ࡞Ͱ΋OKͰ͢ʣ

  44. http://lodash.com/

  45. Lo-Dash.js  Underscore.jsͱAPIͷޓ׵ੑͷ͋ΔϥΠϒϥϦ  each()΍!rst()ͳͲɺศརͳϢʔςΟϦςΟΛඋ ͍͑ͯΔ

  46. ͋Εʁ ͡Ό͋Underscore.js͸ʁ

  47. http://underscorejs.org/

  48. Underscore.jsͰ΋OK  Lo-Dash.jsΑΓϑΝΠϧαΠζ͕ܰྔ  ίʔυ͕ͱͯ΋៉ྷͳͷͰίʔυϦʔσΟϯάʹ ޲͍ͯΔ

  49. ͳͥ Lo-Dash.jsΛબͿͷ͔ʁ

  50. - John-David Dalton at StackOver"ow “better overall performance and optimizations

    for large arrays/object iteration, and more !exibility with custom builds and template pre- compilation utilities.” http://stackover"ow.com/questions/13789618/di#erences-between- lodash-and-underscore
  51. ʰେ͖ͳ഑ྻ΍ΦϒδΣΫτͷྻڍʹ࠷దԽ͞Ε͓ͯ ΓɺશମతʹύϑΥʔϚϯε͕վળ͞Ε͍ͯΔɻ· ͨɺΧελϜϏϧυ΍ςϯϓϨʔτͷϓϦίϯύΠϧ ͳͲɺΑΓॊೈͳ࡞Γʹͳ͍ͬͯΔɻʱ

  52.  ࣮ߦύϑΥʔϚϯε͕ྑ͍  ໰୊͕৭ʑղܾ͞Ε͍ͯ·͢  ΧελϜϏϧυͰ͖·͢  Underscore.jsʹޓ׵ੑ͋Γ·͢

  53. Performance of Lo-Dash.js

  54. Compare Performance(1) Underscore#forEach Native#forEach Lo-Dash#forEach with bind Lo-Dash#forEach Native#for 0

    1250000 2500000 3750000 5000000
  55. ͏ʔΜɺ΍ͬͺΓ forจ͕1൪ߴ଎…ɻ

  56. ͦΕͰ΋Lo-Dash.jsΛ ࢖͏Ձ஋͕͋Δɻ

  57. _.each() via Underscore.js

  58. _.each = _.forEach = function(obj, iterator, context) { if (obj

    == null) return; if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(iterator, context); } else if (obj.length === +obj.length) { for (var i = 0, l = obj.length; i < l; i++) { if (iterator.call(context, obj[i], i, obj) === breaker) return; } } else { for (var key in obj) { if (_.has(obj, key)) { if (iterator.call(context, obj[key], key, obj) === breaker) return; } } } };
  59. _.each = _.forEach = function(obj, iterator, context) { if (obj

    == null) return; if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(iterator, context); } else if (obj.length === +obj.length) { for (var i = 0, l = obj.length; i < l; i++) { if (iterator.call(context, obj[i], i, obj) === breaker) return; } } else { for (var key in obj) { if (_.has(obj, key)) { if (iterator.call(context, obj[key], key, obj) === breaker) return; } } } };
  60. _.each = _.forEach = function(obj, iterator, context) { if (obj

    == null) return; if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(iterator, context); } else if (obj.length === +obj.length) { for (var i = 0, l = obj.length; i < l; i++) { if (iterator.call(context, obj[i], i, obj) === breaker) return; } } else { for (var key in obj) { if (_.has(obj, key)) { if (iterator.call(context, obj[key], key, obj) === breaker) return; } } } }; ωΠςΟϒͷforEach͕࢖͑Δ ৔߹͸ͦΕΛ࣮ߦ͍ͯ͠Δ
  61. _.each() via Lo-Dash.js

  62. function forEach(collection, callback, thisArg) { var index = -1, length

    = collection ? collection.length : 0; callback = callback && typeof thisArg == 'undefined' ? callback : lodash.createCallback(callback, thisArg); if (typeof length == 'number') { while (++index < length) { if (callback(collection[index], index, collection) === false) { break; } } } else { forOwn(collection, callback); } return collection; }
  63. function forEach(collection, callback, thisArg) { var index = -1, length

    = collection ? collection.length : 0; callback = callback && typeof thisArg == 'undefined' ? callback : lodash.createCallback(callback, thisArg); if (typeof length == 'number') { while (++index < length) { if (callback(collection[index], index, collection) === false) { break; } } } else { forOwn(collection, callback); } return collection; }
  64. function forEach(collection, callback, thisArg) { var index = -1, length

    = collection ? collection.length : 0; callback = callback && typeof thisArg == 'undefined' ? callback : lodash.createCallback(callback, thisArg); if (typeof length == 'number') { while (++index < length) { if (callback(collection[index], index, collection) === false) { break; } } } else { forOwn(collection, callback); } return collection; } جຊతʹwhileจΛ࢖͍ͬͯΔ
  65. Compare Performance(2) Underscore#forEach Array Underscore#forEach Object Lo-Dash#forEach Object Lo-Dash#forEach Array

    0 750000 1500000 2250000 3000000
  66. Compare Performance(2) Underscore#forEach Array Underscore#forEach Object Lo-Dash#forEach Object Lo-Dash#forEach Array

    0 750000 1500000 2250000 3000000 forจͱArray.forEachͷ ͕ࠩग़ͯΔ
  67. Compare Performance(2) Underscore#forEach Array Underscore#forEach Object Lo-Dash#forEach Object Lo-Dash#forEach Array

    0 750000 1500000 2250000 3000000 whileจͳͷͰ΄ͱΜͲ ͕ࠩग़͍ͯͳ͍
  68. ΧελϜϏϧυͱ Underscore.jsޓ׵

  69. Modern build Legacy build Mobile build Strict build Underscore build

    Backbone build Ϟμϯϒϥ΢β޲͚ͷϏϧυɻ ϨΨγʔϒϥ΢βରԠ͕ͳ͞Ε͍ͯΔɻ ؔ਺ͷίϯύΠϧ͕͞Ε͍ͯͳ͍ ಡΈऔΓઐ༻ϓϩύςΟΛ্ॻ͖͠Α͏ ͱͨ͠ͱ͖ʹΤϥʔΛ౤͛Δɻ Underscore.jsʹAPIΛ߹Θͤͯ͋Δɻ Backbone.jsʹඞཁͳAPIͷΈඋ͑Δɻ
  70. Modern build Legacy build Mobile build Strict build Underscore build

    Backbone build Ϟμϯϒϥ΢β޲͚ͷϏϧυɻ ϨΨγʔϒϥ΢βରԠ͕ͳ͞Ε͍ͯΔɻ ؔ਺ͷίϯύΠϧ͕͞Ε͍ͯͳ͍ ಡΈऔΓઐ༻ϓϩύςΟΛ্ॻ͖͠Α͏ ͱͨ͠ͱ͖ʹΤϥʔΛ౤͛Δɻ Underscore.jsʹAPIΛ߹Θͤͯ͋Δɻ Backbone.jsʹඞཁͳAPIͷΈඋ͑Δɻ
  71. Compare File Size lodash.min.js lodash.underscore.min.js underscore.min.js 0 7500 15000 22500

    30000 compressed gzipped
  72. Compare File Size lodash.min.js lodash.underscore.min.js underscore.min.js 0 7500 15000 22500

    30000 compressed gzipped gzip͢Ε͹͞΄ͲมΘΒͳ͍ʂ ʢ7,701bytesʣ
  73. ৭ʑߟྀ͢Δͱݱ࣌఺Ͱ͸ Lo-Dash.js͕ྑͦ͞͏…ɻ

  74. ͔͠͠Underscore.jsͷ ඒ͍࣮͠૷͸ ඇৗʹࢀߟʹͳΔɻ

  75. _.pluck() via Underscore.js

  76. var stooges = [ {name : 'moe', age : 40},

    {name : 'larry', age : 50}, {name : 'curly', age : 60} ]; _.pluck(stooges, 'name'); //=> ["moe", "larry", "curly"]
  77. _.pluck = function(obj, key) { return _.map(obj, function(value){ return value[key];

    }); };
  78. …ඒ͍͠ɻ

  79. _.compose = function(/*, funs */) { var functions = arguments;

    return function() { var args = arguments; for (var i = functions.length - 1; i >= 0; i--) { args = [functions[i].apply(this, args)]; } return args[0]; }; };
  80. var plusFive = function(num) { return num + 5; };

    var multiplyThree = function(num) { return num * 3; }; var plus5_multiply3 = _.compose(multiplyThree, plusFive); plus5_multiply3(4); //=>27
  81. ΑΓFunctionalʹॻͨ͘Ίʹ ϓϥάΠϯ͕͋Γ·͢ɻ

  82. https://github.com/documentcloud/underscore-contrib

  83. _.pipeline = function(/*, funs */){ var functions = arguments; return

    function(seed) { return _.reduce(functions, function(l, r) { return r(l); }, seed); }; };
  84. var plusFive = function(num) { return num + 5; };

    var multiplyThree = function(num) { return num * 3; }; var multiply3_plus5 = _.pipeline(multiplyThree, plusFive); multiply3_plus5(4); //=>17
  85. http://dtao.github.io/lazy.js/

  86. function square(x) { return x * x; } function inc(x)

    { return x + 1; } function isEven(x) { return x % 2 === 0; } var result = _.chain(array) .map(square).map(inc) .filter(isEven).take(5).value(); var result = Lazy(array) .map(square).map(inc) .filter(isEven).take(5);
  87. http://functionaljs.org/

  88. http://moutjs.com/

  89. ͲΕ΋DOMΛૢ࡞͢Δ࣮૷͸ ؚΜͰ͍ͳ͍ɻ ※͋͘·ͰɺJavaScriptͷUtilityͰ͋ΔͨΊɻ

  90. DOMૢ࡞ͷAPIΛ ఏڙ͢Δ֦ுΛ࡞ͬͯΈͨɻ ※ࢀߟఔ౓ʹͲ͏ͧɻ

  91. https://github.com/1000ch/_.domextend

  92. ಛ௃  ཁૉબ୒API  ΠϕϯτͷόΠϯυAPI  CSSΫϥεͷ෇͚֎͠API  ͍ܰ(mini!edͰ3KB͘Β͍)

  93. Conclusion !

  94. OBJECT-ORIENTED TO FUNCTIONAL

  95. OBJECT-ORIENTED TO FUNCTIONAL

  96. OBJECT-ORIENTED WITH FUNCTIONAL

  97. ෺ࣄͷந৅Խ͸ΦϒδΣΫτ ࢦ޲Ͱ͔͠Ͱ͖ͳ͍ɻ

  98. USE LO-DASH.jS WATCH UNDERSCORE.JS

  99. Underscore.jsͱ͔MOUT͋ͨ Γ͕៉ྷͳͷͰࢀߟʹʂ

  100. Thank you ! by @1000ch

  101. http://www."ickr.com/photos/vicpowles/7229138156/ http://www."ickr.com/photos/49875617@N06/8770578796/ http://www."ickr.com/photos/vicpowles/7229138156/ http://www."ickr.com/photos/scalamax/8764370935/ http://www."ickr.com/photos/jody_art/8758073909/ http://www."ickr.com/photos/liza-photography/6272074016/ http://www."ickr.com/photos/51710089@N08/8758089618/ Photo Credits

  102. http://fontawesome.io/ http://font.ubuntu.com/ https://github.com/adobe/Source-Code-Pro http://www.fontsquirrel.com/fonts/bebas-neue Font Credits