ES6+の新機能完全解説:モダンJavaScriptをマスターしよう

ES6から最新のJavaScriptまで、現代的なJavaScript開発で知っておくべき機能を実例とともに解説します。

ES6+の新機能完全解説

ES6(ES2015)以降の JavaScript は、多くの便利な機能が追加されています。モダンな JavaScript 開発で必須の機能を実例とともに学びましょう。

変数宣言:let と const

const と let の使い分け

// ES5(古い書き方)
var name = "山田太郎";
var age = 25;

// ES6+(推奨)
const name = "山田太郎"; // 再代入しない値
let age = 25; // 再代入する可能性がある値

// constは再代入不可
const PI = 3.14159;
// PI = 3.14; // エラー

// オブジェクトや配列の中身は変更可能
const user = { name: "田中", age: 30 };
user.age = 31; // OK
user.city = "東京"; // OK

const numbers = [1, 2, 3];
numbers.push(4); // OK
// numbers = [5, 6, 7]; // エラー

ブロックスコープ

// letとconstはブロックスコープ
if (true) {
  let blockVariable = "ブロック内";
  const BLOCK_CONSTANT = "ブロック内定数";
  console.log(blockVariable); // OK
}
// console.log(blockVariable); // エラー

// forループでの使用
for (let i = 0; i < 5; i++) {
  setTimeout(() => {
    console.log(i); // 0, 1, 2, 3, 4 (期待通り)
  }, 100);
}

アロー関数

基本的な書き方

// 従来の関数
function add(a, b) {
  return a + b;
}

// アロー関数
const add = (a, b) => {
  return a + b;
};

// 短縮形(単一式の場合)
const add = (a, b) => a + b;

// 引数が1つの場合
const square = (x) => x * x;

// 引数がない場合
const greet = () => "こんにちは!";

// オブジェクトを返す場合
const createUser = (name, age) => ({ name, age });

this バインディングの違い

class Counter {
  constructor() {
    this.count = 0;
  }

  // 従来の関数:thisが変わる
  incrementOld() {
    setTimeout(function () {
      this.count++; // thisはundefined
      console.log(this.count); // NaN
    }, 1000);
  }

  // アロー関数:thisが保持される
  increment() {
    setTimeout(() => {
      this.count++; // thisはCounterインスタンス
      console.log(this.count); // 正しい値
    }, 1000);
  }
}

const counter = new Counter();
counter.increment(); // 1秒後に1が表示

テンプレートリテラル

文字列の埋め込み

const name = "佐藤";
const age = 28;

// 従来の書き方
const message1 = "こんにちは、" + name + "さん。年齢は" + age + "歳ですね。";

// テンプレートリテラル
const message2 = `こんにちは、${name}さん。年齢は${age}歳ですね。`;

// 式の埋め込み
const price = 1000;
const tax = 0.1;
const total = `合計:${price * (1 + tax)}`;

// 関数の呼び出し
const formatDate = (date) => date.toLocaleDateString("ja-JP");
const today = new Date();
const dateMessage = `今日は${formatDate(today)}です。`;

複数行文字列

// 従来の書き方
const html1 = "<div>" + "<h1>タイトル</h1>" + "<p>本文</p>" + "</div>";

// テンプレートリテラル
const html2 = `
  <div>
    <h1>タイトル</h1>
    <p>本文</p>
  </div>
`;

// タグ付きテンプレートリテラル
function highlight(strings, ...values) {
  return strings.reduce((result, string, i) => {
    const value = values[i] ? `<mark>${values[i]}</mark>` : "";
    return result + string + value;
  }, "");
}

const searchTerm = "JavaScript";
const text = highlight`プログラミング言語の${searchTerm}を学習中です。`;
// "プログラミング言語の<mark>JavaScript</mark>を学習中です。"

分割代入

配列の分割代入

const numbers = [1, 2, 3, 4, 5];

// 従来の書き方
const first = numbers[0];
const second = numbers[1];
const third = numbers[2];

// 分割代入
const [first, second, third] = numbers;

// スキップ
const [, , third, fourth] = numbers; // 1, 2をスキップ

// デフォルト値
const [a, b, c = 0] = [1, 2]; // c は 0

// 残りを取得
const [head, ...tail] = numbers; // head: 1, tail: [2, 3, 4, 5]

// 交換
let x = 1;
let y = 2;
[x, y] = [y, x]; // x: 2, y: 1

オブジェクトの分割代入

const user = {
  id: 1,
  name: "田中太郎",
  email: "tanaka@example.com",
  address: {
    city: "東京",
    country: "日本",
  },
};

// 基本的な分割代入
const { name, email } = user;

// 別名をつける
const { name: userName, email: userEmail } = user;

// デフォルト値
const { age = 25 } = user;

// ネストしたオブジェクト
const {
  address: { city },
} = user;

// 残りを取得
const { name, ...otherProps } = user;

// 関数の引数で使用
function greetUser({ name, age = 25 }) {
  return `こんにちは、${name}さん(${age}歳)`;
}

greetUser({ name: "山田", age: 30 });
greetUser({ name: "佐藤" }); // ageはデフォルト値25

スプレッド演算子とレスト演算子

配列でのスプレッド演算子

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];

// 配列の結合
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5, 6]

// 配列のコピー
const copy = [...arr1]; // [1, 2, 3]

// 要素の追加
const withExtra = [0, ...arr1, 4]; // [0, 1, 2, 3, 4]

// Math関数での使用
const numbers = [3, 1, 4, 1, 5];
const max = Math.max(...numbers); // 5
const min = Math.min(...numbers); // 1

// 文字列を文字の配列に
const chars = [..."Hello"]; // ['H', 'e', 'l', 'l', 'o']

オブジェクトでのスプレッド演算子

const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };

// オブジェクトの結合
const combined = { ...obj1, ...obj2 }; // { a: 1, b: 2, c: 3, d: 4 }

// オブジェクトのコピー
const copy = { ...obj1 }; // { a: 1, b: 2 }

// プロパティの上書き
const updated = { ...obj1, b: 10, e: 5 }; // { a: 1, b: 10, e: 5 }

// 条件付きプロパティ
const includeExtra = true;
const obj = {
  name: "太郎",
  ...(includeExtra && { extra: "データ" }),
};

レスト演算子

// 関数の引数
function sum(...numbers) {
  return numbers.reduce((total, num) => total + num, 0);
}

sum(1, 2, 3, 4, 5); // 15

// 一部の引数を取り出し
function greet(greeting, ...names) {
  return `${greeting} ${names.join(", ")}さん`;
}

greet("こんにちは", "田中", "佐藤", "山田");
// "こんにちは 田中, 佐藤, 山田さん"

デフォルト引数

// ES5の書き方
function greet(name, greeting) {
  greeting = greeting || "こんにちは";
  return greeting + "、" + name + "さん";
}

// ES6+のデフォルト引数
function greet(name, greeting = "こんにちは") {
  return `${greeting}${name}さん`;
}

// 複雑なデフォルト値
function createUser(name, options = {}) {
  const { age = 25, city = "東京", active = true } = options;

  return { name, age, city, active };
}

// 関数を呼び出すデフォルト値
function getTimestamp() {
  return new Date().toISOString();
}

function logMessage(message, timestamp = getTimestamp()) {
  console.log(`[${timestamp}] ${message}`);
}

配列メソッドの拡張

map, filter, reduce

const users = [
  { id: 1, name: "田中", age: 25, active: true },
  { id: 2, name: "佐藤", age: 30, active: false },
  { id: 3, name: "山田", age: 35, active: true },
];

// map: 変換
const names = users.map((user) => user.name);
// ['田中', '佐藤', '山田']

// filter: 絞り込み
const activeUsers = users.filter((user) => user.active);
// [{ id: 1, name: '田中', ... }, { id: 3, name: '山田', ... }]

// reduce: 集約
const totalAge = users.reduce((sum, user) => sum + user.age, 0);
// 90

// メソッドチェーン
const activeUserNames = users
  .filter((user) => user.active)
  .map((user) => user.name)
  .join(", ");
// "田中, 山田"

find, findIndex, some, every

const products = [
  { id: 1, name: "ノートPC", price: 80000, category: "electronics" },
  { id: 2, name: "デスク", price: 15000, category: "furniture" },
  { id: 3, name: "チェア", price: 25000, category: "furniture" },
];

// find: 条件に合う最初の要素
const expensiveProduct = products.find((p) => p.price > 20000);
// { id: 1, name: 'ノートPC', ... }

// findIndex: 条件に合う最初の要素のインデックス
const index = products.findIndex((p) => p.name === "デスク");
// 1

// some: 条件に合う要素が1つでもあるか
const hasElectronics = products.some((p) => p.category === "electronics");
// true

// every: すべての要素が条件に合うか
const allExpensive = products.every((p) => p.price > 10000);
// true

Promise と非同期処理

Promise 基本

// Promise作成
function fetchData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url) {
        resolve(`データ: ${url}`);
      } else {
        reject(new Error("URLが必要です"));
      }
    }, 1000);
  });
}

// Promise使用
fetchData("https://api.example.com")
  .then((data) => {
    console.log(data);
    return data.toUpperCase();
  })
  .then((upperData) => {
    console.log(upperData);
  })
  .catch((error) => {
    console.error("エラー:", error.message);
  })
  .finally(() => {
    console.log("処理完了");
  });

async/await

// async関数
async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }

    const user = await response.json();
    return user;
  } catch (error) {
    console.error("ユーザーデータの取得に失敗:", error);
    throw error;
  }
}

// 使用例
async function displayUser() {
  try {
    const user = await fetchUserData(1);
    console.log(user.name);
  } catch (error) {
    console.log("ユーザーを表示できません");
  }
}

// 並列処理
async function fetchMultipleUsers() {
  try {
    const [user1, user2, user3] = await Promise.all([
      fetchUserData(1),
      fetchUserData(2),
      fetchUserData(3),
    ]);

    return [user1, user2, user3];
  } catch (error) {
    console.error("複数ユーザーの取得に失敗:", error);
  }
}

クラス

基本的なクラス定義

class User {
  // コンストラクタ
  constructor(name, email) {
    this.name = name;
    this.email = email;
    this.createdAt = new Date();
  }

  // メソッド
  greet() {
    return `こんにちは、${this.name}さん`;
  }

  // ゲッター
  get displayName() {
    return this.name.toUpperCase();
  }

  // セッター
  set name(newName) {
    if (newName.length < 2) {
      throw new Error("名前は2文字以上である必要があります");
    }
    this._name = newName;
  }

  get name() {
    return this._name;
  }

  // 静的メソッド
  static validateEmail(email) {
    return email.includes("@");
  }
}

// 使用例
const user = new User("田中太郎", "tanaka@example.com");
console.log(user.greet()); // "こんにちは、田中太郎さん"
console.log(user.displayName); // "田中太郎"
console.log(User.validateEmail("test@example.com")); // true

継承

class Admin extends User {
  constructor(name, email, permissions) {
    super(name, email); // 親のコンストラクタ呼び出し
    this.permissions = permissions;
  }

  // メソッドのオーバーライド
  greet() {
    return `${super.greet()}(管理者)`;
  }

  // 新しいメソッド
  hasPermission(permission) {
    return this.permissions.includes(permission);
  }
}

const admin = new Admin("佐藤", "sato@example.com", [
  "read",
  "write",
  "delete",
]);
console.log(admin.greet()); // "こんにちは、佐藤さん(管理者)"
console.log(admin.hasPermission("write")); // true

モジュール

エクスポートとインポート

// utils.js
export const PI = 3.14159;

export function calculateCircleArea(radius) {
  return PI * radius * radius;
}

export class Calculator {
  add(a, b) {
    return a + b;
  }
}

// デフォルトエクスポート
export default function greet(name) {
  return `Hello, ${name}!`;
}

// main.js
import greet, { PI, calculateCircleArea, Calculator } from "./utils.js";

// または
import greet from "./utils.js";
import { PI, calculateCircleArea } from "./utils.js";

// 名前を変更してインポート
import { calculateCircleArea as calcArea } from "./utils.js";

// すべてをまとめてインポート
import * as Utils from "./utils.js";

console.log(Utils.PI);
console.log(Utils.calculateCircleArea(5));

最新の ES 機能

Optional Chaining (?.)

const user = {
  id: 1,
  name: "田中",
  address: {
    city: "東京",
    country: "日本",
  },
};

// 従来の書き方
const city = user && user.address && user.address.city;

// Optional Chaining
const city = user?.address?.city;
const phone = user?.contact?.phone; // undefined(エラーにならない)

// 配列での使用
const firstFriend = user?.friends?.[0];

// メソッド呼び出し
const result = user?.calculateSomething?.();

Nullish Coalescing (??)

// ||演算子の問題
const config = {
  timeout: 0,
  retries: 0,
  debug: false,
};

// ||だとfalsyな値が置き換えられてしまう
const timeout = config.timeout || 5000; // 5000(期待値は0)

// ??はnullとundefinedのみチェック
const timeout = config.timeout ?? 5000; // 0(期待値)
const retries = config.retries ?? 3; // 0(期待値)
const debug = config.debug ?? true; // false(期待値)

まとめ

ES6+の機能を活用することで、より読みやすく、保守しやすい JavaScript コードを書くことができます。これらの機能を段階的に習得し、モダンな JavaScript 開発をマスターしましょう。

継続的な学習と実践を通じて、効率的なコードを書けるようになりましょう。

最後まで読んでいただきありがとうございました!てばさん(@basabasa8770)でした!

この記事をシェア