Functional JavaScript with Lo-Dash.js
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Functional JavaScript with Lo-Dash.js

on

  • 775 views

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

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

Statistics

Views

Total Views
775
Views on SlideShare
732
Embed Views
43

Actions

Likes
1
Downloads
0
Comments
0

1 Embed 43

https://twitter.com 43

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Functional JavaScript with Lo-Dash.js Presentation Transcript

  • 1. Functional JavaScript with Lo-Dash.js Frontrend Vol.5 泉水翔吾 / @1000ch CyberAgent, Inc.
  • 2. About me @1000ch WebDeveloper@CyberAgent 1年前までProgrammer
  • 3. Output ! http://1000ch.net " @1000ch # http://github.com/1000ch # http://github.com/enja-oss
  • 4. Agenda ! Object-Oriented & Functional ! Underscore.js & Lo-Dash.js ! Conclusion!
  • 5. Object-Oriented AND Functional
  • 6. Functional ???
  • 7. Functional Programming ! 『数学的な関数の評価を行い、状態やデータの変 更を行わないプログラミングパラダイム』
 via Wikipedia ! 関数型と言われてもイメージしにくい…。
  • 8. 押し寄せるFunctional ! HaskellとかClojureとか。 ! TwitterはScalaを採用。 ! JavaScriptもFunctional?
  • 9. 哲学的な話をする気は ございません! Functionalなアプローチもしてみてねっていう話
  • 10. Object-Oriented ???
  • 11. Object-Oriented Programming ! 『オブジェクト同士の相互作用として、システム の振る舞いを捉える考え方』 via Wikipedia ! つまり『プログラムで実現したいモデルを抽象化 したもの』である。
  • 12. OOPの例えば…
  • 13. 猫を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-
  • 14. 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!"); });
  • 15. オブジェクト指向の問題点
  • 16. ! オブジェクト同士の関係が密になりがち ! 役割が大きくなりすぎてクラスが複雑になりがち ! 継承されすぎて変更できなくなりがち OOPが抱えがちな問題
  • 17. œš‘“›⁸コードの役割分担が複雑で、コードの 役割が密接な関係を持っていると テストを行うことが難しくなる。’”⁹ - @cssradar
 at Frontrend Vol.4 https://speakerdeck.com/studiomohawk/testable-javascript
  • 18. つまるところ、 保守性を維持しにくい。
  • 19. jQueryの保守しろって 言われても、できません。
  • 20. Functionalなアプローチが 解決してくれる! (かもしれない!)
  • 21. Funtional Function ! 引数が同じであれば出力も同じである ! 果たす役割が簡潔で分割されていること
  • 22. Funtional Function ! 引数が同じであれば出力も同じである ! 果たす役割が簡潔で分割されていること
  • 23. 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] //引数が変更されてしまっている…。

  • 24. 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!

  • 25. Funtional Function ! 引数が同じであれば出力も同じである ! 果たす役割が簡潔で分割されていること
  • 26. This is bad... var setElement = function() { $(".hoge").addClass("decorationClass").animate({...}); }; var initMainPage = function() { window.scrollTo(1, 0); setElement(); }; window.onload = function() { initMainPage(); }; 

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

  • 28. Functionalなアプローチを 試してみる。
  • 29. 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); }); } }; };
  • 30. 使い方イメージ //$コンストラクタにCSSセレクタを渡し、要素にクラスを追加。 $(".parentClass .childClass").addClass("hoge");
  • 31. この実装の問題点 ! each()もaddClass()もコンストラクタの querySelectorAllに依存している。 ! 再利用出来ない。 ! クラスを拡張するたびにテストの見直し。
  • 32. 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); }); };
  • 33. 使い方イメージ //CSSセレクタを渡し、要素を選択する。 var elementList = _.qsa(".parentClass .childClass"); ! ! 
 //取得した要素にクラスを追加する。 _.addClass(elementList, "hoge");
  • 34. 解決された点 ! _.qsa()も、_.addClass()も互いに依存していない ので、再利用できる。 ! テストケースの見直しが発生しにくい。
  • 35. Functionalなアプローチが もたらすメリット
  • 36. Simplicity メソッド間の依存性が疎になることで、
 コードがシンプルになる。
  • 37. Testablity 関数の役割が簡潔になることで、 テストが書きやすくなる。
  • 38. Maintainablity SimplicityとTestablityによって、
 保守性の向上が期待できる。
  • 39. Underscore.js AND Lo-Dash.js
  • 40. Functionalなアプローチの メリットはわかった!
  • 41. ユーティリティをつくったり 保守するコストが…。
  • 42. 優秀なライブラリがあります。 使いましょう。 (※もちろん自作でもOKです)
  • 43. http://lodash.com/
  • 44. Lo-Dash.js ! Underscore.jsとAPIの互換性のあるライブラリ ! each()やfirst()など、便利なユーティリティを備 えている
  • 45. あれ? じゃあUnderscore.jsは?
  • 46. http://underscorejs.org/
  • 47. Underscore.jsでもOK ! Lo-Dash.jsよりファイルサイズが軽量 ! コードがとても綺麗なのでコードリーディングに 向いてる
  • 48. なぜ Lo-Dash.jsを選ぶのか?
  • 49. - John-David Dalton
 at StackOverflow œš‘“›⁸better overall performance and optimizations for large arrays/object iteration, and more flexibility with custom builds and template pre- compilation utilities.’”⁹ http://stackoverflow.com/questions/13789618/differences-between- lodash-and-underscore
  • 50. 『大きな配列やオブジェクトの列挙に最適化されてお り、全体的にパフォーマンスが改善されている。また、 カスタムビルドやテンプレートのプリコンパイルなど、 より柔軟な作りになっている。』
  • 51. ! 実行パフォーマンスが良い ! 問題が色々解決されています ! カスタムビルドできます ! Underscore.jsに互換性あります
  • 52. Performance of Lo-Dash.js
  • 53. Compare Performance(1) Underscore#forEach Native#forEach Lo-Dash#forEach with bind Lo-Dash#forEach Native#for 0 1250000 2500000 3750000 5000000
  • 54. うーん、やっぱり for文が1番高速…。
  • 55. それでもLo-Dash.jsを 使う価値がある。
  • 56. _.each() via Underscore.js
  • 57. _.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; } } } };
  • 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; } } } }; ネイティブのforEachが使える場 合はそれを実行している
  • 60. _.each() via Lo-Dash.js
  • 61. 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; }
  • 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; } 基本的にwhile文を使っている
  • 64. Compare Performance(2) Underscore#forEach Array Underscore#forEach Object Lo-Dash#forEach Object Lo-Dash#forEach Array 0 550000 1100000 1650000 2200000
  • 65. Compare Performance(2) Underscore#forEach Array Underscore#forEach Object Lo-Dash#forEach Object Lo-Dash#forEach Array 0 550000 1100000 1650000 2200000 for文とArray.forEachの
 差が出てる
  • 66. Compare Performance(2) Underscore#forEach Array Underscore#forEach Object Lo-Dash#forEach Object Lo-Dash#forEach Array 0 550000 1100000 1650000 2200000 while文なのでほとんど
 差が出ていない
  • 67. カスタムビルドと Underscore.js互換
  • 68. Modern build モダンブラウザ向けのビルド。 Legacy build レガシーブラウザ対応がなされている。 Mobile build 関数のコンパイルがされていない Strict build 読み取り専用プロパティを上書きしよう としたときにエラーを投げる。 Underscore build Underscore.jsにAPIを合わせてある。 Backbone build Backbone.jsに必要なAPIのみ備える。
  • 69. Modern build モダンブラウザ向けのビルド。 Legacy build レガシーブラウザ対応がなされている。 Mobile build 関数のコンパイルがされていない Strict build 読み取り専用プロパティを上書きしよう としたときにエラーを投げる。 Underscore build Underscore.jsにAPIを合わせてある。 Backbone build Backbone.jsに必要なAPIのみ備える。
  • 70. Compare File Size lodash.min.js lodash.underscore.min.js underscore.min.js 0 5500 11000 16500 22000 compressed gzipped
  • 71. Compare File Size lodash.min.js lodash.underscore.min.js underscore.min.js 0 5500 11000 16500 22000 compressed gzipped gzipすればさほど変わらな い!(7,701bytes)
  • 72. 色々考慮すると現時点では Lo-Dash.jsが良さそう…。
  • 73. しかしUnderscore.jsの 美しい実装は 非常に参考になる。
  • 74. _.pluck() via Underscore.js
  • 75. var stooges = [
 {name : 'moe', age : 40}, 
 {name : 'larry', age : 50}, 
 {name : 'curly', age : 60}
 ];
 ! _.pluck(stooges, 'name'); //=> ["moe", "larry", "curly"]
  • 76. _.pluck = function(obj, key) { return _.map(obj, function(value){
 return value[key];
 }); };
  • 77. …美しい。
  • 78. _.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]; }; };
  • 79. var plusFive = function(num) { return num + 5; }; var multiplyThree = function(num) { return num * 3; }; var plus5_multiply3 = _.compose(multiplyThree, plusFive); plus5_multiply3(4); //=>27
  • 80. よりFunctionalに書くために プラグインがあります。
  • 81. https://github.com/documentcloud/underscore-contrib
  • 82. _.pipeline = function(/*, funs */){ 
 var functions = arguments;
 ! return function(seed) { return _.reduce(functions, function(l, r) { return r(l); }, seed); }; };
  • 83. var plusFive = function(num) { return num + 5; }; var multiplyThree = function(num) { return num * 3; }; var multiply3_plus5 = _.pipeline(multiplyThree, plusFive); multiply3_plus5(4); //=>17
  • 84. http://dtao.github.io/lazy.js/
  • 85. 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);
  • 86. http://functionaljs.org/
  • 87. http://moutjs.com/
  • 88. どれもDOMを操作する実装は 含んでいない。 ※あくまで、JavaScriptのUtilityであるため。
  • 89. DOM操作のAPIを 提供する拡張を作ってみた。 ※参考程度にどうぞ。
  • 90. https://github.com/1000ch/_.domextend
  • 91. 特徴 ! 要素選択API ! イベントのバインドAPI ! CSSクラスの付け外しAPI ! 軽い(minifiedで3KBくらい)
  • 92. Conclusion !
  • 93. OBJECT-ORIENTED TO FUNCTIONAL
  • 94. OBJECT-ORIENTED TO FUNCTIONAL
  • 95. OBJECT-ORIENTED WITH FUNCTIONAL
  • 96. 物事の抽象化はオブジェクト 指向でしかできない。
  • 97. USE LO-DASH.jS WATCH UNDERSCORE.JS
  • 98. Underscore.jsとかMOUTあた りが綺麗なので参考に!
  • 99. Thank you ! by@1000ch
  • 100. http://www.flickr.com/photos/vicpowles/7229138156/ http://www.flickr.com/photos/49875617@N06/8770578796/ http://www.flickr.com/photos/vicpowles/7229138156/ http://www.flickr.com/photos/scalamax/8764370935/ http://www.flickr.com/photos/jody_art/8758073909/ http://www.flickr.com/photos/liza-photography/6272074016/ http://www.flickr.com/photos/51710089@N08/8758089618/ Photo Credits
  • 101. http://fontawesome.io/ http://font.ubuntu.com/ https://github.com/adobe/Source-Code-Pro http://www.fontsquirrel.com/fonts/bebas-neue Font Credits