これからのJavaScriptー関数型プログラミングとECMAScript6
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

これからのJavaScriptー関数型プログラミングとECMAScript6

on

  • 314 views

2014/9/5にLIG社で行われたWebフロント勉強会で発表した資料です。

2014/9/5にLIG社で行われたWebフロント勉強会で発表した資料です。

Statistics

Views

Total Views
314
Views on SlideShare
306
Embed Views
8

Actions

Likes
2
Downloads
0
Comments
0

2 Embeds 8

http://s.deeeki.com 1
https://twitter.com 1

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

これからのJavaScriptー関数型プログラミングとECMAScript6 Presentation Transcript

  • 1. これからのJavaScript 関数型プログラミングとECMAScript 6 /46 1
  • 2. 自己紹介 • 名前:安田裕介 • Trifortに今年4月入社の新卒1年生 • JavaScript, Scala, C++が好き • Webフロントエンジニアやってます • GitHubアカウント: TanUkkii007 この資料に出てくるサンプルコードはhttps://github.com/TanUkkii007/js_pitch_sample_codeにあります /46 2
  • 3. Webアプリケーション開発の 現状 /46 より大規模に • クライアントマシンのパフォーマンスの向上 • WebブラウザのAPIの充実 • ランタイムの強化 • SPAやMVCの導入 • JavaScriptのサーバーサイドへの進出 3
  • 4. スケーラブルで保守性の高いコードを JavaScriptで書く必要がある /46 4
  • 5. どうやって? • 短く書く • 変更しない(させない) /46 5
  • 6. 今日話す内容 /46 • 関数型プログラミング • 第一級オブジェクトとしての関数 • 不変性 • 副作用をなくす • 不変なデータ • ECMAScript 6 • const • 分割代入(デストラクチャリング) • 末尾呼び出し最適化 • for-ofループ 6
  • 7. 関数型プログラミング スケーラブルで保守性の高いコードを構築する手 法として関数型プログラミングが注目されている 関数型のスタイルを言語レベルでサポートしたも のが関数型プログラミング言語 e.g. Lisp, Scheme, SML, Erlang, Haskell, OCaml, F#, Scala /46 7
  • 8. JavaScriptは関数型プログラミング言語 ではない しかし 関数型プログラミングの考え方は 大規模化するコードに立ち向かう際に 役に立つ /46 8
  • 9. JavaScriptとは • Javaのシンタックス • Selfのプロトタイプ • Schemeの第一級関数オブジェクト ポータビリティに優れたダイナミックな言語 /46 9
  • 10. JavaScriptとは • Javaのシンタックス • Selfのプロトタイプ • Schemeの第一級関数オブジェクト JavaScriptの先祖であるSchemeが  関数型プログラミング言語 =>JavaScriptでもある程度関数型プログラミングが可能 10 /46
  • 11. 関数型プログラミングの 構成要素 • 第一級オブジェクトとしての関数 • イミュータビリティ(不変性) 11 /46
  • 12. • 第一級オブジェクトとしての関数 • イミュータビリティ(不変性) 12 /46
  • 13. 第一級オブジェクトとしての 関数とは オブジェクトとしてのすべての能力をもつ関数である 13 /46
  • 14. オブジェクト(値)は 1 //変数に代入できる! 2 var one = 1;! 3 //関数からの返り値として返すことができる! 4 function getOne() {! 5 return 1;! 6 }! 7 //関数の引数として渡すことができる! 8 function identity(num) {! 9 return num;! 10 }! 11 //意味はないが値が書けるところにはどこにでも書ける! 12 1;! 13 "1";! 14 //メンバーとしてメソッドをもつ! 15 "1".toString();! 16 //メンバーとしてプロパティをもつ! 17 "1".length;! 14 /46
  • 15. 関数オブジェクトも同様 1 //変数に関数を代入する! 2 var func = function() {};! 3 //関数からの返り値として関数を返す! 4 function emptyFunc() {! 5 return function() {};! 6 }! 7 ///関数の引数として関数を渡す! 8 function result(f) {! 9 return typeof f === 'function' ? f() : f; ! 10 }! 11 //意味はないが値が書けるところにはどこにでも書ける! 12 (function(){});! 13 func;! 14 //関数がメンバーとしてメソッドをもつ! 15 (function(){}).apply(null, []);! 16 //関数がメンバーとしてプロパティをもつ! 17 (function(){}).length;! 15 /46
  • 16. 関数オブジェクトを用いれば 短いコードで柔軟な処理が可能だ Array.prototype.reduceの例 *配列の要素を集約し1つの値にする reduce(callback(prev, next, index, array){}, initial) 16 /46
  • 17. 1 //合計! 2 [0,1,2,3,4,5].reduce(function(prev, current) {! 3 return prev + current;! 4 });! 5 // > 15! 6 //平均! 7 [0,1,2,3,4,5].reduce(function(prev, current, index, array) {! 8 return prev + current/array.length;! 9 });! 10 // > 2.5! 11 //最大値! 12 [0,1,2,3,4,5].reduce(function(prev, current, index, array) {! 13 return prev < current ? current : prev;! 14 });! 15 // > 5! 16 //配列の平坦化! 17 [! 18 [1,2,3,4,5],! 19 [6,7,8,9,10],! 20 [11,12,13,14,15],! 21 [16,17,18,19,20],! 22 ].reduce(function(prev, current) {! 23 return prev.concat(current);! 24 }, []);! 25 // > [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, /46 17, 18, 19, 20]! 17
  • 18. • 第一級オブジェクトとしての関数 • イミュータビリティ(不変性) 18 /46
  • 19. JavaScriptはSchemeから 関数オブジェクトを受け継いだが、 イミュータビリティ(不変性)は受け継がな かった /46 ! JavaScriptは ダイナミックであることを選んだ 19
  • 20. JavaScriptは言語として イミュータビリティを提供しない。 なのでイミュータビリティを実現 したければ、 プログラマーの手によって なしとげなければならない 20 /46
  • 21. 副作用 副作用とは プログラミングにおける副作用(ふくさよう)とは、ある機能が コンピュータの(論理的な)状態を変化させ、それ以降で得られる結 果に影響を与えることをいう。 wikipedia 関数型プログラミングでは副作用を徹底的に排除する 21 /46
  • 22. /46 副作用をもつ 関数の例 副作用がある関数 は有意な値を返さ ない特徴がある 1 // 1/2! 2 var half = {! 3 numer: 1,! 4 denom: 2! 5 };! 6 console.log(half.numer + '/' + half.denom);! 7 // > 1/2! 8 //halfに1を足す! 9 function plusOne() {! 10 half.numer += half.denom;! 11 }! 12 //rationalをnumで割る! 13 function devide(rational, num) {! 14 rational.denom *= num;! 15 }! 16 plusOne();! 17 // > undefined! 18 console.log(half.numer + '/' + half.denom);! 19 // > 3/2! 20 devide(half, 2);! 21 // > undefined! 22 console.log(half.numer + '/' + half.denom);! 23 // > 3/4! 22
  • 23. 副作用をもたない関数の例 • 外部のデータや状態に依存しない • 外部のデータや状態を変更しない • 引数をもとにただマップ(写像)するだけ 23 /46
  • 24. 1 //分数r1とr2を足す関数! 2 function plus(r1, r2) {! 3 return {! 4 numer: r1.numer*r2.denom + r2.numer* r1.denom,! 5 denom: r1.denom*r2.denom! 6 };! 7 }! 8 // 1/2! 9 var half = {! 10 numer: 1,! 11 denom: 2! 12 };! 13 // 1/4! 14 var quarter = {! 15 numer: 1,! 16 denom: 4! 17 };! 18 // 1/2 + 1/4! 19 var threeOverFour = plus(half, quarter);! 20 ! 21 console.log(threeOverFour.numer + '/' + threeOverFour.denom);! 22 // > 6/8! 23 console.log(half.numer + '/' + half.denom);! 24 // > 1/2! 25 console.log(quarter.numer + '/' + quarter.denom);! 26 // > 1/4 24 /46
  • 25. 副作用をもたない関数の利点 /46 • 汎用性が高い • 安全である • 結果を予測できる 25
  • 26. おなじみの 副作用をもつ関数 1 // DOM API! 2 image.setAttribute('href', 'image.png');! 3 element.appendChild(child);! 4 element.removeChild(child);! 5 element.classList.add('someclass');! 6 element.classList.remove('someclass');! 7 ! 8 // IO! 9 console.log('side effect'); DOM APIやコンソールへの出力は副作用 副作用はGUIや入出力にはなくてはならないもの 26 /46
  • 27. 関数型プログラミングの手法はModelとController /46 M V C で新価を発揮する 27
  • 28. イミュータブル(不変)な データをつくる 不変な分数を作りたい /46 ! ! とりあえずオブジェクト指向の考え 方に基づきカプセル化する 28
  • 29. 1 var Rational = function(numer, denom) {! 2 this.numer = numer || 0; this.denom = denom || 1;! 3 };! 4 Rational.prototype = {! 5 toString: function() {! 6 return this.numer + '/' + this.denom;! 7 },! 8 ensure: function(r) {! 9 return (r instanceof Rational)? r: new Rational(r, 1);! 10 },! 11 plus: function(rational) {! 12 var r = this.ensure(rational);! 13 this.numer = this.numer*r.denom + r.numer*this.denom;! 14 this.denom *= r.denom; return this;! 15 },! 16 minus: function(rational) {! 17 var r = this.ensure(rational);! 18 this.numer = this.numer*r.denom - r.numer*this.denom;! 19 this.denom *= r.denom; return this;! 20 },! 21 multiply: function(rational) {! 22 var r = this.ensure(rational);! 23 this.numer *= r.numer;! 24 this.denom *= r.denom; return this;! 25 },! 26 divide: function(rational) {! 27 var r = this.ensure(rational);! 28 this.numer *= r.denom;! 29 this.denom *= r.numer; return this;! 30 }! 31 }; 29 /46 • 自身の状態を変更することに  よって分数の計算を行う • 自分自身を返す
  • 30. 1 // 1/2! 2 var half = new Rational(1,2);! 3 // 1/2! 4 var threeOverTwo = half.plus(1);! 5 ! 6 half === threeOverTwo;! 7 // > true! 8 console.log(half);! 9 // > 3/2! 10 console.log(threeOverTwo);! 11 // > 3/2! 12 ! 13 // 3/2 * 2/3! 14 threeOverTwo.multiply(new Rational(2,3));! 15 // > 6/6! データの状態はメソッドの呼び出しごとに変化していく 30 /46
  • 31. イミュータブルにする 31 /46
  • 32. 1 var Rational = function(numer, denom) {! 2 this.numer = numer || 0; this.denom = denom || 1;! 3 };! 4 Rational.prototype = {! 5 toString: function() {! 6 return this.numer + '/' + this.denom;! 7 },! 8 ensure: function(r) {! 9 return (r instanceof Rational)? r: new Rational(r, 1);! 10 },! 11 plus: function(rational) {! 12 var r = this.ensure(rational);! 13 var numer = this.numer*r.denom + r.numer*this.denom;! 14 var denom = this.denom * r.denom;! 15 return new Rational(numer, denom);! 16 },! 17 minus: function(rational) {! 18 var r = this.ensure(rational);! 19 var numer = this.numer*r.denom - r.numer*this.denom;! 20 var denom = this.denom * r.denom;! 21 return new Rational(numer, denom);! 22 },! 23 multiply: function(rational) {! 24 var r = this.ensure(rational);! 25 var numer = this.numer * r.numer;! 26 var denom = this.denom * r.denom;! 27 return new Rational(numer, denom);! 28 },! 29 divide: function(rational) { /* omitted */ }! 30 };! 32 /46 • 自身の状態を変更しない • 新しいオブジェクトを返す
  • 33. 1 // 1/2! 2 var half = new Rational(1,2);! 3 ! 4 // 1/2 + 1! 5 var threeOverTwo = half.plus(1);! 6 // > 3/2! 7 // halfとthreeOverTwoは違うオブジェクト! 8 half === threeOverTwo;! 9 // > false! 10 ! 11 console.log(half);! 12 // > 1/2! 13 console.log(threeOverTwo);! 14 // > 3/2! 15 ! 16 3/2 * 2/3! 17 threeOverTwo.multiply(new Rational(2,3));! 18 // > 6/6! 19 ! 20 console.log(threeOverTwo);! 21 // > 3/2! /46 いずれも メソッド呼び出 しの後に 変化していない 33
  • 34. ES6で導入される 便利な機能 短く、より安全に書けるようになります • const • 分割代入(デストラクチャリング) • 末尾呼び出し最適化 • for-ofループ /46 すべてFF33で動作 34
  • 35. const • constキーワードを使うことで、再代入不可能な 定数を宣言できる 35 /46
  • 36. 1 const foo = 'foo';! 2 //再代入しても値は変わらない! 3 foo = 'bar';! 4 console.log(foo);! 5 // > foo! 6 ! 7 const cobj = {foo: 'foo'};! 8 //再代入しても値は変わらない! 9 cobj = {};! 10 console.log(cobj.foo);! 11 // > foo! 12 //プロパティは変更できる! 13 cobj.foo = 'bar';! 14 console.log(cobj.foo);! 15 // > bar! 16 ! 17 Object.freeze(cobj);! 18 //Writable is false! 19 cobj.foo = 'baz';! 20 console.log(cobj.foo);! 21 // > 'foo'! 22 //Extensible is false! 23 cobj.bar = 'bar';! 24 console.log(cobj.bar);! 25 // > undefined 36 /46 • const指定された変数は  再代入できない • const指定のオブジェクトでは参照 先は変わらないが、プロパティは変 更できる • プロパティの変更を防ぐには Object.freeze()を慎重に使う
  • 37. 分割代入(デストラクチャリング) • 配列やオブジェクトをパターンで分解し、値を 取り出す • 代入文を逆にしたような構文 • 元の配列やオブジェクトを変更しない • 代入の回数を減らせる 37 /46
  • 38. 1 //配列パターンによる分割! 2 var [a,b] = [1,2];! 3 console.log(a, b);! 4 // > 1 2! 5 ! 6 //従来の値の交換! 7 var tmp = a;! 8 a = b;! 9 b = tmp;! 10 //分割代入による値の交換! 11 [b, a] = [a,b];! 12 ! 13 //オブジェクトパターンによる分割! 14 var half = {numer: 1, denom: 2};! 15 var {numer, a, denom: b} = half;! 16 console.log(a, b);! 17 // > 1 2! 18 ! 19 //ネストも可能! 20 var {name: name, family: {sister: sister}} =! {name: 'John Doe', family: {sister: 1}}! 21 console.log(name, sister);! 22 // "John Doe" 1 38 /46
  • 39. 末尾呼び出し最適化 • 繰り返し計算では関数の再帰の方がループより も短く書ける • 関数内の代入文という副作用を排除できる 39 /46
  • 40. 例えば階乗の計算を 再帰とループの2通りで書いてみると 1 // 再帰による階乗計算! 2 function factorial1(n) {! 3 if (n === 0)! 4 return 1;! 5 return n * factorial1(n - 1);! 6 }! 7 ! 8 //ループによる階乗計算! 9 function factorial2(n) {! 10 var result = 1;! 11 for (var i = 1; i <= n; ++i) {! 12 result *= i;! 13 }! 14 return result;! 15 }! 16 ! 17 // 5 * 4 * 3 * 2 * 1! 18 factorial1(5);! 19 // > 120! 40/46
  • 41. 再帰の問題 • 関数呼び出しのオーバーヘッド • スタックオーバーフローの危険性 ES6で導入される末尾呼び出し最適化 により末尾再帰に限り これらは解決される 41 /46
  • 42. for-ofループ • オブジェクトの列挙可能なプロパティの名前を列挙する。値ではない。 • 配列のインデックスを文字列にして列挙する。 • prototype中の列挙可能なプロパティも対象にする • 順番が未定義 • カスタマイズ不能 /46 for-inループの問題 for-ofループは値を列挙し、カスタマイズ可能である 42
  • 43. 1 //for-inループはキーを列挙する! 2 for (var i in [1,2,3,4,5])! 3 console.log(i);! 4 // > "0" "1" "2" "3" "4"! 5 ! 6 //for-inループは[[Enumerable]]なprototypeプロパティを列挙する! 7 var F = function() {};! 8 F.prototype = {enumerable: 'enumerable'};! 9 var obj = new F();! 10 for (var key in obj)! 11 console.log(obj[key], obj.hasOwnProperty(key));! 12 // > enumerable false! 13 ! 14 //独自のデータ構造はfor-inでループできない! 15 var LinkedList = function(value, next) {! 16 this.value = value;! 17 this.next = next || null;! 18 };! 19 var list = ! new LinkedList(1, new LinkedList(2, new LinkedList(3)));! 20 for (var curr = list; curr !== null; curr = curr.next)! 21 console.log(curr.value);! 22 // > 1 2 3! 開始、終了、次のデータを知っている必要がある 43/46
  • 44. 1 //for-ofループは値を列挙する 2 for (var i of [1,2,3,4,5]) 3 console.log(i); 4 // > 1 2 3 4 5 5 //@@iteratorを定義すれば、独自のデータ構造もループ可能 6 var LinkedList = function(value, next) { 7 this.value = value; 8 this.next = (next === undefined)? new LinkedList(null, null) /46 : next; 9 }; 10 LinkedList.prototype = { 11 '@@iterator': function() { 12 return { 13 current: this, 14 next: function() { 15 var prev = this.current; 16 this.current = prev.next; 17 return {value: prev.value, done: prev.value === null}; 18 } 19 }; 20 } 21 }; 22 var list = new LinkedList(1, new LinkedList(2, new LinkedList(3))); 23 //@@iteratorの暗黙の呼び出しがおきる 24 for (var value of list) 25 console.log(value); 26 // > 1 2 3 44
  • 45. @@iteratorの定義が長いって? ジェネレーターつかえば簡単に書けるよ 1 var LinkedList = function(value, next) {! 2 this.value = value;! 3 this.next = (next === undefined)? ! new LinkedList(null, null) : next;! 4 };! 5 LinkedList.prototype = {! 6 //! 7 '@@iterator': function*() {! 8 for (var curr = list; curr.next !== null; curr = /46 curr.next) {! 9 yield curr.value;! 10 }! 11 }! 12 };! 13 var list = ! new LinkedList(1, new LinkedList(2, new LinkedList(3)));! 14 //! 15 for (var value of list)! 16 console.log(value);! 17 // > 1 2 3 45
  • 46. まとめ • JavaScriptの関数オブジェクトは強力 • 関数、データは副作用をもたないように設計す べき • JavaScriptで不変性を強制することは難しい =>プログラマ自身が努力するしかない • ES6の登場をお楽しみに 46 /46