ホームページ >ウェブフロントエンド >jsチュートリアル >クリーンなコード: JavaScript の不変性、中心となる概念とツール

クリーンなコード: JavaScript の不変性、中心となる概念とツール

Susan Sarandon
Susan Sarandonオリジナル
2025-01-17 20:33:15527ブラウズ

Clean Code: JavaScript immutability, core concepts and tools

突然変異とは何ですか?

突然変異とは、既存の値を直接変更することを指します。 JavaScript では、オブジェクトと配列はデフォルトで変更 (変異) できます:

<code class="language-javascript">// 变异示例
const user = { name: 'Alice' };
user.name = 'Bob';           // 变异对象属性

const numbers = [1, 2, 3];
numbers.push(4);             // 变异数组
numbers[0] = 0;              // 变异数组元素</code>

これらの突然変異は、特に大規模なアプリケーションで、見つけにくいバグを引き起こす可能性があります。

なぜ突然変異は避けるべきなのでしょうか?

簡単な例を見てみましょう:

<code class="language-javascript">// 使用变异的代码
const cart = {
  items: [],
  total: 0
};

function addProduct(cart, product) {
  cart.items.push(product);
  cart.total += product.price;
}

// 使用示例
const myCart = cart;
addProduct(myCart, { id: 1, name: "Laptop", price: 999 });
// 直接更改 myCart
console.log(cart === myCart); // true,两个变量指向同一个对象</code>

突然変異の問題:

  1. 共有参照: コードの異なる部分は、他の部分が知らないうちに同じオブジェクトを変更できます。
  2. 副作用: 変更は、同じオブジェクトを使用する他の関数に影響します。
  3. デバッグが難しい: コードのどの部分がオブジェクトを変更したかを追跡できません。
  4. 複雑なテスト: 突然変異により、単体テストの作成が困難になります。

ソリューション: 不変プログラミング

不変メソッドは、変更ごとにオブジェクトの新しいコピーを作成します。

<code class="language-javascript">// 不变性代码
function addProduct(cart, product) {
  // 创建一个新对象,而不更改原始对象
  return {
    items: [...cart.items, product],
    total: cart.total + product.price
  };
}

// 使用示例
const initialCart = { items: [], total: 0 };
const newCart = addProduct(initialCart, { id: 1, name: "Laptop", price: 999 });

console.log(initialCart); // { items: [], total: 0 }
console.log(newCart);     // { items: [{...}], total: 999 }
console.log(initialCart === newCart); // false,它们是不同的对象</code>

このアプローチの利点:

  1. 予測可能性: すべての関数は新しい状態を返し、隠れた副作用はありません。
  2. 変更追跡: すべての変更により、追跡可能な新しいオブジェクトが作成されます。
  3. テストが簡単: 関数は純粋な関数なので、テストが簡単です。
  4. デバッグの向上: 変更前後のステータスを比較できます。

最新の不変ツール

Immer: シンプルな書き方

Immer を使用すると、通常の JavaScript コードのように見えるコードを作成できますが、結果は変更されません。

<code class="language-javascript">import produce from 'immer';

const initialCart = {
  items: [],
  total: 0,
  customer: {
    name: 'Alice',
    preferences: {
      notifications: true
    }
  }
};

// 不使用 Immer(冗长的方法)
const updatedCart = {
  ...initialCart,
  items: [...initialCart.items, { id: 1, name: "Laptop", price: 999 }],
  total: initialCart.total + 999,
  customer: {
    ...initialCart.customer,
    preferences: {
      ...initialCart.customer.preferences,
      notifications: false
    }
  }
};

// 使用 Immer(简单的方法)
const updatedCartImmer = produce(initialCart, draft => {
  draft.items.push({ id: 1, name: "Laptop", price: 999 });
  draft.total += 999;
  draft.customer.preferences.notifications = false;
});</code>

Immer の利点:

  • 使い慣れた構文: 通常と同じようにコードを記述します。
  • 新しい API を学ぶ必要はありません。通常の JavaScript オブジェクトと配列を使用してください。
  • クイック: 変更された部分のみをコピーします。
  • 自動変更検出: 変更を追跡し、必要な場合にのみ新しい参照を作成します。
  • TypeScript とうまく連携し、すべての型情報が保持されます。

Immutable.js: 効率的なデータ構造

Immutable.js は、不変性を実現するために設計されたデータ構造を提供します。

<code class="language-javascript">import { Map, List } from 'immutable';

// 创建不变的数据结构
const cartState = Map({
  items: List([]),
  total: 0
});

// 添加一个项目
const newCart = cartState
  .updateIn(
    ['items'],
    items => items.push(Map({
      id: 1,
      name: "Laptop",
      price: 999
    }))
  )
  .update('total', total => total + 999);

// Immutable.js 方法始终返回新实例
console.log(cartState.getIn(['items']).size); // 0
console.log(newCart.getIn(['items']).size);   // 1

// 轻松比较
console.log(cartState.equals(newCart)); // false

// 转换回常规 JavaScript
const cartJS = newCart.toJS();</code>

Immutable.js の利点:

  • 不変のデータ構造を使用すると高速です。
  • データを操作するための豊富な API。
  • メモリ効率の高いデータ共有。
  • 簡単に等価性をチェックするには、equals() を使用します。
  • 偶発的な変更を防ぎます。

ESLint の不変構成

ESLint は、特定のルールを通じて不変のコーディング慣行を強制するのに役立ちます。

<code class="language-javascript">// .eslintrc.js
module.exports = {
  plugins: ['functional'],
  rules: {
    'functional/immutable-data': 'error',
    'functional/no-let': 'error',
    'functional/prefer-readonly-type': 'error'
  }
};</code>

これらのルールは次のことを行います:

  • 直接的なデータ変更を防止します。
  • let の代わりに const の使用を奨励します。
  • TypeScript では読み取り専用型を使用することをお勧めします。

TypeScript と不変性

TypeScript は、その型システムを通じて不変性を強制するのに役立ちます:

<code class="language-typescript">// 购物车的不变类型
type Product = {
  readonly id: number;
  readonly name: string;
  readonly price: number;
};

type Cart = {
  readonly items: ReadonlyArray<Product>;
  readonly total: number;
};

// TypeScript 防止变异
const cart: Cart = {
  items: [],
  total: 0
};

// 编译错误:items 是只读的
cart.items.push({ id: 1, name: "Laptop", price: 999 });

// 函数必须创建一个新的购物车
function addProduct(cart: Cart, product: Product): Cart {
  return {
    items: [...cart.items, product],
    total: cart.total + product.price
  };
}

// TypeScript 确保原始对象不会更改
const newCart = addProduct(cart, { id: 1, name: "Laptop", price: 999 });</code>

TypeScript の読み取り専用修飾子:

  • readonly: 属性の変更を禁止します。
  • ReadonlyArray: 配列の変更を禁止します。
  • Readonly: すべてのプロパティを読み取り専用にします。

これらの型はコンパイル時にチェックされ、エラーを早期に発見するのに役立ちます。

結論

不変性により、コードの予測性と保守性が向上します。慣れるには少し時間がかかりますが、信頼性と保守性の利点にはそれだけの価値があります。

以上がクリーンなコード: JavaScript の不変性、中心となる概念とツールの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。