JavaScript 初心者の学習の軌跡（①基本編）

# はじめに
今まで何となく GAS を書いてGoogleスプレッドシートの改善とかやっていましたが、いつもわからないところを都度ググりつつ何となく JavaScript 書いているのがイヤになってきたので、ここで一度体系的に学習しようと思いました。（GAS ≒ JavaScript ではありますが…）

Salesforceの認定資格 [JavaScriptデベロッパー](https://trailhead.salesforce.com/ja/credentials/javascriptdeveloperi) 取得をゴールに見据えつつ、**自分が今まで知識としてちゃんと定着できていなかった部分**を記録として残していこうと思います。

なお学習には以下書籍を使用しています。(アフィリンクではありません）
また色々問題になりそうなので、記事内に本の内容をそのまま転載するようなことはせず、あくまで頭の中でかみ砕いた内容で記事作成をします。

https://www.shoeisha.co.jp/book/detail/9784798160276

# 実行環境
どちらも導入自体がかなり簡単でした！
学習のハードルが低いのは初心者には嬉しいところです。

## Visual Studio Code を使う
[Visual Studio Code](https://azure.microsoft.com/ja-jp/products/visual-studio-code/)
拡張機能：[Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)

## Chrome や Edge の開発者コンソール
Chrome（やEdge）で
「F12→コンソール」から、直接コードを記載可能。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/368775/0fd62377-4582-57f7-c8cb-2af08f32ad9b.png)

# 変数とデータ型

## データ型

### データ型の種類
| データ型 | 値 | 備考 |
|:-|:-|:-|
| String | 文字列 |   |
| Number | 数値 | 整数または浮動小数点数 |
| BigInt | 巨大な数値 |  |
| Boolean | 真偽値 | true / false |
| null | ヌル |   |
| undefined | 未定義 |   |
| Symbol | シンボル | 一意で不変な値 |
| Object | オブジェクト |   |

## プリミティブ型

### 文字列（String）
- 文字列リテラルはシングル、ダブル、バッククォートで表現可
- バッククォートのみ、`${変数名}` で変数内の値を直接参照可
```javascript
// 文字列リテラルはシングル、ダブル、バッククォートで表現可
console.log('こんにちは');	// > こんにちは
console.log("こんにちは");	// > こんにちは
console.log(`こんにちは`);	// > こんにちは

// バッククォートのみ、${変数名} で変数内の値を直接参照可
let name = "太郎"
console.log('こんにちは ${name}');	// > こんにちは ${name}
console.log("こんにちは ${name}");	// > こんにちは ${name}
console.log(`こんにちは ${name}`);	// > こんにちは 太郎
```

- シングルクォートやダブルクォートを、文字列として表現したい場合は以下の2パターン。
```javascript
// エスケープシーケンスで逃がす
console.log("こんにちは \"太郎\"さん");		// > こんにちは "太郎"さん

// 別のリテラルで囲う
console.log(`こんにちは "太郎"さん`);			// > こんにちは "太郎"さん
console.log("こんにちは '太郎'さん");			// > こんにちは '太郎'さん
```

### 数値（Number）
- 2進数は `0b`、8進数は `0o`、16進数は `0x` で表現。
```javascript
// 10進数：0以外の数値から始める
console.log(1234);		// > 1234

// 2進数：0bから始める（ゼロビー）
console.log(0b11);		// > 3

// 8進数：0oから始める（ゼロオー）
console.log(0o12);		// > 10

// 16進数：0xから始める（ゼロエックス）
console.log(0x12);		// > 18
```

### BigInt
Number型よりも大きな数値を **整数値** として扱える。
Number型は -($2^{53}$ - 1)～($2^{53}$ - 1) までの数値を正確に扱える。

BigIntは、数値の末尾に `n` を付けて表現。
```javascript
// 「2の53乗 + 1 = 9007199254740993」です

// 範囲外のため、間違った値が出力
console.log(2 ** 53 + 1);		// > 9007199254740992

// BigIntで表現することで、正確な値が出力
console.log(2n ** 53n + 1n);	// > 9007199254740993n
```

- BigInt と Number 型は、混在して使用不可（エラーになる）
- BigInt は整数値しか扱えない（小数点以下は切り捨て）
```javascript
// BigInt と Number 型は、混在して使用不可（エラーになる）
console.log(2n + 1);	// > Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions

// BigInt は整数値しか扱えない
let hoge = 5.1n;		// Uncaught SyntaxError: Invalid or unexpected token

// BigInt 同士の割り算は、小数点以下が切り捨てられる
let ten = 10n;
let three = 3n;
console.log(ten / three);	// > 3n（※小数点以下が切り捨てられた！）
```

#### コラム：安全な整数
Number型は -($2^{53}$ - 1)～($2^{53}$ - 1) が範囲。
これは「**IEEE754**」という規格に準じた、64ビット倍精度浮動小数点数に由来するそうです。

正常に扱える整数に対しては、「**安全な整数**」という表現をすることがあり、JavaScript では安全な整数かどうかを`Number.isSafeInteger()` で判定することが可能。
```javascript
// 安全な整数
console.log( Number.isSafeInteger(2 ** 53 - 1) );	// > true

// 安全な整数ではない
console.log( Number.isSafeInteger(2 ** 53) );		// > false
console.log( Number.isSafeInteger(2 ** 53 + 1) );	// > false
```

本コラムに関しては、以下の記事を参考に勉強させていただきました！（感謝:clap:）

https://qiita.com/uhyo/items/f9abb94bcc0374d7ed23#javascript%E3%81%AE%E6%95%B0%E5%80%A4%E3%81%AE%E7%B2%BE%E5%BA%A6%E3%81%AF53%E3%83%93%E3%83%83%E3%83%88

https://language-and-engineering.hatenablog.jp/entry/20150513/JavaScriptIeee754OutOfRangeError

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger

### null と undefined
- null は参照を保持していないことを表す
- undefined は変数が未定義であることを表す
```javascript
let hoge = null;	// null は参照を保持していない
let huga;			// 値を未設定

console.log( hoge );	// > null
console.log( huga );	// > undefined
```

## オブジェクト型

### 初期化とプロパティへのアクセス方法
- `{～}` をオブジェクトリテラルと呼び、プロパティと値を `:(コロン)` で区切って表現する
- ドットか、ブラケットを使って、プロパティにアクセスする
```javascript
// 人を表す person オブジェクト
let person = {
	age: 30,
	country: "Japan",
	name: { first: "太郎", last: "JS" }
};

// ドットか、ブラケットを使って、プロパティにアクセスする
console.log( person.age );				// > 30
console.log( person["age"] );			// > 30
console.log( person.name.first );		// > 太郎
console.log( person["name"]["first"] );	// > 太郎
```

- ブラケット記法でのみ、プロパティ名に変数を用いることが可能（ドット記法で下記は実現できない）
```javascript
// 全ユーザーを表す allUser オブジェクト
let allUser = {
	user1: "ユーザーA",
	user2: "ユーザーB"
};

// プロパティ名のベース
let propNameBase = "user";

// 変数を使って、プロパティにアクセス
console.log( allUser[ propNameBase + "1" ] );	// > ユーザーA
console.log( allUser[ propNameBase + "2" ] );	// > ユーザーB
```

### プロパティの追加・更新・削除
- オブジェクトには、プロパティの追加・更新・削除が可能
- 追加、更新は変数と同じ感覚で
- 削除は `delete` 演算子を使う
```javascript
// 人を表す person オブジェクト
let person = {
	age: 30,
	country: "Japan",
	name: { first: "太郎", last: "JS" }
};

// person オブジェクトに、性別(sex)を追加
person.sex = "male";
// person オブジェクトの、年齢(age)を更新
person.age = 35;
// person オブジェクトの、国(country)を削除
delete person.country;

console.log( person );
// > age: 35
// > name: {first: '太郎', last: 'JS'}
// > sex: "male"
```

### メソッド
- オブジェクト内に関数を持たせることができ、それをメソッドと呼ぶ
- メソッドは `function` で表現する
```javascript
// 挨拶を表す aisatsu オブジェクト
let aisatsu = {
	japanese: function() { console.log("こんにちは"); },
	american: function() { console.log("Hello"); }
};

aisatsu.japanese();		// > こんにちは
aisatsu.american();		// > Hello

// ES6 から、省略記法が可
let aisatsu2 = {
	japanese() { console.log("こんにちは"); },
	american() { console.log("Hello"); }
};

aisatsu2.japanese();	// > こんにちは
aisatsu2.american();	// > Hello
```

# 演算子

## 等価演算子
今まで何となく使っていた `==` と `===` の違い等、まとめてみます。

- `==` を**抽象的な等価性**と呼ぶ
- `===` を**厳格な等価性**と呼ぶ
- `==` では**値が等しい**ことを確認し、`===` では**値と型が等しい**ことを確認する
```javascript
// 1 と 1 の等価性
console.log( 1 == 1 );		// > true
console.log( 1 === 1 );		// > true

// 1 と "1" の等価性
console.log( 1 == "1" );	// > true
console.log( 1 === "1" );	// > false

// 1 と true の等価性
console.log( 1 == true );	// > true
console.log( 1 === true );	// > false
```
こう見ると、基本は `===` を使ったほうが良さそうな感触を得ました。

## 関係演算子
- `>`, `>=`, `<`, `<=` は他言語でも見受けられる一般的な使い方（なので省略）
- `in`：`prop in obj` の場合、プロパティ prop が、オブジェクト obj 内に存在するか確認できる
```javascript
// 人を表す person オブジェクト
let person = {
	name: "太郎",
	age: 18
};

// name プロパティは存在する
console.log( "name" in person );	// > true
// country プロパティは存在しない
console.log( "country" in person );	// > false
```

## 論理積と論理和
お馴染みの論理積（`&&`）と論理和（`||`）ですが、式としては判定が終わった段階の結果を `boolean` としてではなく、そのまま値として返す挙動のようです。

```javascript
// 論理積（&&）の結果を確認
console.log( "truthy1" && "truthy2" );				// > truthy2
console.log( "truthy1" && "truthy2" && "truthy3");	// > truthy3
console.log( "truthy" && null );					// > null
console.log( null && "truthy" );					// > null
console.log( null && undefined );					// > null

// 論理和（||）の結果を確認
console.log( "truthy1" || "truthy2" );				// > truthy1
console.log( "truthy1" || "truthy2" || "truthy3");	// > truthy1
console.log( "truthy" || null );					// > truthy
console.log( null || "truthy" );					// > truthy
console.log( null || undefined );					// > undefined
```

## null 合体演算子
`??` で表す。左のオペランドが `null` か `undefined` の場合、右のオペランドを返す。（逆なら左のオペランドをそのまま返す）
```javascript
// a が null なので、右のオペランドが返る
let a = null;
console.log( a ?? "null or undefined です" );	// > null or undefined です

// b が undefined なので、右のオペランドが返る
let b = undefined;
console.log( b ?? "null or undefined です" );	// > null or undefined です

// c が null や undefined じゃないので、c そのものが返る
let c = 0;
console.log( c ?? "null or undefined です" );	// > 0
```

# 制御構文

## for...in 文
オブジェクトの各要素を順番に返してくれる。
ただし返す要素の順番が担保されているわけではない点に注意！
```javascript
// 色を表す colors オブジェクト
const colors = { red: "赤", yellow: "黄", blue: "青" };

// for...in で列挙
for( const val in colors ) {
	console.log(val);
}
// > red
// > yellow
// > blue
```

### コラム：列挙可能性
`for...in` で列挙される/列挙されないは、各要素が持っている **プロパティ記述子** 内に定義されている。

プロパティ記述子の取得は `Reflect.getOwnPropertyDescriptor()` で取得可能。
プロパティ記述子の設定は `Reflect.defineProperty()` で設定可能。
列挙される/列挙されないは、プロパティ記述子内の `enumelable` が `boolean` で保持している。
```javascript
// 色を表す colors オブジェクト
const colors = { red: "赤", yellow: "黄", blue: "青" };

// for...in で列挙（3つとも表示される）
for( const val in colors ) {
	console.log(val);
}
// > red
// > yellow
// > blue

// 各要素のプロパティ記述子を見てみる
for( const val in colors ) {
	const propInfo = Reflect.getOwnPropertyDescriptor( colors, val );
	console.log(propInfo);
}
// > {value: '赤', writable: true, enumerable: true, configurable: true}
// > {value: '黄', writable: true, enumerable: true, configurable: true}
// > {value: '青', writable: true, enumerable: true, configurable: true}

// red のみ、enumerable の値を false に変える
Reflect.defineProperty( colors, "red", { enumerable: false } );
console.log( Reflect.getOwnPropertyDescriptor( colors, "red" ) );
// > {value: '赤', writable: true, enumerable: false, configurable: true}
//                                              ↑falseに変わった！

// for...in で列挙（red が出なくなる）
for( const val in colors ) {
	console.log(val);
}
// > yellow
// > blue
```

### コラム：for...in と for...of
似たような機能を持っていますが、主に配列に対して使用すると違いが顕著に現れました。
```javascript
// 配列を定義
const colorsAry = [ "red", "yellow", "blue" ];
// 色を表す colors オブジェクト
const colorsObj = { red: "赤", yellow: "黄", blue: "青" };

// for...in で配列を列挙すると、インデックスが表示
for( const val in colorsAry ) {
	console.log(val);
}
// > 0
// > 1
// > 2

// for...of で配列を列挙すると、配列の中身が表示
for( const val of colorsAry ) {
	console.log(val);
}
// > red
// > yellow
// > blue

// for...in でオブジェクトを列挙すると、key が表示
for( const val in colorsObj ) {
	console.log(val);
}
// > red
// > yellow
// > blue

// for...of でオブジェクトを列挙すると、エラー
for( const val of colorsObj ) {
	console.log(val);
}
// > Uncaught TypeError: colorsObj is not iterable
// ※オブジェクトリテラル {} で定義したオブジェクトは、反復可能オブジェクトではないため
//   for...of 文では使えない
```

オブジェクトに対して for...of を使いたい場合は、`Object.keys()`, `Object.values()`, `Object.entries()` を使う。
- `Object.keys()`：オブジェクトのキーを配列で取得
- `Object.values()`：オブジェクトの値を配列で取得
- `Object.entries()`：オブジェクトのキーと値のペアを配列で取得
```javascript
// 色を表す colors オブジェクト
const colorsObj = { red: "赤", yellow: "黄", blue: "青" };

// キーの一覧を配列で取得
const keys = Object.keys( colorsObj );
// 値の一覧を配列で取得
const values = Object.values( colorsObj );
// オブジェクトのキーと値のペアを配列で取得
const entries = Object.entries( colorsObj );

// キーの一覧を表示
for( const key of keys ) {
	console.log(key);
}
// > red
// > yellow
// > blue

// 値の一覧を表示
for( const val of values ) {
	console.log(val);
}
// > 赤
// > 黄
// > 青

// キーと値のペアを表示
for( const entry of entries ) {
	console.log(entry[0], entry[1]);
}
// > red 赤
// > yellow 黄
// > blue 青
```

#### まとめ
| 種類              | for...in    | for...of          | 備考                                              |
|:-----------------:|:------------|:------------------|:--------------------------------------------------|
| 配列              | indexが列挙 | 配列の中身が列挙  |                                                   |
| オブジェクト `{}` | keyが列挙   | エラー            | keys, values, entries を使えば、for...of も使用可 |

#### 使い分け
- 配列：`for` または `for...of` を使う
- オブジェクト：`for...in` を使う。もしくは `Object.keys()`, `Object.values()`, `Object.entries()` を経由してから、`for...of` を使う。

# 最後に
次の記事（※作成中）へ続きます。