第一部: 基本文法
データ型とリテラル
-
typeof nullはobjectとなる -
""と''は全く同じ - 複数行の文字を入れるなら`
-
undefinedはただのグローバル変数で、undefinedという値を持っているだけ- リテラルではない
演算子
- JSでは
10 + 0.5は10.5となる> 数値は内部的にIEEE 754方式の浮動小数点数として表現されています
-
NaN === NaNはfalseになる -
==は使うべきではない- 暗黙的な型変換をするから
1 + true; // => 2- 使ってよい例外:
value == nullでnullとundefindを両方比較するとき- しかし意図が読めないときもある
- 暗黙的な型変換をするから
-
indexOfは見つからないとき-1を返す-
if (~str.indexOf("木"))で 0 となる- 0 は false なので実行されない
-
-
includes("木")で見つけられる -
""(空文字列)はfalse
暗黙的な型変換
1 + "2"; // => "12"-
Number.parseInt("1", 10);で1をパースして10進数で取り出す- 文字列とundefindは
NaNとなる-
Number.isNaN(NaN);でNaNとわかる
-
- 文字列とundefindは
- sumでundefinedが入ると"NaN"になる
-
if (typeof value !== "number")で防いだりする
-
- 空文字列は
typeof str === "string" && str.length === 0;で判定する -
const obj1 = { foo: 'bar' }; const obj2 = { foo: 'bar' }; console.log(obj1 === obj2);- falseになる
- 別々に作ったら別のオブジェクトだから
- falseになる
関数と宣言
- 仮引数より呼び出し時の引数が少ない時、余ったものは
undefinedになる- 溢れたときは無視される
-
prefix || 'デフォルト';だと空文字列の時にもデフォルトが入ってしまう- デフォルト引数にする
-
prefix ?? 'デフォルト';にする
- まとめて入れたり、残りをまとめたり、まとめて出したりは
...arrayでできる- Rest parameters
- 関数の中でのみ使える
arguments[0]-
function() { console.log(arguments[0]); }のように引数が定義されてなくても使える- Rest parameters が使えるならこれは使わない方がいい
-
-
function printUserId({ id }) { ... }でuser.idのidだけとれる-
const { id } = user;でもとれる- 分割代入
-
- 関数は関数オブジェクト
- アロー関数
- 常に名前がない
-
thisが決まっている - 短く書ける
-
newできない -
argument変数は使えない
- 同じ名前の関数宣言は上書きされる
-
functionとvarを使ったときだけ起こる
-
- 引数となる関数はコールバック関数とよぶ
- コールバック関数を使う関数やメソッドを高階関数とよぶ
- JSでは関数とメソッドの違いはあまりない
文と式
- 式
- 値を生成し、変数に代入できるもの
- 文
- if文など
- ブロックで終わる文にはセミコロンは不要
- 匿名関数は式だからセミコロンは必要
条件分岐
- falsyな値
falseundefinednull00nNaN-
""(空文字列)
- falsyな値以外は真偽値に変換すると
trueとなる
ループと反復処理
-
someは一度でもtrueが返ってくると反復処理を終了するnumbers.some(isEven);
-
filterはtrueになった値だけを集められるarray.filter(isEven);
-
reduceで反復処理ができる-
array.reduce((前回の値, 現在の値) => { return 次の値; }, 初期値);numbers.reduce((total, num) => { return total + num }, 0);
-
オブジェクト
- オブジェクトとはプロパティの集合
- プロバティはキーとバリューの対
- プロパティ名と変数名が同じだと
{ name }と省略して書ける- importの時などに使う
- プロパティ名に変数を使うときは
[]を使うlanguages[myLang]
- オブジェクトのプロパティを変数として使うときは分割代入する
const { ja, en } = languages;
- プロパティの削除は
delete obj.key1 - JSの
constは再代入を防ぐだけ- 値の変更はできてしまう
-
const obj = { key: "value" };でもobj.key = "Hi!";できる
-
- 変更防止は
Object.freezeを使う
- 値の変更はできてしまう
- 存在しないプロパティにアクセスすると
undefinedとなる- 例外は発生しない
- プロパティを持つか確認するには?
-
undefinedと比較する-
obj.key !== undefined- 値が
undefinedのときと区別できない- 最終的に取得したいのが値の時に使う
- 値が
-
-
in演算子if ("key" in obj) { ... };
-
hasOwnPropertyメソッドif (obj.hasOwnProperty("key")) { ... }
-
- nullの可能性があるときには
obj?.a?.bwidget?.window?.title ?? "未定義"-
[]でも使えるlanguages?.[ja]?.[messageKey]
-
toString()とString()は同じ - オブジェクトのプロパティ名は暗黙的に文字列に変換される
- オブジェクトを配列にする
-
Object.keys(obj)でkeyの配列にする -
Object.values(obj)でvalueの配列にする -
Object.entries(obj)でkeyとvalueの配列にする
-
-
Object.assignで複製やマージができる-
const merged = Object.assign({}, objectA, objectB);- プロパティが重複すると後ろで上書きされる
-
const merged = { ...objectA, ...objectB };でもマージできる -
const cloneObj = Object.assign({}, obj);で浅い複製できる
-
プロトタイプオブジェクト
- 空オブジェクトでも
.toString()を呼び出せる-
Object.prototypeのメソッドを継承しているから- プロトタイプメソッド
- 同名のインスタンスメソッドがあればそれが優先
- プロトタイプメソッド
-
-
hasOwnPropertyとinの違い-
inはプロトタイプまで遡る
-
-
Object.create(null)で本当に空のオブジェクトを作れる- 昔は
Mapの代わりに使われていた-
const obj = {};でもobj["toString"]でアクセスできてしまうから
-
- 昔は
配列
- 最後の要素へのアクセスは
array[array.length - 1]でできる - 存在しないインデックスへのアクセスは
undefinedになる- オブジェクトでも同じ
- 配列は常に
lengthの数だけ要素を持っているとは限らない- 疎な配列
-
const sparseArray = [1,, 3];-
sparseArray.lengthは3となる-
sparseArray[1]はundefined
-
-
-
- 疎な配列
- 配列かどうか確認するには
isArray-
typeof arrayでは"object"となる
-
- 配列も分割代入できる
- 疎な配列があるから本当に未定義なものと区別できない
-
hasOwnPropertyで区別する-
sparseArray.hasOwnProperty(1)がfalseとなる
-
-
- どの位置にあるか知りたい
-
indexOfとlastIndexOf-
ary.indexOf("JS");- なければ
-1が返ってくる
- なければ
-
- オブジェクトには
findIndexを使うcolors.findIndex((obj) => { return obj.color === "blue" });
-
- 条件に一致する要素を取得する
colors.find((obj) => { return obj.color === "white" });
- 指定範囲の要素を取得する
slice(1, 4)
- 目当てのものが含まれているか確認する
-
inclues- オブジェクトには使えない
-
somecolors.some((obj) => return obj.color === "blue"; });
-
- オブジェクトには使えない
-
- 追加と削除
-
pushとpop -
unshiftとshift
-
- 結合したい
array.concat(["D", "E"]);
- flattenにしたい
-
newArray = ["X", "Y", "Z", ...array];-
["X", ...array, "Z"]もできる
-
newArray = ["X", "Y", "Z"].concat(array);- ES2019なら
flat(Infinity)も使える- これ以上フラット化できなくてもそのまま返す
-
- 任意のインデックス要素を削除する
-
array.splice(インデックス, 削除する要素数)- 自動的に詰められるから疎にはならない
-
- すべての要素を削除
-
array.length = 0;とすると配列が空になる- その要素数に切り詰められるから
-
- 注意する破壊的メソッド
sort
- 非破壊でコピーする
array.slice()-
array.concat()- 引数なしで呼び出すとコピーを返す
- コピーしてから破壊的メソッドを使う
- 引数なしで呼び出すとコピーを返す
- 指定の要素だけ集める
array.filter((currentValue, index, array) => { return currentValue % 2 == 1; });
-
reducearray.reduce((累積値, 要素, インデックス, 配列) => { return 処理;}, 初期値)
文字列
- 分解したり繋げたりする
const strings = "赤・青・黄".split("・").join("、");
- 正規表現を使って抜き出す
const strings = str.split(/\s+/);
- "?"以降を抜き出す
-
const indexOfQuery = url.indexOf("?"); const queryString = url.slice(indexOfQuery):-
sliceとsubstringはほとんど同じ
-
-
- 文字列の検索
str.startsWith()str.endsWith()str.includes()
- 正規表現
const patternA = /パターン/フラグ;-
const patternB = new RedExp("パターン文字列", "フラグ");- 関数として呼び出されるまで評価されない
- 動的に変更できる
- 関数として呼び出されるまで評価されない
str.search()-
"文字列".match(/パターン/);- マッチしない時はnullを返す
-
/[a-zA-Z]+/gで見つかっても最後までやる -
/バターン1(パターン2)/でカッコを取り出せる
- そのパターンにマッチするものがあるか調べる
-
/^にわ/.test(str)- 繰り返しや文字の集合なども検索できる
-
- 基本はStringメソッドでやる
- 柔軟性や曖昧検索のときは正規表現+コメントする
- 置換
str.replace("文字", "");-
str.replace(/文字/, "");- コールバックもできる
dateString.replace(/(\d{4})-(\d{2})-(\d{2})/, (all, year, month, day) => { ... };
- コールバックもできる
- URLは
getResource()-
scheme、host、pathname- 最後の
/は削除してから使う- Node.jsのPathモジュールを使う
- 最後の
-
- タグ付きテンプレート関数
- tag
template ${0} literal ${1}; -
function tag(strings, ...values) { ... };- valuesが取れる
String.raw()
- tag
文字列とUnicode
- JSでは「文字列は
Code Unitが順番に並んだもの」として扱われる- リンゴの絵文字の length は 2 になる
- サロゲートペア
-
const codePoints = Array.from("リンゴ🍎"); // => ["リ", "ン", "ゴ", "🍎"]- 完璧ではない
- ビルトインだけでは難しい
- 完璧ではない
- リンゴの絵文字の length は 2 になる
- 正規表現のときは
uをつける-
const [all, fish] = "𩸽のひらき".match(/(.)のひらき/);- 文字化けする
-
const [all, fish] = "𩸽のひらき".match(/(.)のひらき/u);- 基本的には
uを付ける
- 基本的には
-
ラッパーオブジェクト
- なぜ型がメソッド呼び出しできるのか
- プリミティブ型は自動的にラッパーオブジェクトに変換されるからメソッド呼び出しできる
- JSはすべてがオブジェクトのように「見える」
- すべてがオブジェクトではない
- JSはすべてがオブジェクトのように「見える」
- プリミティブ型は自動的にラッパーオブジェクトに変換されるからメソッド呼び出しできる
関数とスコープ
- 関数を定義する
- 新しいスコープを作るということ
- スコープとは
- 参照できる範囲を決めるもの
- 関数スコープ
function fn() { 関数スコープ };
- ブロックスコープ
if() { ブロックスコープ };
- スコープチェーン
-
{ { } }- 内側から外側を順番に参照できる
-
- グローバルスコープ
- グローバル変数
- ビルトインオブジェクト
-
undefinedやisNaN -
ArrayやRegExp -
documentやmodule
-
- むやみにグローバルスコープへ変数定義しない
- 外側の変数が隠蔽されるから
- shadowing
- 関数を使って小さなスコープにして書く
- 外側の変数が隠蔽されるから
-
varは巻き上げする- ブロックスコープを無視してしまう
- クロージャー
- 「外側のスコープにある変数への参照を保持できる」
- グローバル変数を減らせる
- 高階関数を作る
- 静的スコープ
- 変数の中身は静的に決まる
- 内側にいないなら一つ外側のスコープを確認する
- 変数の中身は静的に決まる
- メモリ管理の仕組み
- 解放はあくまでそのデータが参照されているかどうかで決まる
- 「外側のスコープにある変数への参照を保持できる」
関数とthis
- 実行コンテキスト
- Script
-
thisはwindowオブジェクト
-
- Module
-
thisはundefined- ES2020では
globalThis
- ES2020では
-
- Script
- アロー関数以外の関数のthis
- ベースオブジェクト
-
selfのようなもの- 実行時に決定される
- なければ
undefined
-
-
call、apply、bindで明示的に指定できる
- ベースオブジェクト
- アロー関数の
this- 外側で最も近い関数の
thisとなる- 静的に決まる
- 外側で最も近い関数の
非同期処理:コールバック/Promise/Async Function
- 同期的なブロック処理
- ブラウザでは大問題になる
- スクロールが効かなくなる
- ブラウザでは大問題になる
- 非同期処理はメインスレッドで実行される
- 非同期処理も同期処理の影響を受ける
- 非同期なタイミングで実行される処理
- 普通に
try...catch書くと非同期処理のエラーは処理できない - エラーファーストコールバック
- 共通ルールの一つ
- 処理が失敗したら
errorにエラーオブジェクトを渡す - 処理が成功なら2番目以降の引数に結果を渡す
fs.readfile("./example.txt", (error, data) => { if(error) { ...} else { ... } });
- 処理が失敗したら
- 共通ルールの一つ
- Promise
- 成功したら
resolve - 失敗したら
rejectconst executor = (resolve, reject) => {};
-
thenメソッドで成功時と失敗時の処理を渡す -
try...catchを使わなくても例外がキャッチされる-
catchはpromise.then(undefined, onRejected)のシンタックスシュガー
-
- 成功したら
- Promiseの状態
- Fullfilled
-
resolve成功したとき
-
- Rejected
-
reject失敗したとき
-
- Pending
- FullfilledまたはRejectedではないとき
-
new Promiseでインスタンスを作成したとき- 最初はPendingで、一度でも変化したらそこからは変わらない
-
resolveした後のrejectは呼び出されない -
resolveした後にもう一度resolveしても呼び出されない
-
- 最初はPendingで、一度でも変化したらそこからは変わらない
- Fullfilled
-
Promise.resolve- 最初からFullfilledな
Promiseインスタンスを作る-
Promise.rejectもある- テストコードで使われる
- 短く書けるから
- テストコードで使われる
-
- 最初からFullfilledな
- Promiseチェーン
- 失敗時は一番近い失敗処理が呼び出される
- 途中の
thenは無視される- 失敗を一度キャッチするとまたチェーンに戻る
-
catchはFullfilled状態のPromiseインスタンスを作成するから -
return Promise.reject(new Error("失敗"));- これはRejected状態となる
- キャッチしてもRejected状態を継続できる
- これはRejected状態となる
-
- 失敗を一度キャッチするとまたチェーンに戻る
- 例外時も同じ
- 途中の
-
returnで値を返すと次のthenへ引数として渡せる -
Promise#finallyは必ず呼び出される-
isLoadingをfalseにするなど
-
-
thenごとに配列に値をpushしていく使い方もできる-
Promise.allで複数のPromiseもまとめられる- どれを先に取得しても問題ないとき
- 一つでもRejectedとなったら失敗処理が呼び出される
- どれを先に取得しても問題ないとき
-
-
Promise.race- 1つでもSettled状態になれば次の処理をする
- 非同期処理のタイムアウトが作れる
- 一定時間経過しても処理が終わってないならエラーとする
-
timeoutとやりたい処理をPromise.raceの引数にする
-
- 一定時間経過しても処理が終わってないならエラーとする
- 非同期処理のタイムアウトが作れる
- 1つでもSettled状態になれば次の処理をする
- 失敗時は一番近い失敗処理が呼び出される
- Async Function
async function doAsync() { return "値"; }-
function doAsync() { return Promise.resolve("値"); }- 同じ
- 必ず
Promiseインスタンスを返す-
Promiseを返すただの関数と何も変わらない
-
-
awaitが使える
- 必ず
- 同じ
- どれにでもつけられる
async function fn1() {}const fn2 = async function() {};const fn3 = async() => {};const obj = { async method() {} };
-
await- 同期処理のように書ける
-
Promiseのresolveされた値がawaitの返り値になる-
const value = await Promise.resolve(42);- 例外があったらRejectedなPromiseが返る
-
try...catch構文でキャッチできる- 同期処理と同じ
-
- 例外があったらRejectedなPromiseが返る
-
- 非同期でもループ処理ができる
-
Promise.allも使える- Promiseを複数作ってから
Promise.allに渡す-
const promises = resources.map(function(resource) { return fetch(resource); });const responses = await Promise.all(promises);
-
- Promiseを複数作ってから
- Async Funtion の中でのみ使える
- 外の処理は止まらない
- UIなどの処理が止まってしまうから
- コールバック関数の時に注意
-
forをforEachに単純に変えられない -
Promise.allでまとめるか
-
- 外の処理は止まらない
Map/Set
- Map
- 初期値で渡せるのはエントリーの配列
new Map([["key1", "value1"], ["key2", "value2"]]);
-
sizeで数がわかる -
setで追加する- 同じキーは上書きされる
-
getで取り出す -
hasでそのキーがあるか確認できる -
deleteで削除する -
clearで全て削除する -
keys,valuesが返すのは配列ではない-
Array.from(map.keys());- 配列に変換して反復処理をする
-
- マップとしての
Objectとの違い- デメリット
-
Objectはprototypeメソッドで意図しない動きをすることがある- 昔は
Object.create(null)のようにして使っていた-
Mapが導入された
-
- 昔は
- キーは
Symbolのみ
-
- メリット
- リテラル表現で作成しやすい
-
JSON.stringifyで変換できる - 多くの場所で使われている
- デメリット
- 初期値で渡せるのはエントリーの配列
- WeakMap
- キーを弱い参照で持つ
- ガベージコレクションを妨げない
- iterableではない
-
keysやsizeがない
-
- 使い方
- イベントリスナーを管理する
- 使われなくなったら消える
- キャッシュとして一時的に計算結果を保存する
- イベントリスナーを管理する
- キーを弱い参照で持つ
- Set
- 同じ値を入れると1つのみ格納される
- インデックスはない
-
forEachが使える
- WeakSet
- データの一意性を確認することに特化したセット
JSON
-
JSON.parse-
json = [1, 2, 3]なら返り値も配列 - パースできないと例外が投げられる
- 基本的に
try...catchされる
- 基本的に
-
-
JSON.stringify- 第二引数に
replacerを渡せる- keyがnullならundefinedにする処理
- ホワイトリストとしてkeyの配列を渡せる
-
["id", "name"]だけを抜き出す
-
- 第三引数にフォーマット時のインデントを設定できる
-
JSON.stringify(obj, null, 2)ならスペース2個でインデント-
"\t"もできる
-
-
- Symbolやundefinedは変換されない
- オブジェクト内に
toJSONがある場合- その返り値のみ使う
- 特殊な形式でシリアライズできる
- その返り値のみ使う
- 第二引数に
Date
-
Date.UTC(2006, 0, 2, 15, 4, 5, 999)- UTCなのでローカルのタイムゾーンに影響されない
-
getMonthで取得すると+1する必要があるconst mm = String(date.getMonth() + 1).padStart(2, "0");
- ほとんどライブラリを使う
moment()
Math
- 乱数をつくる
Math.random()
ECMAScriptモジュール
- なぜモジュールを使うのか
- 保守性
- 依存性の高いコードをまとめて、他への依存性を減らせる
- 名前空間
- グローバルを汚染しない
- 再利用性
- コピペせずに再利用できる
- 保守性
- 名前つきエクスポート/インポート
export { foo };export function bar() {};import { foo, bar } from "./my-module.js";- エイリアス
export { internalFoo as foo };import { foo as myFoo } from "./named-export-alias.js";
- デフォルトエクスポート/インポート
- デフォルトエクスポートに名前をつけてインポートする
ECMAScript
- その機能がどのような経緯で入ったのかを調べる手段を持っておく
- その機能が何を解決するために導入されたのかを知る
- 調べたいと思ったときに調べることができるように、調べ方を知っておくことが重要
- その機能が何を解決するために導入されたのかを知る