前回の記事 では、非同期処理について、Promise、async/await構文、そしてAPI通信の方法を紹介しました。今回は最終回として、最新のJavaScript機能と今後の開発について、モダンな開発環境や新しい言語機能を中心に紹介します。
ESモジュール(import/export)
ES2015(ES6)で導入されたモジュールシステムは、コードの再利用性と保守性を向上させる重要な機能です。
基本的なexport/import
以下の例では、math.jsというモジュールファイルを作成し、そこから関数と定数をエクスポートし、main.js でそれらをインポートしています。
// math.js - モジュールのエクスポート
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// デフォルトエクスポート
export default function subtract(a, b) {
return a - b;
}
// main.js - モジュールのインポート
import subtract, { PI, add, multiply } from './math.js';
console.log(PI); // 3.14159
console.log(add(5, 3)); // 8
console.log(multiply(4, 6)); // 24
console.log(subtract(10, 3)); // 7
デフォルトエクスポートは、1つのモジュールにつき1つだけ可能です。デフォルトエクスポートにしたものは、インポート時に { } を使う必要がないという特徴があります。
モジュールの種類と使い分け
// 名前つきエクスポート(複数の機能をエクスポート)
export { PI, add, multiply };
// すべてをインポート
import * as MathUtils from './math.js';
console.log(MathUtils.add(1, 2));
// エイリアスを使用
import { add as addNumbers } from './math.js';
console.log(addNumbers(3, 4));
// 動的インポート(非同期)
async function loadModule() {
const mathModule = await import('./math.js');
console.log(mathModule.PI);
}
プライベートクラスフィールド
ES2022で導入されたプライベートクラスフィールドにより、真の意味でのプライベートメンバーが利用可能になりました。
プライベートフィールドの基本
class BankAccount {
// プライベートフィールド(#で始める)
#balance = 0;
#accountNumber;
constructor(accountNumber) {
this.#accountNumber = accountNumber;
}
// パブリックメソッド
deposit(amount) {
if (amount > 0) {
this.#balance += amount;
console.log(`${amount}円を入金しました`);
}
}
withdraw(amount) {
if (amount > 0 && amount <= this.#balance) {
this.#balance -= amount;
console.log(`${amount}円を出金しました`);
} else {
console.log('残高不足です');
}
}
getBalance() {
return this.#balance;
}
// プライベートメソッド
#validateTransaction(amount) {
return amount > 0 && amount <= 1000000;
}
}
const account = new BankAccount('123-456-789');
account.deposit(10000);
console.log(account.getBalance()); // 10000
// プライベートフィールドには外部からアクセスできない
// console.log(account.#balance); // SyntaxError
従来の書き方との比較
// 従来の書き方(命名規則でプライベートを表現)
class OldClass {
constructor() {
this._privateField = 'private'; // アンダースコアで表現
}
_privateMethod() {
return 'This should be private';
}
}
// 新しい書き方(真のプライベート)
class NewClass {
#privateField = 'private';
#privateMethod() {
return 'This is truly private';
}
getPrivateValue() {
return this.#privateField;
}
}
新しい組み込みメソッド
便利なメソッドをいくつか紹介します。
String.replaceAll()
マッチしたすべての部分を置換するメソッドです。従来は正規表現を使う必要がありましたが、replaceAll により簡単に実現できます。
const text = "Hello world, world is beautiful";
// 従来の方法
const oldWay = text.replace(/world/g, "JavaScript");
console.log(oldWay); // "Hello JavaScript, JavaScript is beautiful"
// 新しい方法(ES2021)
const newWay = text.replaceAll("world", "JavaScript");
console.log(newWay); // "Hello JavaScript, JavaScript is beautiful"
Array.flat() とArray.flatMap()
下記のサンプルのように、フラット化とは、多次元配列を1次元に変換することを言います。
// 配列のフラット化
const nestedArray = [1, [2, 3], [4, [5, 6]]];
console.log(nestedArray.flat()); // [1, 2, 3, 4, [5, 6]]
console.log(nestedArray.flat(2)); // [1, 2, 3, 4, 5, 6]
// flatMap() - map と flat を組み合わせ
const numbers = [1, 2, 3, 4];
const doubled = numbers.flatMap(x => [x, x * 2]);
console.log(doubled); // [1, 2, 2, 4, 3, 6, 4, 8]
Object.fromEntries()
キーと値のペアの配列やMapからオブジェクトを生成します。
// 配列からオブジェクトを作成
const entries = [['name', '太郎'], ['age', 25], ['city', '東京']];
const person = Object.fromEntries(entries);
console.log(person); // { name: '太郎', age: 25, city: '東京' }
// Map からオブジェクトへの変換
const map = new Map([
['language', 'JavaScript'],
['year', 2025]
]);
const obj = Object.fromEntries(map);
console.log(obj); // { language: 'JavaScript', year: 2025 }
Temporal API(日時操作の新しい方法)
執筆時点(2025年8月)でまだexperimental(実験的)な段階にあるTemporal APIは、Dateオブジェクトの問題点を解決する、日付・時刻データ用APIです。
Dateオブジェクトの問題点
// Dateオブジェクトの問題例
const date1 = new Date('2025-01-15'); // タイムゾーンに依存
const date2 = new Date(2025, 0, 15); // 月は0ベース(0=1月)
// 月の計算が直感的でない
console.log(new Date(2025, 0, 15).getMonth()); // 0(1月なのに0)
// タイムゾーンの扱いが複雑
console.log(new Date().getTimezoneOffset()); // UTCの時刻との分単位の時差
Temporal APIの利点(将来的な機能)
注意: 執筆時点(2025年8月)で、下記のコードはFirefoxでのみサポートされています。今後、他のブラウザでも実行可能になるものと思われます。
// より直感的な日付操作
const today = Temporal.PlainDate.from('2025-01-15');
console.log(today.month); // 1(1月は1)
// タイムゾーンを意識した時刻操作
const zonedDateTime = Temporal.ZonedDateTime.from({
year: 2025,
month: 1,
day: 15,
hour: 15,
minute: 30,
timeZone: 'Asia/Tokyo'
});
// 期間の計算
const startDate = Temporal.PlainDate.from('2025-01-01');
const endDate = Temporal.PlainDate.from('2025-12-31');
const duration = startDate.until(endDate);
console.log(duration.days); // 364
TypeScriptやDenoなどへの展開
TypeScriptの基本
TypeScriptの大きな特徴は、型注釈を追加できることです。以下は、その簡単な例です。
// types.ts - 型定義
interface User {
id: number;
name: string;
email: string;
isActive: boolean;
}
// 関数の型注釈
function createUser(name: string, email: string): User {
return {
id: Math.floor(Math.random() * 1000),
name,
email,
isActive: true
};
}
// 配列とジェネリクス
function filterActiveUsers(users: User[]): User[] {
return users.filter(user => user.isActive);
}
const users: User[] = [
createUser("太郎", "taro@example.com"),
createUser("花子", "hanako@example.com")
];
console.log(filterActiveUsers(users)); // アクティブなユーザーのみ表示
Denoの特徴
Denoは、TypeScriptをネイティブにサポートし、セキュリティを重視したランタイムです。以下は、Denoでの簡単なHTTPサーバーの例です。
// Deno環境でのHTTPサーバー例
import { serve } from "https://deno.land/std@0.201.0/http/server.ts";
const handler = (request: Request): Response => {
const body = `Your user-agent is:\n\n${request.headers.get("user-agent") ?? "Unknown"}`;
return new Response(body, { status: 200 });
};
// deno run --allow-net server.js
serve(handler);
復習
問題
1. ESモジュールを使用して、以下の要件を満たすコードを作成する
-
calculator.jsファイルに加算、減算、乗算、除算の関数を定義 -
main.jsでそれらの関数をインポートして使用 - デフォルトエクスポートとして計算履歴を管理するクラスを作成
2. プライベートクラスフィールドを使用してカウンタークラスを作成する
要件
- プライベートフィールド
#countを持つ -
increment(),decrement(),getCount()メソッドを提供 - 外部から
#countに直接アクセスできないことを確認
3. 以下のデータを変換するコードを作成する
const data = [
['name', '太郎'],
['skills', ['JavaScript', 'Python', 'Go']],
['experience', [
['JavaScript', 3],
['Python', 2]
]]
];
要件
-
Object.fromEntries()を使用してオブジェクトに変換 - スキル配列をフラット化
- 経験データもオブジェクトに変換
解答例
問題1
// calculator.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export function multiply(a, b) {
return a * b;
}
export function divide(a, b) {
if (b === 0) {
throw new Error('ゼロで割ることはできません');
}
return a / b;
}
export default class CalculatorHistory {
#history = [];
addCalculation(operation, operands, result) {
this.#history.push({
operation,
operands,
result,
timestamp: new Date()
});
}
getHistory() {
return [...this.#history];
}
clearHistory() {
this.#history = [];
}
}
// main.js
import CalculatorHistory, { add, subtract, multiply, divide } from './calculator.js';
const history = new CalculatorHistory();
const result1 = add(10, 5);
console.log('10 + 5 =', result1);
history.addCalculation('add', [10, 5], result1);
const result2 = multiply(4, 6);
console.log('4 * 6 =', result2);
history.addCalculation('multiply', [4, 6], result2);
console.log('計算履歴:', history.getHistory());
問題2
class Counter {
#count = 0;
increment() {
this.#count++;
console.log(`カウント: ${this.#count}`);
}
decrement() {
this.#count--;
console.log(`カウント: ${this.#count}`);
}
getCount() {
return this.#count;
}
// プライベートメソッドの例
#reset() {
this.#count = 0;
}
}
const counter = new Counter();
counter.increment(); // カウント: 1
counter.increment(); // カウント: 2
counter.decrement(); // カウント: 1
console.log('現在のカウント:', counter.getCount()); // 1
// プライベートフィールドにはアクセスできない
// console.log(counter.#count); // SyntaxError
問題3
const data = [
['name', '太郎'],
['skills', ['JavaScript', 'Python', 'Go']],
['experience', [
['JavaScript', 3],
['Python', 2]
]]
];
// Object.fromEntriesを使用してオブジェクトに変換
const person = Object.fromEntries(data);
console.log('基本オブジェクト:', person);
// スキル配列のフラット化
const flattenedSkills = person.skills.flat();
console.log('フラット化されたスキル:', flattenedSkills);
// 経験データをオブジェクトに変換
const experienceObject = Object.fromEntries(person.experience);
console.log('経験オブジェクト:', experienceObject);
// 最終的なデータ構造
const finalData = {
...person,
skills: flattenedSkills,
experience: experienceObject
};
console.log('最終データ:', finalData);
まとめ
本シリーズの最終回として、最新のJavaScript機能と開発環境について、主に下記の内容を紹介しました。
- モジュールシステム
- プライベートクラスフィールド
- 新しい組み込みメソッド
最後に補足事項です。
今後の学習ステップ
-
フレームワークの学習
- React, Vue.js
- Next.js, Nuxt.js
-
TypeScriptの習得
- 型安全性の向上
- 大規模開発への対応
- 開発効率の向上
-
バックエンド開発
- Node.js, Express.js
- データベース操作(RDB, NoSQL)
- REST API, GraphQLの構築
-
モバイル・デスクトップ開発
- React Native(モバイル)
- Electron(デスクトップ)
- Progressive Web Apps(PWA)
-
開発ツールとワークフロー
- Webpack, Viteなどのバンドラ
- Docker, CI/CDの活用
- テスト駆動開発(Jest, Vitest)
公式ドキュメントとリソース
-
MDN Web Docs
- JavaScript言語仕様の詳細
- ブラウザ互換性情報
- 実践的なサンプルコード
-
ECMAScript仕様書
- TC39のGitHubリポジトリ
- 新機能の提案状況
- Stage 0-4の進捗状況
-
Can I Use
- ブラウザサポート状況の確認
- 代替手段の情報
情報収集のコツ
- 公式ドキュメント で学ぶ
- TC39のproposal で新機能の動向をチェック
- GitHub でのオープンソースプロジェクトから学ぶ
- 技術ブログやカンファレンス で最新トレンドを把握
- 実際にコードを書いて試す
これで「新・JavaScript文法」シリーズは完了です。読んでいただき、ありがとうございました。