$30 off During Our Annual Pro Sale. View Details »

Functional JavaScript with Lo-Dash.js

Functional JavaScript with Lo-Dash.js

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

Shogo Sensui

May 25, 2013
Tweet

More Decks by Shogo Sensui

Other Decks in Programming

Transcript

  1. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  6. Object-Oriented
    AND
    Functional

    View Slide

  7. Functional ???

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  11. Object-Oriented ???

    View Slide

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

    View Slide

  13. OOPͷྫ͑͹…

    View Slide

  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-

    View Slide

  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!");
    });

    View Slide

  16. ΦϒδΣΫτࢦ޲ͷ໰୊఺

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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]
    //Ҿ਺͕มߋ͞Εͯ͠·͍ͬͯΔ…ɻ

    View Slide

  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!

    View Slide

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

    View Slide

  27. This is bad...
    var setElement = function() {
    $(".hoge").addClass("decorationClass").animate({...});
    };
    var initMainPage = function() {
    window.scrollTo(1, 0);
    setElement();
    };
    window.onload = function() {
    initMainPage();
    };

    View Slide

  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"));
    };

    View Slide

  29. FunctionalͳΞϓϩʔνΛ
    ࢼͯ͠ΈΔɻ

    View Slide

  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);
    });
    }
    };
    };

    View Slide

  31. ࢖͍ํΠϝʔδ
    //$ίϯετϥΫλʹCSSηϨΫλΛ౉͠ɺཁૉʹΫϥεΛ௥Ճɻ
    $(".parentClass .childClass").addClass("hoge");

    View Slide

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

    View Slide

  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);
    });
    };

    View Slide

  34. ࢖͍ํΠϝʔδ
    //CSSηϨΫλΛ౉͠ɺཁૉΛબ୒͢Δɻ
    var elementList = _.qsa(".parentClass .childClass");
    //औಘͨ͠ཁૉʹΫϥεΛ௥Ճ͢Δɻ
    _.addClass(elementList, "hoge");

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  40. Underscore.js
    AND
    Lo-Dash.js

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  44. http://lodash.com/

    View Slide

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

    View Slide

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

    View Slide

  47. http://underscorejs.org/

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  51. ʰେ͖ͳ഑ྻ΍ΦϒδΣΫτͷྻڍʹ࠷దԽ͞Ε͓ͯ
    ΓɺશମతʹύϑΥʔϚϯε͕վળ͞Ε͍ͯΔɻ·
    ͨɺΧελϜϏϧυ΍ςϯϓϨʔτͷϓϦίϯύΠϧ
    ͳͲɺΑΓॊೈͳ࡞Γʹͳ͍ͬͯΔɻʱ

    View Slide

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

    View Slide

  53. Performance of Lo-Dash.js

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  57. _.each()
    via Underscore.js

    View Slide

  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;
    }
    }
    }
    };

    View Slide

  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;
    }
    }
    }
    };

    View Slide

  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͕࢖͑Δ
    ৔߹͸ͦΕΛ࣮ߦ͍ͯ͠Δ

    View Slide

  61. _.each()
    via Lo-Dash.js

    View Slide

  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;
    }

    View Slide

  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;
    }

    View Slide

  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จΛ࢖͍ͬͯΔ

    View Slide

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

    View Slide

  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ͷ
    ͕ࠩग़ͯΔ

    View Slide

  67. Compare Performance(2)
    Underscore#forEach Array
    Underscore#forEach Object
    Lo-Dash#forEach Object
    Lo-Dash#forEach Array
    0 750000 1500000 2250000 3000000
    whileจͳͷͰ΄ͱΜͲ
    ͕ࠩग़͍ͯͳ͍

    View Slide

  68. ΧελϜϏϧυͱ
    Underscore.jsޓ׵

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  72. Compare File Size
    lodash.min.js
    lodash.underscore.min.js
    underscore.min.js
    0 7500 15000 22500 30000
    compressed gzipped
    gzip͢Ε͹͞΄ͲมΘΒͳ͍ʂ
    ʢ7,701bytesʣ

    View Slide

  73. ৭ʑߟྀ͢Δͱݱ࣌఺Ͱ͸
    Lo-Dash.js͕ྑͦ͞͏…ɻ

    View Slide

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

    View Slide

  75. _.pluck()
    via Underscore.js

    View Slide

  76. var stooges = [
    {name : 'moe', age : 40},
    {name : 'larry', age : 50},
    {name : 'curly', age : 60}
    ];
    _.pluck(stooges, 'name');
    //=> ["moe", "larry", "curly"]

    View Slide

  77. _.pluck = function(obj, key) {
    return _.map(obj, function(value){
    return value[key];
    });
    };

    View Slide

  78. …ඒ͍͠ɻ

    View Slide

  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];
    };
    };

    View Slide

  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

    View Slide

  81. ΑΓFunctionalʹॻͨ͘Ίʹ
    ϓϥάΠϯ͕͋Γ·͢ɻ

    View Slide

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

    View Slide

  83. _.pipeline = function(/*, funs */){
    var functions = arguments;
    return function(seed) {
    return _.reduce(functions, function(l, r) {
    return r(l);
    }, seed);
    };
    };

    View Slide

  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

    View Slide

  85. http://dtao.github.io/lazy.js/

    View Slide

  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);

    View Slide

  87. http://functionaljs.org/

    View Slide

  88. http://moutjs.com/

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  93. Conclusion !

    View Slide

  94. OBJECT-ORIENTED
    TO
    FUNCTIONAL

    View Slide

  95. OBJECT-ORIENTED
    TO
    FUNCTIONAL

    View Slide

  96. OBJECT-ORIENTED
    WITH
    FUNCTIONAL

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  100. Thank you !
    by @1000ch

    View Slide

  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

    View Slide

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

    View Slide