新・JavaScript文法（2）：文字列／演算子／型変換の基礎

[前回の記事](https://qiita.com/azmnc/items/5ee92169a7bc4f6ba208) では、JavaScriptの実行環境の構築と基本的な文法について紹介しました。今回は、文字列の取り扱い方やデータ型、演算子について、より詳しく扱います。

## 文字列の基本

### 文字列の作成方法

JavaScriptでは、文字列を作成するために以下の3つの方法があります。

```javascript
// 1. シングルクォート
const str1 = 'Hello World';

// 2. ダブルクォート
const str2 = "Hello World";

// 3. テンプレートリテラル（バッククォート）
const str3 = `Hello World`;
```

シングルクォートとダブルクォートは機能的に同じですが、テンプレートリテラル（バッククォート）には特別な機能があります。

### テンプレートリテラルの機能と利点

テンプレートリテラルには、以下のような機能と利点があります。

```javascript
const name = "Alice";
const age = 25;

// 従来の文字列結合
const message1 = name + "さんは" + age + "歳です。";


// テンプレートリテラルを使用
const message2 = `${name}さんは${age}歳です。`;


console.log(message1); // "Aliceさんは25歳です。"
console.log(message2); // "Aliceさんは25歳です。"
```

#### テンプレートリテラルのメリット

- 変数の埋め込みが直感的
- 複数行の文字列が書きやすい
- 特殊文字のエスケープが少なくて済む

```javascript
// 複数行の文字列
const multiline = `
  こんにちは、
  これは複数行の
  文字列です。
`;

console.log(multiline);
// 出力：
//   こんにちは、
//   これは複数行の
//   文字列です。

// タグ付きテンプレート
function sanitizeHTML(strings, ...values) {
  return strings.reduce((result, str, i) => {
    const value = values[i - 1] || '';
    // 簡易的なHTMLエスケープ
    const escaped = String(value)
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
    return result + escaped + str;
  });
}

const userInput = '<script>alert("危険!")</script>';
const safeName = sanitizeHTML`<div>ようこそ、${userInput}さん</div>`;     // タグつきテンプレートの使用例

console.log(safeName);
// 出力：<div>ようこそ、&lt;script&gt;alert(&quot;危険!&quot;)&lt;/script&gt;さん</div>
```

### エスケープシーケンス

文字列内で特殊な文字を表現する場合は、エスケープシーケンスを使用します。

```javascript
// よく使うエスケープシーケンス
const str1 = "改行を\n入れます";
const str2 = "タブを\t入れます";
const str3 = "\"クォートを\"入れます";

// テンプレートリテラルなら、クォートのエスケープが不要
const str4 = `"クォート"を入れます`;
```

## 型と変数の基本

JavaScriptの型システムを理解することは、バグの少ない安全なコードを書くために重要です。第1回でも扱いましたが、ここで改めて取り上げます。

### プリミティブ型の種類

JavaScriptには以下の6つのプリミティブ型があります。

```javascript
// 1. 文字列型（String）
const str = "Hello";

// 2. 数値型（Number）
const num = 42;

// 3. 真偽値型（Boolean）
const bool = true;

// 4. undefined型
let undef;  // 値を代入していない変数。初期化されていない変数は暗黙的に undefined になる
console.log(undef); // undefined が出力される
console.log(typeof undef); // "undefined" が出力される

// 5. null型
const nullValue = null;  // 意図的に「値がない」ことを表現

// 6. Symbol型（ES2015で追加）：ユニークな値を生成
const sym = Symbol('description');
```

### 変数宣言の種類と使い分け

モダンなJavaScriptでは、`var` の代わりに `let` と `const` を使用します。

```javascript
// constの使用例（再代入不可）
const PI = 3.14159;
// PI = 3.14;  // エラー：constは再代入できない

// letの使用例（再代入可能）
let count = 0;
count = 1;  // OK

// varは使用を避ける（スコープの問題があるため）
var x = 10;  // 非推奨
```

### typeof演算子による型の確認

変数の型を調べるには `typeof` 演算子を使用します。

```javascript
const str = "Hello";
const num = 42;
const bool = true;
const arr = [1, 2, 3];
const obj = { name: "Alice" };

console.log(typeof str);   // "string"
console.log(typeof num);   // "number"
console.log(typeof bool);  // "boolean"
console.log(typeof arr);   // "object"
console.log(typeof obj);   // "object"

// nullの特殊なケース
console.log(typeof null);   // "object" （これはJavaScriptの有名な仕様バグ）
```

### NaNとNumber型のユーティリティ関数

JavaScriptでの数値の扱いには、いくつか注意の必要な点があります。

```javascript
// NaN（Not a Number）の特徴
console.log(typeof NaN);           // "number"
console.log(NaN === NaN);          // false（NaNは自分自身とも等しくない）
console.log(Number.isNaN(NaN));    // true（正しいNaNの判定方法）
console.log(isNaN("文字列"));      // true（グローバルのisNaN()は型変換を行う）
console.log(Number.isNaN("文字列")); // false（Number.isNaN()は型変換を行わない）：グローバルの isNaN() は文字列を数値に変換しようとするため true を返してしまう

// 数値判定のユーティリティ関数
console.log(Number.isFinite(42));     // true（有限数かどうか）
console.log(Number.isFinite(1/0));    // false（Infinityは有限数ではない）
console.log(Number.isInteger(42));    // true（整数かどうか）
console.log(Number.isInteger(42.5));  // false（小数は整数ではない）

// 安全な整数の範囲
console.log(Number.MAX_SAFE_INTEGER);  // 9007199254740991
console.log(Number.MIN_SAFE_INTEGER);  // -9007199254740991
console.log(Number.isSafeInteger(9007199254740991));  // true
console.log(Number.isSafeInteger(9007199254740992));  // false
```

## 演算子と型変換

### 比較演算子

JavaScriptには2種類の等価演算子があります。

```javascript
// 1. 厳密等価演算子（===）：型と値の両方を比較
console.log(5 === "5");    // false
console.log(5 === 5);      // true

// 2. 等価演算子（==）：暗黙的な型変換を行ってから比較
console.log(5 == "5");     // true（非推奨）

// 型変換の例
console.log(1 == true);    // true (数値型 1 に変換される)
console.log(0 == false);   // true (数値型 0 に変換される)
console.log("" == false);   // true (空文字は数値の0、つまり false に変換される)
console.log("1" == true); // true (文字列型 "1" は数値型 1 に変換され、true は数値型 1 に変換される)
console.log(null == undefined); // true （特殊なケース）
```

モダンなJavaScriptでは、バグの予防のために `===`（厳密等価演算子）の使用を推奨します。

```javascript
// 厳密等価演算子を使った安全な比較
function checkValue(value) {
  // 良い例：型を含めた厳密な比較
  if (value === null || value === undefined) {
    //  == を使うと null も undefined も true となるので注意
    //  if (value == null) と書くこともできるが、型を明確にするために === を使う方が良い
    // if(value == null)
    //    console.log('値は null or undefined');
    //
    // if(value == undefined)
    //    console.log('値は null or undefined');
    return "値が設定されていません";
  }
  
  // 良い例：数値の型を保証
  if (typeof value === "number") {
    return "数値です";
  }
  
  return "その他の型です";
}

console.log(checkValue(null));     // "値が設定されていません"
console.log(checkValue(42));       // "数値です"
console.log(checkValue("42"));     // "その他の型です"
```

### 型変換

JavaScriptでは、明示的な型変換と暗黙的な型変換があります。安全なコードを書くために、基本的には明示的な型変換を使用することを推奨します。

```javascript
// 明示的な型変換（推奨）
const numStr = "42";
const num = Number(numStr);       // 42（数値型）
const str = String(num);          // "42"（文字列型）
const bool = Boolean(num);        // true（真偽値型）

// 暗黙的な型変換（非推奨）
const result1 = "42" - 0;         // 42（数値型）
const result2 = 42 + "";         // "42"（文字列型）

// 数値への変換
console.log(Number("42"));        // 42
console.log(parseInt("42.5"));    // 42（整数部分のみ）
console.log(parseFloat("42.5"));  // 42.5（小数点も含める）
console.log(Number("abc"));       // NaN（変換できない場合）

// 真偽値への変換
console.log(Boolean(""));         // false（空文字列）
console.log(Boolean("abc"));      // true（空でない文字列）
console.log(Boolean(0));          // false
console.log(Boolean(1));          // true
console.log(Boolean([]));         // true（空配列）
console.log(Boolean(null));       // false
console.log(Boolean(undefined));  // false
```

## 実践的なコード例

```javascript
function validateAndParseNumber(input) {
  try {
    // 文字列型でない場合は文字列に変換
    const strValue = String(input).trim();
    
    // 空文字列チェック
    if (strValue === "") {
      return {
        isValid: false,
        value: null,
        error: "入力が空です"
      };
    }
    
    // 数値への変換
    const numValue = Number(strValue);
    
    // NaN、Infinity、-Infinityのチェック
    if (!Number.isFinite(numValue)) {
      return {
        isValid: false,
        value: null,
        error: "有効な数値ではありません"
      };
    }
    
    // 安全な整数範囲のチェック（必要な場合）
    if (!Number.isSafeInteger(numValue)) {
      return {
        isValid: false,
        value: null,
        error: "数値が大きすぎるか、整数ではありません"
      };
    }
    
    return {
      isValid: true,
      value: numValue,
      error: null
    };
  } catch (error) {
    // 予期せぬエラーの処理
    console.error("数値変換処理でエラーが発生:", error);
    return {
      isValid: false,
      value: null,
      error: "入力の処理中にエラーが発生しました"
    };
  }
}

// 使用例
console.log(validateAndParseNumber("42"));        // { isValid: true, value: 42, error: null }
console.log(validateAndParseNumber("abc"));       // { isValid: false, value: null, error: "有効な数値ではありません" }
console.log(validateAndParseNumber(""));          // { isValid: false, value: null, error: "入力が空です" }
console.log(validateAndParseNumber("1e+999"));    // { isValid: false, value: null, error: "有効な数値ではありません" }
console.log(validateAndParseNumber(42.5));        // { isValid: false, value: null, error: "数値が大きすぎるか、整数ではありません" }

// より実践的な数値処理の例
function calculateDiscount(price, discountRate) {
  try {
    // 引数の型チェックと変換
    const validPrice = validateAndParseNumber(price);
    if (!validPrice.isValid) {
      throw new Error(`価格が不正です: ${validPrice.error}`);
    }

    // 割引率の検証（0-100の範囲）
    const rate = Number(discountRate);
    if (!Number.isFinite(rate) || rate < 0 || rate > 100) {
      throw new Error("割引率は0から100の間で指定してください");
    }

    // 割引計算
    const discount = Math.floor(validPrice.value * (rate / 100));
    const finalPrice = validPrice.value - discount;

    return {
      originalPrice: validPrice.value,
      discountRate: rate,
      discount: discount,
      finalPrice: finalPrice
    };
  } catch (error) {
    return {
      error: error.message,
      originalPrice: null,
      discountRate: null,
      discount: null,
      finalPrice: null
    };
  }
}

// 使用例
console.log(calculateDiscount("1000", 20));
// {
//   originalPrice: 1000,
//   discountRate: 20,
//   discount: 200,
//   finalPrice: 800
// }

console.log(calculateDiscount("invalid", 20));
// {
//   error: "価格が不正です: 有効な数値ではありません",
//   originalPrice: null,
//   discountRate: null,
//   discount: null,
//   finalPrice: null
// }
```

## まとめ

この回では、JavaScriptの文字列操作、データ型、演算子、型変換について学びました。下記は重要なポイントです。

1. 文字列操作
   - テンプレートリテラル（`` ` ``）を使用すると、可読性の高いコードが書ける
   - タグ付きテンプレートを使用することで、文字列の安全な加工が可能
   - 複数行の文字列も簡単に記述可能
2. 変数宣言
   - `const`と`let`を使用し、`var`は避ける
   - できるだけ`const`を使用し、再代入が必要な場合のみ`let`を使用
3. 比較演算子
   - `===`（厳密等価演算子）を使用し、`==`は避ける
   - 型の安全性を確保することでバグを防ぐ
4. 型変換
   - 明示的な型変換（`Number()`、`String()`、`Boolean()`）を使用
   - 暗黙的な型変換は予期せぬバグの原因になるため、特別な理由がない限り避ける
   - `Number.isNaN()`や`Number.isFinite()`などの適切な判定関数を使用
5. エラー処理
   - 型変換やバリデーションでは適切なエラーハンドリングを実装
   - エラーメッセージは具体的で理解しやすいものを使用
   - try-catchを使用して予期せぬエラーに対応

これらの基礎を押さえることで、より安全で保守性の高いJavaScriptコードが書けるようになります。とくに、型の安全性を意識したコーディングは、バグの予防と早期発見に大きく貢献します。

## 次回予告

[次回](https://qiita.com/azmnc/items/dbfa749aba34a1ad8fa3) は「制御構文とループ」について紹介します。条件分岐（if文、switch文）や繰り返し処理（for文、while文）の基本と、モダンな書き方について扱います。