最新JavaScript機能の実践的活用ガイド

トップレベルawaitの非同期処理最適化

非同期初期化の簡素化

従来のコールバック地獄を解消する非同期処理の新アプローチ:

// 複雑なネスト構造
初期化処理(設定 => {
  データ取得(設定.id, 結果 => {
    レンダリング(設定, 結果);
  });
});

async/awaitによる線形化処理:

const プロファイル読み込み = async () => {
  const 設定 = await 初期化処理();
  const 結果 = await データ取得(設定.id);
  レンダリング(設定, 結果);
};

モジュール初期化の具体例

// 設定モジュールの非同期初期化
export const 環境設定 = await fetch('/config')
  .then(res => res.json())
  .catch(() => デフォルト設定());

// 他モジュールでの直接利用
import { 環境設定 } from './env.js';
console.log(環境設定.apiEndpoint);
// ブラウザ機能検出に基づくダイナミックインポート
const 旧式対応必要 = await ブラウザ検証();

if (旧式対応必要) {
  await import('./polyfill-loader.js');
}

Object.hasOwnによる安全なプロパティ検証

プロパティ検査手法の比較

class 輸送手段 {
  constructor(モデル) {
    this.model = モデル;
  }
}

class 自動車 extends 輸送手段 {
  constructor(モデル, 排気量) {
    super(モデル);
    this.engine = 排気量;
  }
}

自動車.prototype.種別 = '乗用車';

const myCar = new 自動車('Camry', '2.5L');

// 1. in演算子(プロトタイプチェーンを検索)
console.log('model' in myCar); // true
console.log('種別' in myCar);  // true

// 2. Object.hasOwn(自身のプロパティのみ検証)
console.log(Object.hasOwn(myCar, 'model')); // true
console.log(Object.hasOwn(myCar, '種別'));  // false

特殊ケースへの対応

// プロトタイプなしオブジェクトの処理
const 無プロトオブジェクト = Object.create(null);
無プロトオブジェクト.値 = 'データ';

// 旧メソッドではエラー
console.log(Object.hasOwn(無プロトオブジェクト, '値')); // true

// 悪意のあるhasOwnPropertyオーバーライド
const 悪意あるオブジェクト = {
  データ: '重要情報',
  hasOwnProperty: () => true
};

console.log(Object.hasOwn(悪意あるオブジェクト, 'データ')); // true

配列.at()メソッドによる直感的インデックス操作

従来のインデックス操作の課題

const 時間帯 = ['UTC', 'JST', 'EST', 'PST', 'CET'];

// 末尾要素取得の複雑さ
const 最終要素 = 時間帯[時間帯.length - 1]; 

// 負のインデックスの手動計算
function 取得(配列, 位置) {
  return 位置 < 0 ? 配列[配列.length + 位置] : 配列[位置];
}

.at()メソッドの実用例

const 通貨 = ['USD', 'EUR', 'JPY', 'GBP', 'CNY'];

// 負のインデックスによる末尾からの参照
console.log(通貨.at(-1)); // 'CNY'
console.log(通貨.at(-3)); // 'JPY'

// セーフティな境界値処理
console.log(通貨.at(100));  // undefined
console.log(通貨.at(-100)); // undefined
// 時間周期ユーティリティの実装
class 時間サイクル {
  constructor(要素) {
    this.elements = 要素;
  }
  
  取得(インデックス) {
    return this.elements.at(インデックス % this.elements.length);
  }
}

const 曜日 = new 時間サイクル(['月', '火', '水', '木', '金']);
console.log(曜日.取得(7));  // '火'
console.log(曜日.取得(-1)); // '金'

Error Causeによるエラー連鎖管理

エラー原因の追跡実装

class システムエラー extends Error {
  constructor(メッセージ, { 原因, コード } = {}) {
    super(メッセージ);
    this.name = 'システムエラー';
    this.cause = 原因;
    this.code = コード;
  }
}

async function 支払い処理(金額) {
  try {
    await 与信確認(金額);
    return await 決済実行(金額);
  } catch (エラー) {
    throw new システムエラー(
      `支払い処理失敗: ${金額}円`,
      { 原因: エラー, コード: 'PAYMENT_FAILED' }
    );
  }
}

// エラー連鎖の確認
try {
  await 支払い処理(5000);
} catch (エラー) {
  console.error('主要エラー:', エラー.message);
  console.error('根本原因:', エラー.cause.message);
}

プライベートフィールドによる真のカプセル化

クラスの実装例

class 図書管理システム {
  #蔵書 = new Map();
  #貸出履歴 = new Map();
  #稼働状態 = false;

  constructor() {
    this.#初期化();
  }

  async #初期化() {
    const 本データ = await this.#データ取得();
    本データ.forEach(本 => {
      this.#蔵書.set(本.id, 本);
    });
    this.#稼働状態 = true;
  }

  #ログ記録(操作, 書籍ID) {
    const 履歴 = this.#貸出履歴.get(書籍ID) || [];
    履歴.push({ 操作, 時刻: new Date() });
    this.#貸出履歴.set(書籍ID, 履歴);
  }

  貸出(書籍ID) {
    if (!this.#稼働状態) throw new Error('システム未起動');
    if (!this.#蔵書.has(書籍ID)) throw new Error('書籍不存在');
    
    this.#ログ記録('貸出', 書籍ID);
    return this.#蔵書.get(書籍ID);
  }

  // プライベートプロパティへの直接アクセス不可
  // this.#蔵書 = new Map(); // エラー
}

タグ: async-await オブジェクト操作 エラーハンドリング カプセル化 配列操作

6月3日 17:06 投稿