Más contenido relacionado
La actualidad más candente (20)
Similar a これからのJavaScriptー関数型プログラミングとECMAScript6 (20)
これからのJavaScriptー関数型プログラミングとECMAScript6
- 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
- 6. 今日話す内容
/46
• 関数型プログラミング
• 第一級オブジェクトとしての関数
• 不変性
• 副作用をなくす
• 不変なデータ
• ECMAScript 6
• const
• 分割代入(デストラクチャリング)
• 末尾呼び出し最適化
• for-ofループ
6
- 10. JavaScriptとは
• Javaのシンタックス
• Selfのプロトタイプ
• Schemeの第一級関数オブジェクト
JavaScriptの先祖であるSchemeが
関数型プログラミング言語
=>JavaScriptでもある程度関数型プログラミングが可能
10
/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
- 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
- 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
- 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
- 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
- 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
- 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
- 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()を慎重に使う
- 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
- 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
- 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