// Procedural programming
{
const octuples = [];
for (let n = 1; n < 101; n += 1) {
if (n % 8 === 0) {
octuples.push(n);
}
}
console.log(octuples);
}
// Functional programming
{
const range = (start, end) => [...new Array(end - start).keys()].map((n) => n + start);
console.log(range(1, 101).filter((n) => n % 8 === 0));
}
関数型は1つも変数の書き換えが発生しない。手続き型の方はoctuplesやnが何回も書き換えられている(バグが入り込む可能性があがる)。
関数型はそもそも代入を控える傾向にある。
手続き型の方は、forやifを用いて値を返さないサブルーチンの中で処理が実行される。
関数型はすべてが値を返す式の組み合わせで、左辺から右辺に評価されていき最終的な値に到達するのでシンプル。
手続き型の方は、ボトムアップで積み上げていって最終的な解を出す。
関数型は、最初から完成形を見据えた上で大雑把なところから絞り込んでいく。
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
//map() 対象の配列の要素一つ一つを任意に加工した新しい配列を返す
console.log(arr.map((n) => n * 2)); // [2, 4, 6, 8, 10, 12, 14, 16, 18]
//filter() 与えた条件に適合する要素だけを抽出した新しい配列を返す
console.log(arr.filter((n) => n % 3 === 0)); // [3, 6, 9]
//find() 与えた条件に適合した最初の要素を返す。なかった場合はundefinedを返す
console.log(arr.find((n) => n > 4)); //5
//findIndex() 与えた条件に適合した最初の要素のインデックスを返す。なかった場合は-1を返す
console.log(arr.findIndex((n) => n > 4)); //4
//every() 「与えた条件をすべての要素が満たすか」を真偽値で返す
console.log(arr.every((n) => n !== 0)); //true
//some() 「与えた条件を満たす要素が一つでもあるか」を真偽値で返す
console.log(arr.some((n) => n >= 10)); //false
const arr = [1, 2, 3, 4, 5];
//reduce() 第一引数には前回の関数の実行結果、第二引数にはarrの各要素が順番に入る
//最後に実行された結果を返す
console.log(arr.reduce((n, m) => n + m)); //15
//sort() 第一引数は要素のいずれか、第二引数は第一引数の次の要素
//第一引数と第二引数を入れ替える場合は1を返し、入替えない場合は-1を返す。
console.log(arr.sort((n, m) => n > m ? -1 : 1)); //[5, 4, 3, 2, 1]
//sort()は破壊的メソットなので注意が必要
//sliceを間に挟むことでコピーをsortすることになり、元の配列は変更されない
const lst = [5, 7, 1, 3];
console.log(lst.slice().sort((n, m) => n < m ? -1 : 1)); //[1, 3, 5, 7]
sortは参考書籍だけだとイマイチイメージわからなかったので、以下のURLも参照
const arr = [1, 2, 3, 4, 5];
//includes() 「指定した値の要素が一つでも膨れているか」を真偽値で返す
console.log(arr.includes(5)); //true
console.log(arr.includes(8)); //false
const user = {
id: 3,
name: 'Bobby Kumanov',
username: 'bobby',
email: 'bobby@maple.town',
};
//プロパティのキーリストを配列で取得
console.log(Object.keys(user));
// [ 'id', 'name', 'username', 'email' ]
//プロパティ値のリストを配列で取得
console.log(Object.values(user));
// [ 3, 'Bobby Bear', 'bobby', 'bobby@maple.town' ]
//キーと値が対になった2次元配列で取得
console.log(Object.entries(user));
// [
// [ 'id', 3 ],
// [ 'name', 'Bobby Kumanov' ],
// [ 'username', 'bobby' ],
// [ 'email', 'bobby@maple.town' ]
// ]
引数として関数をとったり、戻り値として関数を返したりする関数。「コールバック」はこの引数として渡される関数のことをいう。
// 戻り値として関数を返すサンプル
const greeter = (target) => {
const sayHello = () => {
console.log(`Hi, ${target}!`);
};
// 実行結果(sayHello())でなく、関数そのもの(sayHello)を返す。
// 実行結果を返すとundefinedになってしまう
return sayHello;
};
const greet = greeter('Step Jun');
greet() //Hi, Step Jun!
//余計な表記をなくすと以下のようにスッキリする
const greeter = (target) => () => console.log(`Hi, ${target}!`);
複数の引数を取る関数を、より少ない引数を取る関数に分割し入れ子にする。
// Pre-curried
const multiply = (n, m) => n * m;
console.log(multiply(2, 4)); // 8
// Curried
const withMultiple = (n) => {
return (m) => n * m;
};
console.log(withMultiple(2)(4)); // 8
// Curried with double arrow
const withMultiple = (n) => (m) => n * m;
console.log(withMultiple(2)(4)); // 8
カリー化された関数の引数の一部を渡して新しい関数を作ることを関数の部分適用という
const withMultiple = (n) => (m) => n * m;
console.log(withMultiple(3)(5)); //15
const triple = withMultiple(3); //引数を一部だけ渡して3倍する関数を作る
console.log(triple(5)); //15
「関数閉包」と訳され、関数を関数で閉じて包む
//閉じられてない
let count = 0;
const increment = () => {
return count += 1;
};
//上記ではcountがグローバル変数であり参照透過的でない。以下のように変更する
const counter = () => {
let count = 0;
const increment = () => {
return count += 1;
};
return increment;
};
const increment = counter();
クロージャとは、関数と『その関数が作られた環境(コンテキスト)』という 2 つのものが一体となった特殊なオブジェクトのことを指す
よくわからなかったので以下リンクで補完
Promiseオブジェクトを使うことで、任意の非同期処理の完了を待って次の処理を行うということが可能となる
const isSucceeded = true;
const promise = new Promise((resolve, reject) => {
if (isSucceeded) {
resolve('Success');
}else {
reject(new Error('Failure'));
}
});
promise.then((value) => { // コールバックの結果(resolv or reject)がvalueになる
console.log('1.', value);
return 'Success again';
})
.then((value) => { // 一つ前のthenのreturnがvalueになる
console.log('2.', value);
})
.catch((error) => {
console.error('3.', error);
})
.finally(() => { // 処理が成功でも失敗でもfinallyの関数は必ず最後に実行される
console.log('4.', 'Completed');
});
import fetch from 'node-fetch';
const getUser = (userId) =>
fetch((`https://jsonplaceholder.typicode.com/users/${userId}`).then(
(response) => {
if (!response.ok) {
throw new Error(`${response.status} Error`);
} else {
return response.json();
}
},
);
getUser(2)
.then((user) => {
console.log(user);
})
.catch((error) => {
console.log(error);
})
.finally(() => {
console.log('-- Completed --');
});
上記をシンタックスシュガー(async, await)を用いて書くと以下のようになる
コールバックがなくなるのでシンプルになる
import fetch from 'node-fetch';
const getUser = async (userId) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/${userId}`,
);
if (!response.ok) {
throw new Error(`${response.status} Error`);
}
return response.json();
};
const main = async() => {
try {
const user = await getUser(2);
console.log(user);
} catch(error) {
console.error(error);
} finally {
console.log('-- Completed --');
}
};
関数宣言時にasyncをつけると「非同期関数」となり、返される値はPromise.resolve()にラップされたものになる
非同期関数の中では、他の非同期関数をawaitをつけて呼び出すことができる。awaitをつけて非同期関数を実行するとPromiseから返りがあるまで待つ。awaitが使えるのは非同期関数の中だけ