More Related Content
Similar to ECMAScript6による関数型プログラミング (20)
ECMAScript6による関数型プログラミング
- 2. 自己紹介
• 名前:安田裕介
• Trifortに今年入社の新卒1年生
• Webフロントエンジニアやってます
• JavaScript, Scala, C++が好き
• GitHubアカウント: TanUkkii007
- 6. const宣言子
再代入できない変数を宣言する
1 "use strict";
2 const foo = "foo";
3
4 foo = “bar”;
5 //TypeError: foo is read-only
※strictモードの場合、再代入しようとするとTypeErrorとなる
※strictモードでない場合、再代入は暗黙に失敗する
※letと同様ブロックスコープをもつ
- 8. 分割代入(デストラクチャリング)
配列やオブジェクトからパターンによって値を抽出する
配列パターンによる抽出
1 var [a,b] = [1,2];!
2 console.log(a, b);!
3 // 1 2!
4 !
5 [b, a] = [a,b];!
6 !
7!
8 var {name: name, family: {sister: sister}} !
分割代入による値の交換
オブジェクトパターンによる抽出
! ! ! ! ! ! = {name: 'John Doe', family: {sister: 1}}!
9 console.log(name, sister);!
10 // "John Doe" 1
※for in/ofループや関数の引数でも使えます
- 11. 例)階乗の計算
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 factorial1(100000);!
18 // too much recursion!
19 factorial2(100000);!
20 // Infinity
@k_matsuzaki さんに指摘して
もらいました。これでは最適化
されません。正しくは↓
←これが末尾呼び出し
function factorial1(n, acc) {
if (n == 0)
return acc;
return factorial1(n - 1, n * acc);
末尾呼び出し最適化が実装されれば
解決される!!
}
←再帰ではスタックオーバーフロー
←ループではInfinityではあるが成功
factorial1(100000, 1);
- 13. プロキシ
既存のオブジェクトをラップし、その一部の内部
メソッドをECMAScriptコードで実装して挙動を
変えることを可能にするオブジェクト
プロキシオブジェクトの作り方
!
!
!
new Proxy(target, {
get: //proxy[name]
set: //proxy[name] = val
apply: //proxy()
construct: //new Proxy()
deleteProperty: //delete proxy[name]
})
ラップするターゲットオブジェクト
内部メソッドに実装を与えるハンドラーオブジェクト
がおきたときの処理を
定義できる
※ハンドラーの各メソッドをトラップという ※トラップは全部で14個ある ※定義されていないトラップ
ではデフォルトの挙動が用いられる ※apply, constructトラップは関数がターゲットとなるときのみ有効
- 15. 通常の配列と同様
Arrayコンストラクタで
配列を作れるようにする
1 var immutable = {
2 Array: function(...array) {
3 return immutable.createArray(array);
4 },
5 createArray: function(array = []) {
6 return new Proxy(array, {
7 get: function(target, name, receiver) {
8 var mutator
9 = immutable.mutators.filter(x => x === name)[0];
10 if (mutator) {
11 return (...args) => {
12 var copy = target.slice();
13 copy[mutator].apply(copy, args);
14 return immutable.createArray(copy);
15 };
16 } else {
17 return target[name];
18 }
19 }
20 });
破壊メソッドの判定用の配列
21 },
22 mutators: ["pop","push","reverse","shift","sort","splice","unshift"]
23 };
- 16. 1 var immutable = {
2 Array: function(...array) {
プロキシを
3 return immutable.createArray(array);
4 },
5 createArray: function(array = []) {
6 return new Proxy(array, {
7 get: function(target, name, receiver) {
8 var mutator
9 = immutable.mutators.filter(x => x === name)[0];
10 if (mutator) {
11 return (...args) => {
12 var copy = target.slice();
13 copy[mutator].apply(copy, args);
14 return immutable.createArray(copy);
15 };
16 } else {
17 return target[name];
18 }
19 }
20 });
21 },
22 mutators: ["pop","push","reverse","shift","sort","splice","unshift"]
23 };
作成しているのはここ
- 17. !
!
5 createArray: function(array = []) {
6 return new Proxy(array, {
ターゲットに配列を使用
7 get: function(target, name, receiver) {
8 getトラップ
var mutator
9 を定義
= immutable.mutators.filter(x => x === name)[0];
10 if (mutator) {
11 return (...args) => {
12 破壊メソッドなら
var copy = target.slice();
13 sliceを挟む関数を返す
copy[mutator].apply(copy, args);
14 return immutable.createArray(copy);
15 };
16 } else {
コピーした配列で
17 return target[name];
プロキシを再作成
18 }
19 }
20 });
21 }
- 18. プロキシで作った不変配列は
配列とどう違うのか?
!
1 var array = new immutable.Array(1,2,3);
2 var nativeArray = new Array(1,2,3);
3
4 array[array.length - 1]; //3
5 nativeArray[nativeArray.length - 1]; //3
6
7 for (var v of array) console.log(v); //1 2 3
8 for (var v of nativeArray) console.log(v); //1 2 3
9
10
11 var result = array.push(4).reverse(); //[4,3,2,1]
12 array === result; //false
13 array; //[1,2,3]
14
15 nativeArray.push(4);
16 var nativeResult = nativeArray.reverse(); //[4,3,2,1]
17 nativeResult === nativeResult; //true
18 nativeArray; //[4,3,2,1]
←不変配列
←組み込みの配列
使い方と挙動に
違いはない
不変配列では元の配列は
変わっていない
組み込みの配列では
元の配列は変わっている
- 19. まとめ
関数型の機能をES6でどう実現するかを見てきた
1. 変更不可能な変数の宣言
2. パターンマッチ
3. 再帰による繰り返し処理
4. 不変なデータ構造
→ const宣言子
→ 分割代入
→ 末尾呼び出し最適化
→ プロキシ
ECMAScript6の表現力で
関数型プログラミングを楽しもう!