JavaScriptをもうちょっと理解する54のトピック

## この記事について

JavaScriptはお好きですか？

Reactなどフロンドエンドを中心に人気の言語なので、触ってる人はたくさんいると思います。
奥深くて自分も好きな言語ですが、結構なんとなくで触っていたりしませんか？

今回は自分が後輩によく聞かれた事や、自分が引っかかった事をたくさん書いていきます。
つまり基本的には初心者向け、よくても中級者向けです。
何か一個でも知らない事があれば御の字のつもりで書いたので、ぜひ気軽に読んでください！

## JavaScript編

### 略称はJS、Javaダメぜったい

いなさそうなのに稀にいるという・・
Web開発から入ってプログラムを広く知らない場合に発生しやすい？
Javaは全く別に王者がいるので、万が一使っていたららやめましょう。
略称は __JS（じぇーえす）__ でお願いします。

### データ型、Object

Objectはキーと値をいくつも持てるJSのデータ型です。
JSにはStringやDate、Functionなど多くのデータ型（のようなもの）がありますが、ほとんどのオブジェクトはObjectを継承しています。

…と、最初は難しいことは考えず、`{}`で囲った、キーとデータを複数入れられるデータ型という程度でいいんじゃないでしょうか。データには文字列や数値などのシンプルなものから、配列や関数にオブジェクトまで基本的に何でも入れられます。

```js
const song = {
  name: '終わりなき旅',
  artist: 'Mr.Children',
  // functionも定義できます（メソッド）
  play: function () {
    console.log(`play music ${this.name} by ${this.artist}`);
  },
  // オブジェクトをネストしてもOK
  tags: {
    primary: ['Rock', 'J-POP'],
    secondary: [],
  },
};
```

[Object \- JavaScript \| MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object)


#### コンストラクタ関数
※長くなるのでこの記事では少し触れるだけにします

Dateなどは **コンストラクタ関数** とも呼ばれます。例えば `new Date()`をすると、(1)現在日時をデータに持つObjectのデータ `{}` が作成されて、(2)そのプロトタイプにはDateがセットされます。

つまりDateのプロトタイプをもった、「データ型はObject」なデータが出来上がります。Dateオブジェクトと呼ぶのは間違いではないと思いますが、「データ型はObject」です（くどくてすみません）。プロトタイプについては次の項目で出てきますが、詳しく知りたい方は「コンストラクタ関数」などで調べてみて下さい。

[Date\(\) コンストラクター \- JavaScript \| MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/Date)


### プロトタイプチェーン

JSではほぼ全てのオブジェクトがObjectを継承していると言いました。つまりオブジェクトの祖先を辿るとObjectに行き当たります。

辿るために使うものを __プロトタイプチェーン__ といいます。具体的には、オブジェクトの__proto__に継承したプロトタイプが入っています。(__proto__は現在非推奨でObject.getPrototypeOf(obj)を使うべきですが、見やすさのために古い__proto__で書きます）

```js
var obj = {
    a: 1,
    b: 2,
    __proto__: {
        b: 20,
        c: 3,
    }
}

obj.a // ある -> 1
obj.b // ある -> 2
obj.c // ない -> obj.__proto__にある -> 3
obj.d // ない -> obj.__proto__にない -> obj.__proto__.__proto__に…(以後略)
```

[継承とプロトタイプチェーン \- JavaScript \| MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)
[Object\.prototype\.\_\_proto\_\_ \- JavaScript \| MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/proto)

### プロトタイプチェーンへの追加

プロトタイプチェーンにはプロパティやメソッドを追加できます。
PHPのイメージ的にはクラスにメソッドを書いたような感じです。

```js
// 書き方はこれだけ
Object.prototype.hoge = 'fuga';
Object.prototype.greet = function () {
    console.log('hello');
};
```

### テンプレート文字列

\` 2つで文字を囲う書き方をテンプレートリテラルと言います。今まで文字列はシングルクォート `'` かダブルクォート `"` で囲みましたが、同じような使い方です。

テンプレート文字列では変数展開ができ、`+`演算子で結合するよりも見やすく書けるのが特徴です。

```js
var a = 'hello, ' + name;
var b = `hello, ${name}`;
```

### Objectのプロパティの指定

基本はオブジェクトに `.` 繋ぎでキーを指定します。角カッコで囲んで、中にプロパティ名の文字列を指定しても可能です。

```js
console.log(song.name);
// または
console.log(song['name']);
```

JavaScript 識別子として有効ではないプロパティ名（プロパティ名の先頭が数値や、空白を含むなど）はドット繋ぎで呼び出せないため、後者で指定します。また、プログラムで動的なプロパティ名を指定したい場合にも後者で指定したりします。

```js
// 先頭が数値など無効な識別子
console.log(song['1st']);
// 動的な名前
console.log(song['name_' + i]);
```

[オブジェクトでの作業 \- JavaScript \| MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Working_with_Objects)

### オブジェクト型とプリミティブ型

JSのデータ型はプリミティブ型（文字列、数値、BigInt、真偽値、undefined）と、オブジェクト型に大別されます。
雑に言えば、文字とか数値などシンプルなやつがプリミティブ、それ以外はオブジェクトです。

[Object \- JavaScript \| MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object)
[Primitive \(プリミティブ\) \- MDN Web Docs 用語集: ウェブ関連用語の定義 \| MDN](https://developer.mozilla.org/ja/docs/Glossary/Primitive)

### プリミティブ型でも対応するメソッドが使える（ラッパーオブジェクト）

文字列 'abcde' は、Stringオブジェクトのメソッド `toUpperCase()` を呼び出すことができます。これは文字列でメソッドを呼ぼうとすると、対応するラッパーオブジェクト String のデータ型に変換してメソッドが呼ばれます。

[Primitive \(プリミティブ\) \- MDN Web Docs 用語集: ウェブ関連用語の定義 \| MDN](https://developer.mozilla.org/ja/docs/Glossary/Primitive#javascript_%E3%81%AE%E3%83%97%E3%83%AA%E3%83%9F%E3%83%86%E3%82%A3%E3%83%96%E3%83%A9%E3%83%83%E3%83%91%E3%83%BC%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88)

### window、それはグローバルオブジェクト

ブラウザのJSで頂点に君臨するのがwindowオブジェクトです。ブラウザのタブごとにwindowオブジェクトがあって、ページのhtmlなどのDOMが入ったdocumentプロパティや、ページ履歴などの情報を持つhistoryプロパティなどたくさんの情報を持っています。

ちなみにNode.jsにおけるグローバルオブジェクトは global です。

[Window \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/Window)

### グローバルオブジェクトは省略できる

ブラウザのJSを勉強するときに `document.getElementById('hoge')` や `alert('fuga')` みたいな書き方を見たことはないでしょうか？

厳密には `window.document.getElementById('hoge')` や `window.alert('fuga')` ですが、JSではグローバルオブジェクトを省略できるためです。

### DOM(Document Object Model)

window.documentオブジェクトにはページの情報、DOM(Document Object Model)が入っています。htmlが解釈されると、各要素はタグに記載したようなツリー状でDOMに格納されます。DOMは様々なインターフェースを用意していて、例えば getElementById のようなAPIを使用してページ上の要素を取得できます。

### スコープは変数などが見える範囲のこと

JSでは変数や関数を参照可能な範囲があり、それをスコープと呼びます。

- 関数の中で定義されたものは、その関数の中だけで見える
- 関数の外側で定義されたものは、関数の中からでも見える

```js
var outside = 100;

function fugaFunc() {
  var inside = 50;
}

function hogeFunc() {
  console.log(outside); // 100 関数の中から外側は見える
  console.log(inside);  // error 他の関数の中は見えない
}

console.log(inside); // error 関数の中は見えない
```

[Scope \(スコープ\) \- MDN Web Docs 用語集: ウェブ関連用語の定義 \| MDN](https://developer.mozilla.org/ja/docs/Glossary/Scope)

### ブロックスコープ

`{}` で囲まれたプログラムをブロックと呼びますが（if文とかで普通に書いてるやつです）、関数の単位でなくブロックの範囲内で参照できるスコープをブロックスコープと言います。letやconstはブロックスコープになります。

### 宣言(var,let,const)なしの変数はwindowオブジェクトのプロパティになる

`var = `をつけずに変数を定義すると、windowオブジェクトのプロパティになります。

```js
hoge = 100;

function foo() {
    bar = 200;
    console.log(bar); // 200
}

console.log(hoge); // 100
console.log(window.hoge); // 100

console.log(window.bar);

foo();

// foo()実行後はbar = 200を通って、window.barに代入されている
console.log(window.bar); // 200
```

### 宣言があっても関数外でvar変数を宣言するとwindowオブジェクトのプロパティになる

関数外というのは、functionなどに一切囲まれてない領域です。

```js
// 一番外側でvar定義した変数はグローバルオブジェクトのプロパティになる
var aaa = 100;
console.log(aaa); // 100
console.log(window.aaa); // 100

function hoge() {
    var bbb = 200;
    console.log(bbb); // 200
    console.log(window.bbb); // undefined
}
hoge();
```

その意味でも、varは使わずletを使う方が良いです。

### use strictで厳格(strict)モードに

`use strict;` をファイルや関数の先頭に書くことで、ファイルや関数を厳格モードにできます。
厳密モードで実行されると、例えば先ほどの未宣言変数への代入でグローバルオブジェクトのプロパティにセットされるのを防げます（エラーになる）

試しにjqueryやvueのコードなどを見てみると、先頭に書いてあるはずです。

```js
"use strict"; // ファイルの先頭に書く
aaa = 100; // スコープに宣言されていない変数名なのでerror
```

```js
// 関数単位での先頭
function hoge() {
    "use strict"; // 関数の先頭に書く
    aaa = 100; // スコープに宣言されていない変数名なのでerror
}
```

既存のプログラムに入れると不具合が起こる可能性があるため、新規の場合に入れていくと良いのかなと。

[厳格モード \- JavaScript \| MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Strict_mode)

### 変数宣言の違い

var: スコープは関数、再宣言できる、再代入できる
let: スコープはブロック、再宣言できない、再代入できる
const: スコープはブロック、再宣言できない、再代入できない

### 変数宣言はできればconst、次点でlet

基本的に変数はconstかletだけで宣言しましょう。どちらもvarより影響範囲が狭い（ブロックスコープ）点で優れています。
同様にletよりも再代入できないconstの方が処理中で値が変わらないのでバグを生み出しにくい点で良いですが、再代入が必要なら使っても問題ないと思います。

[【JavaScript】var / let / const を本気で使い分けてみた \- Qiita](https://qiita.com/cheez921/items/7b57835cb76e70dd0fc4)
[\(345\) JavaScriptの変数宣言『let, var, const』の使い分けについて【プログラミング小話】 \- YouTube](https://www.youtube.com/watch?v=yWDmFmCM7fw)

### 再代入できないけど中身は変えられる（変えられてしまう）

const、再代入はできませんが、オブジェクトの中は変更できるので扱いには注意です。配列も同様です。

```js
const obj = {a: 1, b: 2};
obj = {c: 3}; // 再代入はエラー
obj.c = 3; // 中身は変えられる!!
```

### イベントの設定

特定の要素にクリックなどのイベントを設定するには addEventListener で処理関数をセットします。

```js
const el = document.querySelector('.hoge'); // .hoge要素
el.addEventListener('click', function (event) {
    console.log(`click .hoge`);
}
```

### キャプチャリングフェーズとバブリングフェーズ

下記のように親から順番に div > p > span とタグが階層になっている要素があるときに

```html
<div>
  <p>
    <span>テキスト</span>
  </p>
</div>
```

spanタグをクリックすると、clickイベントは上の階層(window)から順番に要素を下に降りてきます。これをキャプチャ（フェーズ）といいます。
クリックした span 要素に到達すると、次はspanから上の階層に登っていきます。これをバブリング（フェーズ）といいます。

(1)キャプチャフェーズの進行方向（おりる）→
window > document > html > body > div > p > span
←(2)バブリングフェーズの進行方向（のぼる）

### 基本はバブリングフェーズで処理する

一般的にイベントはバブリングフェーズで処理します。先程の例だと、クリックしたspan要素にイベントがあれば処理して、次にp要素にイベントがあれば処理して、さらに上の階層のイベント処理をして・・という風です。（イベントが登録されていない要素は何もせず上にのぼります）

jsでaddEventListenerを書いた場合、引数に明示的に指定しないとバブリングフェーズでの処理になります。
（jQueryはキャプチャフェーズで処理する設定すらありません）

キャプチャフェーズは、外部のライブラリなどでバブリングフェーズが定義されていて、それを上書きや打ち消したりする場合などに使われたりするようです。

```js
// キャプチャフェーズに登録するには第3引数にオブジェクトで指定
el.addEventListener('click', 処理関数, {capture: true});
// または単にtrueを設定
el.addEventListener('click', 処理関数, true);
```

### イベントの制御

addEventListenerのコールバックの第1引数にはEventオブジェクトが入ります。Eventオブジェクトの特定のメソッドを呼ぶことでイベントの伝播（キャプチャリング＋バブリング）や、タグのデフォルトの動作（aタグならリンク遷移）などを止めるなどの制御ができます。

[1分でわかるreturn false; preventDefault\(\); stopPropagation\(\) の違い \| iwb\.jp](https://iwb.jp/return-false-preventdefault-stoppropagation/)

### event.preventDefault()

タグのデフォルトの動作（aタグならリンク遷移）を止めます。
例）デザインの都合でaタグを使っているけど、ページ遷移させたくない場合など。

```js
const el = document.querySelector('a');
el.addEventListener('click', function (event) {
    event.preventDefault(); // aタグのデフォルトイベント(=リンク遷移)をキャンセルする
});
```

### event.stopPropagation()

イベントが伝播（キャプチャリング＋バブリング）するのを停止します。これが呼ばれると、それ以降のタグにイベントが渡りません。
例）ボタンのクリックは処理したいけど、親に設定してあるイベントを動かしたくない場合など。

preventDefault()とは意味合いが違うので、必要に応じて両方呼んでもOKです。

```js
const el = document.querySelector('a');
el.addEventListener('click', function (event) {
    event.stopPropagation(); // aタグの上位にあるタグにイベントを回さない
});
```

### eventでreturn false;

その場でそのイベントの処理を終了するのに加えて、イベント伝播とデフォルト動作も停止します。
イベント処理終了＋preventDefault＋stopPropagationという感じです。

### おまじないの意味

こんな感じの書き方を見たことないでしょうか。
意味合いとしては、スコープ制限（グローバルな領域に変数などを宣言しない）をするために記述します。

```js
// おまじない
(function () {
  // 処理
})()

// 1. 実行する処理が動くための無名関数を定義
function () {
  // やりたい処理
}

// 2. 即時で実行するためにカッコで囲う
(function () {
  // やりたい処理
})

// 3. その場で実行するために末尾にカッコをつける
(function () {
  // やりたい処理
})()
```

[【jQuery】よく見るおまじない的なアレについて \- Qiita](https://qiita.com/tomcky/items/63cd6a35180af0699962)

## jQuery編

### jQueryはJSで書かれたユーティリティライブラリ

jQueryはJSで記述されたユーティリティです。
DOMの記述をCSS風にすることで書きやすく、またブラウザごとの挙動の差異の吸収などをしてくれます。

DOMにquerySelectorが実装されたり、ReactやVueなどのフレームワークの登場で出番は減ったものの、作られた大量のプラグインなどもあり、今なお便利な存在です。

JSのユーティリティとしては他にUnderscore.jsなどが有名です。

[Underscore\.js](https://underscorejs.org/)

### $はjQueryのエイリアス

何気なく書いている `$(selector)` って何か分かるでしょうか？

`$` はjQueryのエイリアスになります。つまり `$(なにか)` も `jQuery(なにか)` も処理は同じです。
なぜ `$`か… $はJSの変数として普通に使える文字であり、なおかつよく使うのでできるだけ短く（1文字）ということで白羽の矢が立ったんだったと思います。

### jQuery関数

ではjQuery関数は何か、それは渡したものに応じていい感じのjQueryオブジェクトを返してくれる関数です。

セレクタを指定すればページ中の要素が入ったjQueryオブジェクトを、タグを書けば新しく作った要素の入ったjQueryオブジェクトを返してくれます。

```js
$('div') // -> ページ中のdiv要素が入ったjQueryオブジェクト
$('<p>ほげ</p>') // ほげというテキストが入ったpタグ …が入ったjQueryオブジェクト
```

### jQueryオブジェクト

jQueryオブジェクトは、DOMの要素が入っている特別なオブジェクトです。

例えば `addClass` というメソッドを持っていて、これを呼び出すと、中に入っている要素に引数で指定したクラスの付与をします。

```js
const $allDiv = $('div'); // ページ中のdivが全て入ったjQueryオブジェクト
$allDiv.addClass('hoge'); // 中のjQueryオブジェクトそれぞれにhogeクラスを追加
```

一般的には、jQueryオブジェクトが入った変数は先頭に `$` をつけることが多いようです。

### jQueryオブジェクトとJS(DOMオブジェクト)の比較

以前はDOM操作はjQueryの方が書きやすい印象でしたが、今は素のJS（バニラJS）も進化して、かなり近いものを書けるようになりました。例えば特定のクラス要素を取得してクラスを付与するコード。

```js
// jquery
$('.hoge').addClass('fuga');

// js、querySelector以前は取得してぐるぐる
var hogeList = document.getElementsByClassName("hoge");
for (var i = 0, len = hogeList.length; i < len; i++) {
    hogeList[i].classList.add('fuga');
}

// js、querySelector(All)を使う
const hogeList = document.querySelectorAll('.hoge');
hogeList.forEach(function(el) {
  el.classList.add('fuga');
});

// js、一発でやってもいい
document.querySelectorAll('.hoge').forEach(function(el) {
  el.classList.add('fuga');
});
```

[Document\.querySelectorAll\(\) \- Web API \| MDN](https://developer.mozilla.org/ja/docs/Web/API/Document/querySelectorAll)
[なぜ素の JS のことを Vanilla と呼ぶのか調べてみた](https://zenn.dev/seya/articles/66809b3d59151c)

### jQueryオブジェクトのメリット、空振りできる

違いが少なくなったらわざわざjQueryを使う必要もないですが、便利な面もあります。
例えば、先程のような感じで、特定の要素を取得してクラスを付与するコードで、特定の1要素がターゲットの場合を考えます。

```js
// jquery #hoge要素がない場合、対象要素が0のjQueryオブジェクトにaddClass → 何も起きない
$('#hoge').addClass('fuga'); 

// js #hoge要素がない場合、hogeはnull → nullにclassListというプロパティがない → エラー
var hoge = document.getElementById("hoge");
hoge.classList.add('fuga'); 

// js(エラー対策) hogeがあるときだけ実行する
var hoge = document.getElementById("hoge");
if (hoge) {
  hoge.classList.add('fuga');
}
```

このように、対象要素がないとgetElementByIdはnullを返すので（querySelectorでも同様）チェックが必要になります。この挙動がいいとも悪いとも言えませんが、エラーで止まることがないのはメリットと言えるかもしれません。

ちなみにJSでも複数要素を取得してforEachで処理する場合は、0件でもエラーになりません。
複数要素を取得するメソッドは数に限らずNodeListが返るので、NodeList要素.forEach(処理)は要素がなければ処理しないだけなので大丈夫です。

#### JSでもオプショナルチェイニングで空振り可能（※追記）
コメントいただいたので追記します。
この記事の最後の方で紹介しているオプショナルチェイニング `?.` を使えばJSでも空振りできます！

```js
// .hoge 要素がなければundefinedが返って終了
var hoge = document.getElementById("hoge");
hoge?.classList.add('fuga');

// 1行で書いてもOK
document.getElementById('hoge')?.classList.add('fuga');
```

### jQueryオブジェクトからn番目の要素をjQueryオブジェクトとして取り出す

jQueryオブジェクトに複数の要素が格納されているとき、n番目の要素を取り出す方法は `$obj.eq(n)` で取り出せます。
ここで取り出したものは __jQueryオブジェクト__ として取得されます。
※セレクタの時点でn番目に絞り込む場合は `$('selector:eq(n)')`のように書く

```js
const $divList = $('div'); // ページ中のdivが全て入ったjQueryオブジェクト
const $3rdDiv = $divList.eq(2); // 3番目のdivをjQueryオブジェクトで取り出す(0はじまり)
```

### jQueryオブジェクトからn番目の要素をDOMオブジェクトとして取り出す

jQueryオブジェクトに複数の要素が格納されているとき、n番目の要素を取り出す方法は `$obj.get(n)` で取り出せます。
ここで取り出したものは __DOMオブジェクト__ として取得されます。

```js
const $divList = $('div'); // ページ中のdivが全て入ったjQueryオブジェクト
const $3rdDiv = $divList.get(2); // 3番目のdivをDOMオブジェクトで取り出す(0はじまり)
```

### jQueryのイベント定義

jQueryでイベントを設定する場合、主に `.on` を使います。

```js
$('.btn').on('click', function (event, el) {
    // 処理
});
```

### jQueryでイベントコールバック関数のthisはDOMオブジェクト

jQueryでコールバック関数に渡される this はDOMオブジェクトが入っています。jQueryオブジェクトではないため、例えばaddClassなどjQueryのメソッドを使いたい場合は $(this) などでjQueryオブジェクトにしてあげる必要があります。

```js
$('.btn').on('click', function (event, el) {
    const el = this; // DOMオブジェクト
    const $el = $(this); // jQueryのメソッドを使いたいなら
    $el.addClass('hoge'); // など
});
```

### noConflictの説明

jQueryはscriptタグで読み込まれると、その時点で `window.jQuery`に入っているものを内部の `_jQuery`に、 `window.$`に入っているものを内部の `_$`に退避して、読み込んでいるjQueryを新たに `window.jQuery`と `window.$`にセットします。

ここで `jQuery.noConflict(true);` か `$.noConflict(true);` を呼ぶと、window.jQueryとwindow.$を読み込む前に戻して、noConflictの関数はそのjQuery自身を返してくれます。

ちなみにnoConfilctの引数を空またはfalseにすると、 `window.$`だけ戻して `window.jQuery`はそのままです。 `$` だけを使っているユーティリティなどと同時に読み込むことを想定しているんでしょうかね・・

noConflictの使いどころは、複数バージョンのjQueryを共存して使いたい、 `$`をショートカットに使うユーティリティを同時に使用する場合などです。

```html
// window.jQueryやwindow.$には仮にjQuery2が入ってるものとする

<!-- ↓の読み込みとパースが終わると、window.jQueryとwindow.$はjquery3のものに置き換わっている -->
<script src="jquery3.js"></script>

<script>
// noConflictで window.jQueryはjquery3の直前に戻って、jQuery3に読み込んだjquery3が入ってきます
const jQuery3 = jQuery.noConflict(true);

(function ($) {
    // おまじないの書き方。この中で書く$はjQuery3で使える
})(jQuery3);

// ここで書く$はjquery3よりも上で読み込んでいた$として使える
</script>
```

### jQueryのfnはprototypeのエイリアス

```js
console.log($.fn.jquery) // バージョンが出る
```

上記のような感じでバージョンが見られます。fnは何かというと、prototypeのエイリアスです。prototypeにはjQueryオブジェクトが使うメソッドが入っています。

つまり、要素が入ったjQueryオブジェクトで呼んでも結果が得られます

```js
console.log($('div').jquery) // バージョンが出る（特に意味はない）
```

[jQuery\.fn のfnってどういう意味? $\.fnの使い方\[jQuery\] \- D\-NET](https://forsmile.jp/javascript/1610/)

## ECMAScript

### ECMAScript(ES)はJSの規格

ECMAScript（以下ES）はEcma Internationalという情報分野の国際的な標準化団体が定めたプログラムの仕様です。ESの仕様に基づいて各社が実装したものがJSで、JSはESの基本仕様に加えてブラウザ関係の機能なども実装されています。

いわばESが親、JSは子・・・とは言うものの、ESはそもそもJSを元に作られたそうなので、そう単純でもないのかも。

ちなみに今はなき、Flashで使われたActionScriptもES準拠の言語です。

### 対応状況はブラウザによってまちまち、変換が主流

モダンブラウザはESにかなり対応していて、普通にブラウザ上のJSで使えるものも多くなっています。

とはいうものの.vueのような単一コンポーネントファイルや、Reactで使うJSXのように素のjsでないものが含まれることも多いので、実際はESを使ったコードを書く → ViteやbabelなどNode.js系のシステムで変換・ビルドして使われるのが主流かと思います。

ただ、シンプルなアロー関数や分割代入みたいな記法は、ブラウザ上のJSだけでも十分普及しているものと思われます。

### ESのバージョンと対応内容など

[JavaScript \| JavaScriptとECMAScriptの違い](https://www.javadrive.jp/javascript/ini/index11.html#:~:text=ECMAScript%20%E3%81%AF%E5%AE%9A%E6%9C%9F%E7%9A%84%E3%81%AB,%E3%81%8C%E5%8A%A0%E3%81%88%E3%82%89%E3%82%8C%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82)

ここに一覧があります。西暦とバージョン番号の2種類の略称があります。ES2015はES6と同じで、ES2016はES7と同じ。（出来れば ES2015 = ES5 みたいに末尾が揃っていてくれたら分かりやすかった）

バージョンごとに追加された主な機能は以下の通りです。

- ES2015(ES6)
    - クラス、モジュール、アロー関数、テンプレート文字列、let、const、デフォルト引数、Promise、分割代入、可変長引数
- ES2017(ES8)
    - 非同期関数 (async/await)
- ES2018(ES9)
    - オブジェクトに対するスプレッド構文、非同期イテレーション、Promise.prototype.finally、正規表現への機能追加
- ES2020(ES11)
    - オプショナルチェイニング演算子?.、Null合体演算子??

[ECMAScript \- Wikipedia](https://ja.wikipedia.org/wiki/ECMAScript)

### スプレッド構文

`...` ドット三つのスプレッド構文で、配列やオブジェクトの要素を全て展開できるようになりました。
配列やオブジェクトのコピーなどで使ったりします。

```js
// 元のオブジェクトをコピー＆要素追加の例
const obj = {a: 1, b: 2, c: 3};
const obj2 = { ...obj };       // コピー
const obj3 = { ...obj, d: 4 }; // 要素追加しつつコピー
```

[スプレッド構文 \- JavaScript \| MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax)
[JavaScript operator: Spread syntax \(\.\.\.\) \| Can I use\.\.\. Support tables for HTML5, CSS3, etc](https://caniuse.com/mdn-javascript_operators_spread)

### アロー関数

以前よりfunctionを短く書けるようになりました。

```js
// 従来の関数
function (a) {
  return a + 100;
}

// アロー関数に段階的に分解

// 1. function を削除、引数と本体の { の間に =>を配置する
(a) => {
  return a + 100;
}

// 2. 本体の {} と "return" を削除（returnは暗黙でされる）
(a) => a + 100;

// 3. 引数の括弧を削除
a => a + 100;
```

引数が0個の場合には引数にカッコ()が必要だったり、本体の処理が2行以上になる場合は {} と returnを削除できないなど、ルールはあります。

どこまで省略するかは文化やルールによると思いますが、アロー関数は一般的になった印象です。

[アロー関数式 \- JavaScript \| MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions)

### オブジェクトのプロパティ省略記法

オブジェクトの `キー` と `値にする変数名` が同じとき、`キー: 値` と書かずに `キー` と書けます。

```js
let name = 'kom',
    tags = {hobby: 'sake'};

// ちょっと冗長
let obj = {
  name: name,
  tags: tags,
}

// 省略してシンプルに
let obj = {
  name,
  tags,
}
```

[ES6オブジェクトリテラルの拡張 \- Qiita](https://qiita.com/dondoko-susumu/items/aa79159d137c39251060)

### オブジェクトのメソッド省略記法

オブジェクトのメソッド定義で、functionキーワードを使わずに省略できます

```js
// 普通に
var counter = {
  count: 0,
  increment: function() {
    this.count++;
  }
};

// 省略記法 => ":" と "function" を省略
var counter = {
  count: 0,
  increment() {
    this.count++;
  }
};

// アロー関数を使うとthisの意味が変わるのでこれはNG
var counter = {
  count: 0,
  increment: () => {
    this.count++;
  }
};
```

[ES6オブジェクトリテラルの拡張 \- Qiita](https://qiita.com/dondoko-susumu/items/aa79159d137c39251060)

### 分割代入

今まで配列やオブジェクトのデータを変数に代入したり、引数に渡すときはまるごと渡すしかなかったものを、一部の中身だけ渡せるようになりました。

```js
// 配列の場合
const info = ['Kom', 41];

// 今まで
const greeting = function (info) {
    console.log(`Hi, ${info[0]}! I'm ${info[1]}.`);
}
greeting(info);

// あるいは呼び出すときに個別に
const greeting = function (name, age) {
    console.log(`Hi, ${name}! I'm ${age}.`);
}
greeting(info[0], info[1]);

// 分割代入
const greeting = function ([name, age]) {
    console.log(`Hi, ${name}! I'm ${age}.`);
}
greeting(info);
```

```js
// オブジェクトの場合
const info = {
    name: 'Kom',
    age: 41,
};

// 今まで
const greeting = function (info) {
    console.log(`Hi, ${info.name}! I'm ${info.age}.`);
}
greeting(info);

// あるいは呼び出すときに個別に
const greeting = function (name, age) {
    console.log(`Hi, ${name}! I'm ${age}.`);
}
greeting(info.name, info.age);

// 分割代入
const greeting = function ({name, age}) {
    console.log(`Hi, ${name}! I'm ${age}.`);
}
greeting(info);
```

[分割代入](https://ja.javascript.info/destructuring-assignment)

### 関数のデフォルト引数

関数の引数にデフォルト値を持たせることができます

```js
function multiply(a, b = 1) {
  return a * b;
}

console.log(multiply(5, 2)); // 10

console.log(multiply(5)); // 5
```

[デフォルト引数 \- JavaScript \| MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Default_parameters)

### Promise

Promiseオブジェクトは、非同期処理の完了や失敗の結果と、その結果の値を表すことができるオブジェクトです。Promiseを使うことで、コールバックのネストではない書き方ができるようになりました。

（Promiseはかなり込み入った説明になるのでこの程度に・・)

[Promiseとasyncとawaitをできるだけ短く解説してみる](https://qiita.com/komlabo/items/1d942878ac0afef77a78)

```js
// コールバックネスト地獄の例
$.get(url1, function (res) {
    $.get(url2 + '/' + res.id, function (res2) {
        $.get(url3 + '/' + res2.id, function (res3) {
            // など
        })
    })
})

// Promiseチェーン
fetch(url1)
    .then(response => response.json())
    .then(res => {
        return fetch(url2 + '/' + res.id)
    })
    .then(response2 => response2.json())
    .then(res2 => {
        return fetch(url3 + '/' + res2.id)
    })
    ...
```

[Promise \- JavaScript \| MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise)
[Promise と async/await の理解度をもう1段階上げる \- Qiita](https://qiita.com/sotszk/items/f23199e864cba47455ce)

### クラス

JSでもクラスが使えます。試しに下記のコードをChromeのコンソールで実行すると、Hello, Edgeが出力されます

```js
class Browser {
    constructor(name) {
        this.name = name;
    }

    greet() {
        console.log('Hello, ' + this.name);
    }
}

var edge = new Browser('Edge');
console.log(edge.greet()); // Hello, Edge
```

### モジュール

別ファイルのJSを取り込むことが出来ます。
PHPでいうとuseのようなイメージです。

exportでモジュールを提供して、importで他のファイルを読み込みます。
```js
// sample-alert.js で sayMessageをエクスポート
export function sayMessage(message) {
  alert(message);
}

// index.jsで他ファイルの sayMessageをインポート
import { sayMessage } from "./sample-alert.js";
sayMessage("こんにちは世界");
```

[ES Modules入門 \- JavaScriptでモジュールを使う時代 \- ICS MEDIA](https://ics.media/entry/16511/)


### 非同期関数 (async/await) でPromiseをさらに簡単に

asyncとawaitキーワードを使うと、thenとcatchのメソッドチェーンですらなく、一連の処理のように書けます。
内部的にはPromiseを使っており、async/awaitは単純にPromiseのシンタックスシュガーです。

```js
async function fetchData() {
    const res1 = await fetch(url1)
        .then(response => response.json());
    const res2 = await fetch(url2 + '/' + res.id)
        .then(response => response.json());
    const res3 = await fetch(url3 + '/' + res2.id)
        .then(response => response.json());
    ...
}
fetchData();
```

### オプショナルチェイニング演算子?.

nullやundefinedのものに `.`でプロパティ呼び出しをするとエラーになるので、nullでないことを確認しないとオブジェクトチェーンは呼び出せませんでしたが、nullやundefinedの場合はundefinedで終了してくれるオプショナルチェイニング演算子 `?.` が追加されました。

```js
const obj = {
    dog: {
        name: 'pochi',
    },
};

obj.dog.name // 'pochi'
obj.cat.name // catがundefinedなのでerror

// 今まではobj.catが存在することをチェックする必要があった
console.log(obj.cat && obj.cat.name); // あれば　obj.cat.name なければ obj.cat

// オプショナルチェイニングを使うと同等のことを短くかける
obj.cat?.name // undefined
```

[オプショナルチェーン \(?\.\) \- JavaScript \| MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Optional_chaining)

### Null合体演算子??

今まで値がfalsy(false扱いの値)の場合は、右辺の値を返すというのは `||` で出来ていた
この場合0や''空文字などもfalsyなため、右辺が表示された。
Null合体演算子の場合、nullish(nullとundefined)の場合のみ右辺が表示される違いがある。

```js
console.log(val || val2); // valがfalsyならval2が表示される

console.log(val ?? val2); // valがnullishならval2が表示される
```

## まとめ

どうでしょう・・・知らないトピックは一つでもあったでしょうか？

普段よく触っていても、調べ始めると案外なあなあで対処していたことがあると気付かされました（今回もいくつか初めて知ったものがあります）

知らなくても結構対処できますが、知っていれば対応に役立つことも多いと思うので、ぜひ色々とサーフィンしてみてください！