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)でした!