functional javascript with lo-dash.js

102

Upload: sensui-shogo

Post on 22-Jun-2015

814 views

Category:

Technology


2 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Functional JavaScript with Lo-Dash.js
Page 2: Functional JavaScript with Lo-Dash.js

Functional JavaScript with Lo-Dash.js

Frontrend Vol.5 泉水翔吾 / @1000ch

CyberAgent, Inc.

Page 3: Functional JavaScript with Lo-Dash.js

About me

@1000ch

WebDeveloper@CyberAgent

1年前までProgrammer

Page 4: Functional JavaScript with Lo-Dash.js

Output

! http://1000ch.net

" @1000ch

# http://github.com/1000ch

# http://github.com/enja-oss

Page 5: Functional JavaScript with Lo-Dash.js

Agenda

! Object-Oriented & Functional

! Underscore.js & Lo-Dash.js

! Conclusion!

Page 6: Functional JavaScript with Lo-Dash.js

Object-Oriented AND

Functional

Page 7: Functional JavaScript with Lo-Dash.js

Functional ???

Page 8: Functional JavaScript with Lo-Dash.js

Functional Programming

! 『数学的な関数の評価を行い、状態やデータの変更を行わないプログラミングパラダイム』via Wikipedia

! 関数型と言われてもイメージしにくい…。

Page 9: Functional JavaScript with Lo-Dash.js

押し寄せるFunctional

! HaskellとかClojureとか。

! TwitterはScalaを採用。

! JavaScriptもFunctional?

Page 10: Functional JavaScript with Lo-Dash.js

哲学的な話をする気は ございません!

Functionalなアプローチもしてみてねっていう話

Page 11: Functional JavaScript with Lo-Dash.js

Object-Oriented ???

Page 12: Functional JavaScript with Lo-Dash.js

Object-Oriented Programming

! 『オブジェクト同士の相互作用として、システムの振る舞いを捉える考え方』 via Wikipedia

! つまり『プログラムで実現したいモデルを抽象化したもの』である。

Page 13: Functional JavaScript with Lo-Dash.js

OOPの例えば…

Page 14: Functional JavaScript with Lo-Dash.js

猫を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-

Page 15: Functional JavaScript with Lo-Dash.js

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

Page 16: Functional JavaScript with Lo-Dash.js

オブジェクト指向の問題点

Page 17: Functional JavaScript with Lo-Dash.js

! オブジェクト同士の関係が密になりがち

! 役割が大きくなりすぎてクラスが複雑になりがち

! 継承されすぎて変更できなくなりがち

OOPが抱えがちな問題

Page 18: Functional JavaScript with Lo-Dash.js

œš‘“›⁸コードの役割分担が複雑で、コードの 役割が密接な関係を持っていると テストを行うことが難しくなる。’”⁹

- @cssradarat Frontrend Vol.4

https://speakerdeck.com/studiomohawk/testable-javascript

Page 19: Functional JavaScript with Lo-Dash.js

つまるところ、 保守性を維持しにくい。

Page 20: Functional JavaScript with Lo-Dash.js

jQueryの保守しろって 言われても、できません。

Page 21: Functional JavaScript with Lo-Dash.js

Functionalなアプローチが 解決してくれる!

(かもしれない!)

Page 22: Functional JavaScript with Lo-Dash.js

Funtional Function

! 引数が同じであれば出力も同じである

! 果たす役割が簡潔で分割されていること

Page 23: Functional JavaScript with Lo-Dash.js

Funtional Function

! 引数が同じであれば出力も同じである

! 果たす役割が簡潔で分割されていること

Page 24: Functional JavaScript with Lo-Dash.js

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] //引数が変更されてしまっている…。

Page 25: Functional JavaScript with Lo-Dash.js

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!

Page 26: Functional JavaScript with Lo-Dash.js

Funtional Function

! 引数が同じであれば出力も同じである

! 果たす役割が簡潔で分割されていること

Page 27: Functional JavaScript with Lo-Dash.js

This is bad...

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

Page 28: Functional JavaScript with Lo-Dash.js

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

Page 29: Functional JavaScript with Lo-Dash.js

Functionalなアプローチを 試してみる。

Page 30: Functional JavaScript with Lo-Dash.js

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

Page 31: Functional JavaScript with Lo-Dash.js

使い方イメージ

//$コンストラクタにCSSセレクタを渡し、要素にクラスを追加。 $(".parentClass .childClass").addClass("hoge");

Page 32: Functional JavaScript with Lo-Dash.js

この実装の問題点! each()もaddClass()もコンストラクタのquerySelectorAllに依存している。

! 再利用出来ない。

! クラスを拡張するたびにテストの見直し。

Page 33: Functional JavaScript with Lo-Dash.js

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

Page 34: Functional JavaScript with Lo-Dash.js

使い方イメージ

//CSSセレクタを渡し、要素を選択する。 var elementList = _.qsa(".parentClass .childClass"); !!//取得した要素にクラスを追加する。 _.addClass(elementList, "hoge");

Page 35: Functional JavaScript with Lo-Dash.js

解決された点! _.qsa()も、_.addClass()も互いに依存していないので、再利用できる。

! テストケースの見直しが発生しにくい。

Page 36: Functional JavaScript with Lo-Dash.js

Functionalなアプローチが もたらすメリット

Page 37: Functional JavaScript with Lo-Dash.js

Simplicityメソッド間の依存性が疎になることで、

コードがシンプルになる。

Page 38: Functional JavaScript with Lo-Dash.js

Testablity関数の役割が簡潔になることで、

テストが書きやすくなる。

Page 39: Functional JavaScript with Lo-Dash.js

MaintainablitySimplicityとTestablityによって、保守性の向上が期待できる。

Page 40: Functional JavaScript with Lo-Dash.js

Underscore.js AND

Lo-Dash.js

Page 41: Functional JavaScript with Lo-Dash.js

Functionalなアプローチの メリットはわかった!

Page 42: Functional JavaScript with Lo-Dash.js

ユーティリティをつくったり保守するコストが…。

Page 43: Functional JavaScript with Lo-Dash.js

優秀なライブラリがあります。使いましょう。(※もちろん自作でもOKです)

Page 44: Functional JavaScript with Lo-Dash.js

http://lodash.com/

Page 45: Functional JavaScript with Lo-Dash.js

Lo-Dash.js

! Underscore.jsとAPIの互換性のあるライブラリ

! each()やfirst()など、便利なユーティリティを備えている

Page 46: Functional JavaScript with Lo-Dash.js

あれ? じゃあUnderscore.jsは?

Page 47: Functional JavaScript with Lo-Dash.js

http://underscorejs.org/

Page 48: Functional JavaScript with Lo-Dash.js

Underscore.jsでもOK

! Lo-Dash.jsよりファイルサイズが軽量

! コードがとても綺麗なのでコードリーディングに向いてる

Page 49: Functional JavaScript with Lo-Dash.js

なぜ Lo-Dash.jsを選ぶのか?

Page 50: Functional JavaScript with Lo-Dash.js

- 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

Page 51: Functional JavaScript with Lo-Dash.js

『大きな配列やオブジェクトの列挙に最適化されており、全体的にパフォーマンスが改善されている。また、カスタムビルドやテンプレートのプリコンパイルなど、より柔軟な作りになっている。』

Page 52: Functional JavaScript with Lo-Dash.js

! 実行パフォーマンスが良い

! 問題が色々解決されています

! カスタムビルドできます

! Underscore.jsに互換性あります

Page 53: Functional JavaScript with Lo-Dash.js

Performance of Lo-Dash.js

Page 54: Functional JavaScript with Lo-Dash.js

Compare Performance(1)

Underscore#forEach

Native#forEach

Lo-Dash#forEach with bind

Lo-Dash#forEach

Native#for

0 1250000 2500000 3750000 5000000

Page 55: Functional JavaScript with Lo-Dash.js

うーん、やっぱり for文が1番高速…。

Page 56: Functional JavaScript with Lo-Dash.js

それでもLo-Dash.jsを 使う価値がある。

Page 57: Functional JavaScript with Lo-Dash.js

_.each() via Underscore.js

Page 58: Functional JavaScript with Lo-Dash.js

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

Page 59: Functional JavaScript with Lo-Dash.js

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

Page 60: Functional JavaScript with Lo-Dash.js

_.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が使える場合はそれを実行している

Page 61: Functional JavaScript with Lo-Dash.js

_.each() via Lo-Dash.js

Page 62: Functional JavaScript with Lo-Dash.js

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

Page 63: Functional JavaScript with Lo-Dash.js

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

Page 64: Functional JavaScript with Lo-Dash.js

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文を使っている

Page 65: Functional JavaScript with Lo-Dash.js

Compare Performance(2)

Underscore#forEach Array

Underscore#forEach Object

Lo-Dash#forEach Object

Lo-Dash#forEach Array

0 550000 1100000 1650000 2200000

Page 66: Functional JavaScript with Lo-Dash.js

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の差が出てる

Page 67: Functional JavaScript with Lo-Dash.js

Compare Performance(2)

Underscore#forEach Array

Underscore#forEach Object

Lo-Dash#forEach Object

Lo-Dash#forEach Array

0 550000 1100000 1650000 2200000

while文なのでほとんど差が出ていない

Page 68: Functional JavaScript with Lo-Dash.js

カスタムビルドと Underscore.js互換

Page 69: Functional JavaScript with Lo-Dash.js

Modern build モダンブラウザ向けのビルド。

Legacy build レガシーブラウザ対応がなされている。

Mobile build 関数のコンパイルがされていない

Strict build読み取り専用プロパティを上書きしようとしたときにエラーを投げる。

Underscore build Underscore.jsにAPIを合わせてある。

Backbone build Backbone.jsに必要なAPIのみ備える。

Page 70: Functional JavaScript with Lo-Dash.js

Modern build モダンブラウザ向けのビルド。

Legacy build レガシーブラウザ対応がなされている。

Mobile build 関数のコンパイルがされていない

Strict build読み取り専用プロパティを上書きしようとしたときにエラーを投げる。

Underscore build Underscore.jsにAPIを合わせてある。

Backbone build Backbone.jsに必要なAPIのみ備える。

Page 71: Functional JavaScript with Lo-Dash.js

Compare File Size

lodash.min.js

lodash.underscore.min.js

underscore.min.js

0 5500 11000 16500 22000

compressed gzipped

Page 72: Functional JavaScript with Lo-Dash.js

Compare File Size

lodash.min.js

lodash.underscore.min.js

underscore.min.js

0 5500 11000 16500 22000

compressed gzipped

gzipすればさほど変わらない!(7,701bytes)

Page 73: Functional JavaScript with Lo-Dash.js

色々考慮すると現時点では Lo-Dash.jsが良さそう…。

Page 74: Functional JavaScript with Lo-Dash.js

しかしUnderscore.jsの 美しい実装は

非常に参考になる。

Page 75: Functional JavaScript with Lo-Dash.js

_.pluck() via Underscore.js

Page 76: Functional JavaScript with Lo-Dash.js

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

Page 77: Functional JavaScript with Lo-Dash.js

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

Page 78: Functional JavaScript with Lo-Dash.js

…美しい。

Page 79: Functional JavaScript with Lo-Dash.js

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

Page 80: Functional JavaScript with Lo-Dash.js

var plusFive = function(num) { return num + 5; }; var multiplyThree = function(num) { return num * 3; }; var plus5_multiply3 = _.compose(multiplyThree, plusFive); plus5_multiply3(4); //=>27

Page 81: Functional JavaScript with Lo-Dash.js

よりFunctionalに書くために プラグインがあります。

Page 82: Functional JavaScript with Lo-Dash.js

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

Page 83: Functional JavaScript with Lo-Dash.js

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

Page 84: Functional JavaScript with Lo-Dash.js

var plusFive = function(num) { return num + 5; }; var multiplyThree = function(num) { return num * 3; }; var multiply3_plus5 = _.pipeline(multiplyThree, plusFive); multiply3_plus5(4); //=>17

Page 85: Functional JavaScript with Lo-Dash.js

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

Page 86: Functional JavaScript with Lo-Dash.js

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

Page 87: Functional JavaScript with Lo-Dash.js

http://functionaljs.org/

Page 88: Functional JavaScript with Lo-Dash.js

http://moutjs.com/

Page 89: Functional JavaScript with Lo-Dash.js

どれもDOMを操作する実装は 含んでいない。

※あくまで、JavaScriptのUtilityであるため。

Page 90: Functional JavaScript with Lo-Dash.js

DOM操作のAPIを 提供する拡張を作ってみた。

※参考程度にどうぞ。

Page 91: Functional JavaScript with Lo-Dash.js

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

Page 92: Functional JavaScript with Lo-Dash.js

特徴! 要素選択API

! イベントのバインドAPI

! CSSクラスの付け外しAPI

! 軽い(minifiedで3KBくらい)

Page 93: Functional JavaScript with Lo-Dash.js

Conclusion !

Page 94: Functional JavaScript with Lo-Dash.js

OBJECT-ORIENTED TO

FUNCTIONAL

Page 95: Functional JavaScript with Lo-Dash.js

OBJECT-ORIENTED TO

FUNCTIONAL

Page 96: Functional JavaScript with Lo-Dash.js

OBJECT-ORIENTED WITH

FUNCTIONAL

Page 97: Functional JavaScript with Lo-Dash.js

物事の抽象化はオブジェクト指向でしかできない。

Page 98: Functional JavaScript with Lo-Dash.js

USE LO-DASH.jS WATCH UNDERSCORE.JS

Page 99: Functional JavaScript with Lo-Dash.js

Underscore.jsとかMOUTあたりが綺麗なので参考に!

Page 100: Functional JavaScript with Lo-Dash.js

Thank you !by @1000ch

Page 101: Functional JavaScript with Lo-Dash.js

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

Page 102: Functional JavaScript with Lo-Dash.js

http://fontawesome.io/

http://font.ubuntu.com/

https://github.com/adobe/Source-Code-Pro

http://www.fontsquirrel.com/fonts/bebas-neue

Font Credits