はじめに
今回は JavaScript Primerを読んだのでアウトプットしていきます。
なかなかタフでした...
良かったこと
1つの記法やメソッドについてかなり詳しく書いていたことが良かった。
国語辞典を読んでいる感覚になった。
JavaScriptを学ぶ最初の1冊にするよりかは
YoutubeやUdemyでJavaScript初心者用の動画を1つ見てから取り組んだらより理解が深まる書籍だと思う。
実際にJavaScriptの課題に取り組んでから辞書的な扱いにするのが良いと考えている。
学んだこと
const
再代入できない変数
let
再代入ができる変数を宣言
var (※注意)
letと同じく再代入ができる変数を宣言できる
型検索
typeof演算子を使用
オブジェクトの詳細な種類を判定できない
Object.prototype.toStringでより詳細な型チェックができる
==を使う時
null と undefinedの比較
文字列を数値に変換
Numberコンストラクタ関数を利用する
NaNというもの
NaNは Not a Numberの略称で、特殊な性質を持つNumber型のデータ
NaNは暗黙的な型変換の中で最も避けたい値
理由は何と計算しても結果は NaN になる
NaNしか持っていない特殊な性質として、自分自身と一致しない
arguments
可変長引数を扱う方法として、関数の中でのみ参照できる特殊な変数
可変長引数が必要な場合は arguments 変数よりも、Rest parameters での実装
for...in文の問題点
オブジェクトのプロパティに対して、反復処理をする場合は
for文, forEach, for..of, Object.keys を利用する
オブジェクトの省略記法
プロパティと値が同じ名前の場合は以下のように省略可能
const baseball = "野球";
// `baseball`というプロパティ名で`baseball`の変数を値に設定したオブジェクト
const obj = {
baseball // baseball: baseballと同じ意味
};
console.log(obj); // => { baseball: "野球" }
オブジェクトと分割代入
const ja = languages.ja;
const en = languages.en;
console.log(ja); // => "日本語"
console.log(en); // => "英語"
// ↓以下のように1行でかける↓
const { ja, en } = languages;
console.log(ja); // => "日本語"
console.log(en); // => "英語"
プロパティの存在を確認する方法
- in演算子
const obj = { key: undefined };
// `key`プロパティを持っているならtrue
if ("key" in obj) {
console.log("`key`プロパティは存在する");
}
- Object.hasOwn静的メソッド
const obj = { key: undefined };
// `obj`が`key`プロパティを持っているならtrueとなる
if (Object.hasOwn(obj, "key")) {
console.log("`obj`は`key`プロパティを持っている");
}
作成したオブジェクトのプロパティの変更を防止
-
Object.freeze静的メソッドを利用
Object.freeze 静的メソッドを利用する場合は必ずstrict modeと合わせて使用
strict mode ではない場合は、凍結されたオブジェクトのプロパティを変更しても例外が発生せずに単純に無視される
"use strict";
const countries = Object.freeze({ ja: "日本" });
// freezeしたオブジェクトにプロパティを追加や変更できない
object.ja = "フランス"; // => TypeError: "ja" is read-only
Optional chaining演算子(?.)
const obj = {
a: {
b: "objのaプロパティのbプロパティ"
}
};
// obj.a.b は存在するので、その評価結果を返す
console.log(obj?.a?.b); // => "objのaプロパティのbプロパティ"
console.log(obj?.b?.a); // => undefined
オブジェクトのマージ(合体)
Object.assign静的メソッドを使用することで可能
第一引数には、空のオブジェクトリテラルを指定するのが典型的な利用方法
プロパティ名が重複した場合は、後ろのオブジェクトのプロパティにより上書きされる
プロトタイプオブジェクト
空のオブジェクトであってもメソッドが使用できるのは、プロトタイプオブジェクトがあるから。
const obj = {}; // 空のオブジェクト以下の toString メソッドを定義してない
console.log(obj.toString());
この toString が使える理由は以下
- オブジェクト(今回の例では作成した
obj)の継承元のObject(prototypeオブジェクト)で定義されているから - その結果、オブジェクトを作成した時点で
Objectを継承している
もし、プロトタイプオブジェクトと同じ名前のインスタンスメソッドがある場合はインスタンスメソッドの内容が優先される。
const i1 = {
name: "i1"
}
const i2 = {
name: "i2",
// プロトタイプメソッドと同名のメソッドをオブジェクトで定義(インスタンスメソッド)
toString: () => {
return "こちらが優先";
}
}
console.log(instance1.toString()); // [object Object]
console.log(instance2.toString()); // こちらが優先(※インスタンスメソッドが優先されている)
配列操作における破壊的な操作と非破壊的な操作
結論、以下の違いがある。
破壊的な操作:元の配列を変更する
非破壊的な操作:元の配列を変更せずコピーをして、コピーした配列に変更を加える
例;配列に1つ要素を加えたい場合
const nums = [1, 2, 3, 4, 5]
// 破壊的な操作の場合
nums.push(6);
console.log(nums) // [1, 2, 3, 4, 5, 6]
// 非破壊的な操作の場合
const newNums = [...numbers, 6];
console.log(nums); // [1, 2, 3, 4, 5]
console.log(newNums); // [1, 2, 3, 4, 5, 6]
Array-likeオブジェクト
配列っぽいけど、配列では無いもの。lengthプロパティを持つ。
mapやfilterなどの配列メソッドを直接呼び出すことが不可能
Array.prototype.<メソッド>とすることで実行可能な関数もある
// map
const arrayLike = {
length: 3,
0: 2,
1: 3,
2: 4,
3: 5, // length が 3 なので map() 空は無視される
};
console.log(Array.prototype.map.call(arrayLike, (x) => x ** 2));
// [ 4, 9, 16 ]
Array.from(), スプレッド構文を使用すると
Array-likeオブジェクトを配列に変換できる
正規表現のgフラグを使った繰り返しマッチ
正規表現における g フラグを使用した文字列検索で
マッチしたすべての文字列を含んだ配列が欲しい場合は
matchメソッドよりmatchAllメソッド+ for~of を使用することで
より詳細な情報を取得することができる。
const text = "価格は100円と200円です";
const pattern = /\d+/g; // gフラグ → 全部見つける
// match: 結果だけほしい
const results = text.match(pattern);
console.log(results); // ["100", "200"]
// matchAll: 詳しい情報もほしい
for (const match of text.matchAll(pattern)) {
console.log(`数字: ${match[0]}, 位置: ${match.index}`);
// 数字: 100, 位置: 3
// 数字: 200, 位置: 8
}
クロージャー
内側の関数が外側の関数の変数にアクセスできる仕組みのこと。
外側から内側へのアクセスはエラーとなる。
function createCounter() {
let count = 0;
return () => ++count;
}
// カウンターを作成
const counter = createCounter();
counter();
console.log(conter) // 1
counter();
console.log(conter) // 2
counter();
console.log(conter) // 3
結果: 呼び出すたびに1ずつ増える
count変数が保持されている。
- コールバック関数とthis
対処法: Arrow Functionでコールバック関数を扱う
Privateクラスフィールド
フィールド名の前に「#」をつけて、外部からアクセスできないようにする
機密データや計算ロジックで使用
クラス内部のメソッドでのみ操作可能である。
// 例:お小遣い管理
class PocketMoney {
#money = 0; // ← 秘密の残高(外から見えない)
earn(amount) {
this.#money += amount; // クラス内部でだけアクセス可能
}
checkMoney() {
return this.#money; // メソッド経由でのみ確認可能
}
}
throw文
問題が発生したときに、処理を強制的に止めてエラーを投げる文。
例外処理をする際に使用。
// 例:0で割るとエラーを発生させる
function divide(a, b) {
if (b === 0) {
throw new Error("ダメ!"); // ここで処理が止まる
}
return a / b; // ここは実行されない
}
then や catch メソッドを使用する場面
Promise インスタンスの状態が変化したときに
一度だけ呼ばれるコールバック関数を登録するのがthen や catch メソッドとなる。
非同期処理とは
簡単に言うと、〇〇しながら××すると言うことです。
ある処理を実行したあとに結果を待たずにすぐ次の処理を実行できる処理。
以下の記事でわかりやすく説明されていた。
非同期処理をすることで以下のようなことが実行できるようになります。
- 10記事のブログの読み込み→出力
- 同期:記事1読み込み → 記事1出力 → 記事2読み込み → 記事2出力 → 記事3読み込み → 記事3出力...
「1つ終わったらその次を読み込む」、「読み込み終わるまで次のブログを読み込めない」 - 非同期:記事1〜10を同時に読み込み開始 → 読み込めたものから順次表示
- 同期:記事1読み込み → 記事1出力 → 記事2読み込み → 記事2出力 → 記事3読み込み → 記事3出力...
Promiseとは
Promise オブジェクトとは、非同期処理結果およびその結果の値を返すオブジェクト。
Promise は以下の3つの状態のいずれかになっており
一度状態が変化すると変化後の状態からは変わらない。
- 待機 (pending): 初期状態。成功でも失敗でもない
- 履行 (fulfilled): 処理が成功して完了したという意味
- 拒否 (rejected): 処理が失敗したという意味
pending → fulfilled
このように状態が変化する場合には、成功時のresolved 関数が呼び出される
pending → rejected
このように状態が変化する場合には、エラー時のrejected 関数が呼び出される
Async Function
Promiseよりシンプルな記述で非同期処理を行う記法
純粋にthenで繋げて書くのはコードが長くなるから async/await が推奨される
特徴は以下
- 非同期処理を伴う関数の頭に
asyncを追記 - 非同期処理を伴う関数の実行時に
awaitをつける -
awaitはssync付きの関数でしか使えない
難しかったこと
Promise, async/await の理解をするのに苦戦しました。
私は以下の動画で概要を掴みました。
Promiseはかなり奥が深そうなので完璧に理解するより、都度調べて使う方が良いと感じます。
最後に
なかなかタフでした...。
私にとって JavaScript は第二言語となります。
これから長くお世話になっていくと思うので
JavaScriptに詰まったらお世話になろうかと考えています!