JavaScirpt/TypeScriptで最低限知っておきたいこと 随時更新

## これは何?

JavaScriptを使おうと思ってみたものの歴史が長いため，特殊な仕様だったり，よくわからない言葉がたくさんある気がする。
最近は生成AIでなんとなく動くものは作れてしまうが，もやもやが晴れないので一念発起して気になっていることをちゃんと調べてまとめてみた。

これからJavaScript，TypeScriptを学習しようとする方や，自分のようになんとなく動くものは作れるけどもやっとすることがある方の助けになれば良いなと思う。

### なぜ，今JS/TSを学ぶ気になったのか

- VSCodeの拡張のロジックはJS/TSで書かれるから
- (GitHub Actionsの拡張作りたいみたいなのもあったりするが)
- フレームワークの移り変わりは早いが

---

## ESModuleとCommonJS

フロントエンドでは`import`を使っているのにnode.js(バックエンド)では`require`が使われていて，同じ言語なのにライブラリを使用する構文に違いがあるのだ?と不思議な気持ちになったことはあるだろうか。

これは**ESModule**と**CommonJS**という二種類のモジュールシステムが存在しているためだ。
基本的に新しいESModuleを使う方が主流にはなってきているが，一部のライブラリがESModule(`import`)に対応していない場合にはCommonJS(`require`)を使うこともある。

これらの違いについて公式ドキュメントをもとに記載した。

### ESModule(`import`を使う方)

ECMScriptの第6版(2015)に追加された機能。

> Some of its major enhancements included modules, class declarations, lexical block scoping, iterators and generators, promises for asynchronous programming, destructuring patterns, and proper tail calls. The ECMAScript library of built-ins was expanded to support additional data abstractions including maps, sets, and arrays of binary numeric values as well as additional support for Unicode supplementary characters in strings and regular expressions. The built-ins were also made extensible via subclassing. The sixth edition provides the foundation for regular, incremental language and library enhancements. The sixth edition was adopted by the General Assembly of June 2015. [引用元](https://tc39.es/ecma262/#sec-intro)

:::note info
> ECMAScript（エクマスクリプト）は、Ecmaインターナショナルにおいて標準化されたJavaScriptの国際規格である。https://ja.wikipedia.org/wiki/ECMAScript
:::

- [Node.js ESM Documentation](https://nodejs.org/api/esm.html)のサンプルコードにあるように`import {}`を使ってライブラリを読み込み，`export {}`を使ってライブラリを外部からアクセス可能にする。

> ```addTwo.mjs
> // addTwo.mjs
> function addTwo(num) {
>  return num + 2;
> }
> export { addTwo };
> ```

> ```app.mjs
> // app.mjs
> import { addTwo } from './addTwo.mjs';
> console.log(addTwo(4)); // 6と出力
> ```

### CommonJS(`require`を使う方)

> CommonJS modules are the original way to package JavaScript code for Node.js. Node.js also supports the ECMAScript modules standard used by browsers and other JavaScript runtimes.
> [Node.js v23.9.0 documentation](https://nodejs.org/docs/latest/api/modules.html)より

CommonJSはNodo.jsで作成されたコードをパッケージ化する昔ながらの方法といったところでしょう。

同ドキュメントのサンプルコードを見ると，`require()`を使ってライブラリを読み込み，`export.要素名`で外部からアクセス可能にすることがわかります。

> ```foo.js
> const circle = require('./circle.js');
> console.log(`The area of a circle of radius 4 is ${circle.area(4)}`); 
> ```

> ```circle.js
> const { PI } = Math;
> exports.area = (r) => PI * r ** 2;
> exports.circumference = (r) => 2 * PI * r;
> ```


## 拡張子のあれこれ

### JavaScript

- .js: 基本的なJava Scriptの拡張子
- [.mjs](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Modules#%E4%BD%99%E8%AB%87_%E2%80%94_.mjs_%E3%81%A8_.js): どのファイルがモジュールで、どのファイルが通常の JavaScript であるかを明確にするために[V8のドキュメント](https://v8.dev/features/modules#mjs)で推奨されている拡張子。.mjsが使用されている場合にはESModuleが使われている。

  :::note warn
  .mjsを使うことでサーバが拡張子を正しく判断できなくなる可能性があることに注意する
    > ブラウザーでモジュールを正しく動作させるためには、サーバーが Content-Type ヘッダーで JavaScript の MIME タイプ、例えば text/javascript などを含めて提供していることを確認する必要があります。そうしないと、"The server responded with a non-JavaScript MIME type" のような厳格な MIME タイプチェックエラーが表示され、ブラウザーは JavaScript を実行しません。ほとんどのサーバーでは、.js ファイルにはすでに正しい MIME タイプが設定されていますが、.mjs ファイルにはまだ設定されていません。すでに .mjs ファイルを正しく提供しているサーバーには、GitHub Pages や Node.js の http-server などがあります。
  :::
- [.cjs](https://nodejs.org/api/modules.html#enabling): Node.jsでは.cjs拡張子を使っているとCommonJSとして扱われる。
- [.jsx](https://ja.react.dev/learn/writing-markup-with-jsx): JavaScriptの拡張であり、JavaScriptファイル内にHTMLのようなマークアップを書けるようにするもの

  ```jsx
  // 公式ドキュメントから引用
  export default function TodoList() {
    return (
      // This doesn't quite work!
      <h1>Hedy Lamarr's Todos</h1>
      <img 
        src="https://i.imgur.com/yXOvdOSs.jpg" 
        alt="Hedy Lamarr" 
        class="photo"
      >
      <ul>
        <li>Invent new traffic lights
        <li>Rehearse a movie scene
        <li>Improve the spectrum technology
      </ul>
    );
  }

  ```

### TypeScript

上記のTypeScriptバージョンなので割愛

- .ts
- .mts
- .cts
- .tsx

### JSON

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

```json
{
  "browsers": {
    "firefox": {
      "name": "Firefox",
      "pref_url": "about:config",
      "releases": {
        "1": {
          "release_date": "2004-11-09",
          "status": "retired",
          "engine": "Gecko",
          "engine_version": "1.7"
        }
      }
    }
  }
}
```
- 構造化データを表現するための標準のテキストベースの形式
- JSONのテキストは有効なJavaScriptの式になる ※全てのJavaScriptがJSONになるわけではない
- JavaScriptと文法は微妙に違う

:::note info
VSCodeの`settings.json`はVSCodeがJSONに独自の拡張を加えているらしいので，本来コメントは使えない。
[参考](https://www.cview.co.jp/cvcblog/2021.04.20.dymmfsxaihnbwoekcys_x)
:::

:::note info
最近，[MCP](https://www.anthropic.com/news/model-context-protocol)でAIモデルとデータソース間のデータのやり取りがJSONで行われていることもあり，非プログラマの方の間でも多少ホットなデータ保存形式なのではないだろうか。
:::

---

### 参考: node.jsでESModuleとCommonJSの優先順位の決まり方

https://nodejs.org/api/modules.html#enabling

1. .cjsを使っている→CommonJS，.mjsを使っている→ESModule
2. .jsの場合: package.jsonを確認する
    - package.jsonなし→CommonJS
    - package.jsonに`type`フィールドなし→CommonJS
    - package.jsonの`type`フィールドに`"commonjs"`が指定→CommonJS
    - package.jsonの`type`フィールドに`"module"`が指定→ESModule
3. .mjs，.cjs，.json，.node，.js以外の拡張子の場合
  - `import()`を使うならESModule
  - `require()`を使うならCommonJS

↓せっかくなのでフローチャートにしてみた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3718390/0c0de87b-a9c0-44ba-b82e-3fa8bf06dd5a.png)

---

## 変数の宣言の違い

### 未宣言の変数

未宣言の変数は暗黙的にグローバル変数になる。
[厳格モード](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Strict_mode)(strict)を使うことにより，誤ってグローバル変数が作成されてしまうことを防げる。

> 未宣言の変数への代入
> 厳格モードでは、偶発的にグローバル変数を作成できないようにします。厳格モードでない場合は、代入文で変数名の綴りを間違えるとグローバルオブジェクトに新しいプロパティが作成され、そしてそれは動作し続けます。厳格モードでは、代入文で偶発的にグローバル変数を作成せずにエラーが発生します

```js
function foo() {
  x = 1;
  console.log(x); // 1
}

foo();

console.log(x); // 1
```

### `var`

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/var

> `var`で宣言された変数のスコープは、その現在の実行コンテキストとそのクロージャであり、その中で宣言された関数、あるいは関数の外で宣言された変数の場合はグローバルになります。
> このため，ブロック内等のあらゆる場所で変数が上書きされる恐れがあります。

- グローバルフィールドで`var`を使って宣言するとグローバル変数になる。

```js
// この場合xはグローバル変数
var x = 1;

// ブロック内でグローバル変数を上書きしてみる
if (x === 1) {
  var x = 2;

  console.log(x); // 2
}

console.log(x); // 2に上書きされている
```

- 関数無いで`var`を使って宣言すると関数内でのみ有効な変数になる。

```js
// 関数内でvarを使う
function foo() {
  var x = 1;
  console.log(x); // 1
}

foo();

console.log(x); // ReferenceError: x is not defined 関数内でのみ有効なため
```

- 再代入可能
- 同名の変数がいても問題ない

```js
var x = 1;
var x = 2; // 再代入可能
console.log(x); // 2
```

### `let`

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/let

> let を使用することで、それが使用されたブロックの文または式にスコープを限定した変数を宣言することができます。

- ブロック内変数のため，スコープ外では使えない。
  ```js
  for (let i = 1; i <= 3; i++) {
    console.log(i);
  }
  console.log(i) // ReferenceError: i is not defined
  ```
  :::note info
  比較のため`var`で置き換えてみるとスコープ外でも変数が有効なのがわかる。
  ```js
  for (var i = 1; i <= 3; i++) {
    console.log(i);
  }
  console.log(i) // 4
  ```
  :::
- 同名変数の再宣言を禁止
  ```js
  let x = 1;
  let x = 2; // SyntaxError: redeclaration of let x
  ```

### `const`

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/const

letの定数バージョン

- ブロック内変数
- 上書き禁止
- 再宣言禁止

---

## 関数の書き方

### function宣言

おそらく，最も普通の書き方

```js
function sum(a, b) { 
  return Number(a) + Number(b)
}
result = sum(1, 2)
```

### Functionコンストラクタを使う(非推奨)

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

eval()のようにコンストラクタを呼び出すことで動的に関数を生成できるのが特徴
(現代であえて使うユースケースは思いつかないのでこんなのあるんだ〜くらいに思っておけばよさそう)

```js
const sum = new Function("a", "b", "return a + b");
result = sum(1, 2)
```

### 関数式

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/function

function宣言と違い，関数式は無名関数を生成する。

```js
const sum = function (a, b) {
  return (a + b)
};
result = sum(1, 2);
```

#### callback関数で使う

> コールバック関数は、引数として他の関数に渡され、外側の関数の中で呼び出されて、何らかのルーチンやアクションを完了させる関数のことです。
>　[Callback function](https://developer.mozilla.org/ja/docs/Glossary/Callback_function)

以下の例はボタン要素に対してクリックイベントが発生した場合に実行する処理を関数式で記述したもの。

```js
button.addEventListener("click", function (event) {
  console.log("button is clicked!");
});
```

function宣言を使う処理よりも簡潔にかける

```js
function handleClick(event) {
  console.log("button is clicked!");
}

// addEventListener に関数名を渡す
button.addEventListener("click", handleClick);
```
::: note info
後述するアロー関数式を使うのが現在では一般的だと思われる。
:::

#### 即時実行関数(IIFE)を使用する例

無名関数を作成してそのまま実行することが可能。
再利用しないことを前提にした書き方になる。

```js
(function () {
  console.log(1 + 2);
})();

// こっちでも良い
!function () {
  console.log(1 + 2);
}();
```

即時実行関数を使用することでグローバルスコープを汚さずに処理が実行できるのがメリットになる。

:::note info
昔は[JavaScriptのIIFEとモジュールパターン](https://fenomas.com/2013/06/javascript-iife-module-pattern/)のようにカプセル化のために使用されていたらしい。
現在はclassが使えるので特にこの使い方はしないと思うが参考までに
:::

### アロー関数式

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions

> アロー関数式は、従来の関数式の簡潔な代替構文ですが、意味的な違いや意図的な使用上の制限もあります。

基本的には関数式を使った書き方の省略形くらいに思っておけば良さそう

```js
const sum = (a, b) => {return a + b};
result = sum(1, 2)
```

関数式と同様に，callback関数で使用するのがメインの使い方になる

```js
button.addEventListener("click", (event) => {
  console.log("button is clicked!");
});
```

既に省略した書き方だが，省略記法がある。
e.g. 引数`event`を使わないなら省略可能で，処理が1行なら`{}`も省略可能。

```js
button.addEventListener("click", () => console.log("button is clicked!"));
```

---

## パッケージマネージャ

### npmとyarnの違い

https://npm-compare.com/ja-JP/npm,yarn

- npmがnode.jsの公式パッケージマネージャ
- yarnはMetaが作った代替品

yarnの方が高性能という認識で良さそう。

### npmとnpxの違い

https://docs.npmjs.com/cli/v8/commands/npx

> npx is a command-line utility that comes with npm 5.2 and higher, and is used to execute packages

> npx allows you to run packages without installing them globally, making it easier to use packages without cluttering your global package space.

npmと違ってパッケージをインストールせずに直接実行可能なため，npmと違ってglobaフィールドを汚さずに実行できるのが嬉しい。

---

## セミコロンを文末につけるべきか

> JavaScript では、命令は文 (statement) と呼ばれ、セミコロン (;) によって区切られています。
> 文が単独の行で書かれている場合、文の後にセミコロンは必要ではありません。しかし、行の中に複数の文が必要な場合は、セミコロンで区切る必要があります。
> ECMAScript も文末に自動的にセミコロンを挿入するルールがあります (ASI)。（詳しくは、JavaScript の 字句文法についての詳細なリファレンスを参照してください。）
> 必須ではないとしても、文の後に常にセミコロンを記述することをお勧めします。これによって、コード中にバグが発生する機会を減らすことができます。
> [文法とデータ構造](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Grammar_and_types)

https://qiita.com/igayamaguchi/items/ac48b7e12890351ee55a

自分は以下のように考えている。
- 自動挿入されるらしいが，想定外のバグが発生する可能性がありそうなのでフォーマッタでつける

  :::note info
  prettierの場合はデフォルトでセミコロンを入れるようになっているが明示的に指定するなら以下
  ```.prettierrc
  {
    "semi": true
  }
  ```
  :::

- LLMにコードを食わせる際には`;`があることで文の区切りがわかりやすいなどのメリットはありそう