43

Object.freeze誰かがJavaScriptでいつ使用する必要があるのか​​は私にはわかりません。MDNとMSDNは、有用な場合の実際の例を示していません。実行時にそのようなオブジェクトを変更しようとすると、クラッシュが発生することがわかりました。問題は、むしろ、このクラッシュにいつ感謝するかということです。

私にとって不変性は、タイプチェッカーによって保証されるはずの設計時の制約です。

では、動的型付け言語でランタイムがクラッシュすることには、違反をこれまで以上に後で検出する以外に、何か意味がありますか?

4

12 に答える 12

19

このObject.freeze関数は次のことを行います。

  • オブジェクトを拡張不可能にし、新しいプロパティを追加できないようにします。
  • オブジェクトのすべてのプロパティについて、構成可能な属性をfalseに設定します。--configurationableがfalseの場合、プロパティ属性を変更したり、プロパティを削除したりすることはできません。
  • オブジェクトのすべてのデータプロパティの書き込み可能属性をfalseに設定します。writableがfalseの場合、データプロパティ値は変更できません。

それはの部分ですが、なぜ誰かがこれをするのでしょうか?

オブジェクト指向のパラダイムでは、既存のAPIに、現在のコンテキスト外で拡張、変更、または再利用することを目的としていない特定の要素が含まれているという概念が存在します。さまざまな言語のfinalキーワードは、これに最も適した例えです。コンパイルされていないために簡単に変更できる言語でも、PHP、この場合はJavaScriptが存在します。

于 2013-02-09T20:38:42.657 に答える
14

これは、論理的に不変のデータ構造を表すオブジェクトがある場合、特に次の場合に使用できます。

  • オブジェクトのプロパティを変更したり、その「ダック タイプ」を変更したりすると、アプリケーションの他の場所で不適切な動作が発生する可能性があります
  • オブジェクトが変更可能な型に似ているか、または変更可能に見える場合、未定義の動作を取得するのではなく、変更を試みた場合にプログラマーに警告する必要があります。

API 作成者として、これはまさにあなたが望む動作かもしれません。たとえば、参照によって API のユーザーに提供するが、さまざまな目的で内部的に使用する正規のサーバー レスポンスを表す内部キャッシュ構造がある場合があります。ユーザーはこの構造を参照できますが、これを変更すると、API が未定義の動作をする可能性があります。この場合、ユーザーが変更を試みた場合に例外をスローする必要があります。

于 2013-02-09T20:43:26.180 に答える
11

私のnodejsサーバー環境では、「use strict」を使用するのと同じ理由でfreezeを使用しています。拡張または変更したくないオブジェクトがある場合は、それをフリーズします。フリーズしたオブジェクトを何かが拡張または変更しようとした場合、アプリでエラーをスローするようにします。

私にとって、これは一貫性があり、品質が高く、より安全なコードに関係しています。

また、 Chrome では、凍結されたオブジェクトを操作する際のパフォーマンスが大幅に向上しています。

編集:私の最近のプロジェクトでは、政府機関との間で暗号化されたデータを送受信しています。多くの構成値があります。これらの値に凍結オブジェクトを使用しています。これらの値を変更すると、重大な副作用が生じる可能性があります。さらに、以前にリンクしたように、Chrome は凍結されたオブジェクトでパフォーマンス上の利点を示しています。nodejs も同様であると思います。

簡単にするために、例を次に示します。

var US_COIN_VALUE = {
        QUARTER: 25,
        DIME: 10,
        NICKEL: 5,
        PENNY: 1
    };

return Object.freeze( US_COIN_VALUE );

この例の値を変更する理由はありません。そして、速度の最適化のメリットを享受してください。

于 2015-08-09T16:21:16.667 に答える
7

Object.freeze()主に関数型プログラミング(不変性)で使用

不変性は、関数型プログラミングの中心的な概念です。不変性がないと、プログラム内のデータ フローに損失が生じるからです。状態の履歴は破棄され、奇妙なバグがソフトウェアに忍び寄る可能性があります。

constJavaScript では、 を不変性と混同しないことが重要です。const作成後に再割り当てできない変数名バインディングを作成します。const不変オブジェクトを作成しません。バインディングが参照するオブジェクトを変更することはできませんが、オブジェクトのプロパティを変更することはできます。つまり、で作成されたバインディングconstは変更可能であり、不変ではありません。

不変オブジェクトはまったく変更できません。オブジェクトを深く凍結することで、値を真に不変にすることができます。JavaScript には、オブジェクトを 1 レベルの深さでフリーズするメソッドがあります。

const a = Object.freeze({
 foo: 'Hello',
 bar: 'world',
 baz: '!'
});
于 2017-09-12T07:26:34.523 に答える
2

JS でライブラリ/フレームワークを作成していて、一部の開発者が「内部」またはパブリック プロパティを再割り当てして動的言語の作成を中断させたくない場合。これは、不変性の最も明白な使用例です。

于 2016-05-23T09:01:06.600 に答える
2

これは古い質問ですが、フリーズが役立つ良いケースがあると思います。今日はこの問題がありました。


問題

class Node {
    constructor() {
        this._children = [];
        this._parent = undefined;
    }

    get children() { return this._children; }
    get parent() { return this._parent; }

    set parent(newParent) {
        // 1. if _parent is not undefined, remove this node from _parent's children
        // 2. set _parent to newParent
        // 3. if newParent is not undefined, add this node to newParent's children
    }

    addChild(node) { node.parent = this; }
    removeChild(node) { node.parent === this && (node.parent = undefined); }
    ...
}

ご覧のとおり、親を変更すると、これらのノード間の接続が自動的に処理され、子と親の同期が維持されます。ただし、ここには 1 つの問題があります。

let newNode = new Node();
myNode.children.push(newNode);

現在、 にmyNodeは がありnewNodeますchildrenが、 にはnewNodeありません。だからあなたはそれを壊しただけです。myNodeparent

(オフトピック) どうして子供たちをさらけ出しているのですか?

そうです、countChildren()、getChild(index)、getChildrenIterator() (ジェネレーターを返す)、findChildIndex(node) など、たくさんのメソッドを作成することもできます。すべての JavaScript プログラマーが既に知っているインターフェイスを提供する配列?

  1. それにアクセスしlengthて、子の数を確認できます。
  2. children[i]インデックス (つまり)で子にアクセスできます。
  3. for .. of;を使用して反復処理できます。
  4. また、Array が提供するその他の優れたメソッドを使用することもできます。

注: 配列のコピーを返すことは論外です! 線形時間がかかり、元の配列への更新はコピーに反映されません!


ソリューション

    get children() { return Object.freeze(Object.create(this._children)); }

    // OR, if you deeply care about performance:
    get children() {
        return this._PUBLIC_children === undefined
            ? (this._PUBLIC_children = Object.freeze(Object.create(this._children)))
            : this._PUBLIC_children;
    }

終わり!

  1. Object.create: から継承するオブジェクトを作成しますthis._children(つまりthis._children、その として持っています__proto__)。これだけで、ほぼすべての問題が解決します。
    • シンプルで速い(一定時間)
    • Array インターフェイスによって提供されるものは何でも使用できます
    • 返されたオブジェクトを変更しても、元のオブジェクトは変更されません!
  2. Object.freeze: ただし、返されたオブジェクトを変更できるが、変更が元の配列に影響しないという事実は、クラスのユーザーを非常に混乱させます! だから、私たちはそれを凍らせるだけです。彼がそれを変更しようとすると、例外がスローされ (strict モードを想定)、変更できないこと (およびその理由) を認識します。myFrozenObject[x] = y厳密モードでない場合に例外がスローされないのは悲しいことですがmyFrozenObject、とにかく変更されていないため、それほど奇妙ではありません。

もちろん、プログラマーは にアクセスすることでそれをバイパスでき__proto__ます。

someNode.children.__proto__.push(new Node());

しかし、この場合、彼らは実際に自分が何をしているのかを知っており、そうする正当な理由があると思います.

重要: これはオブジェクトに対してはうまく機能しないことに注意してください: for .. in で hasOwnProperty を使用すると、常に false が返されます。


更新:プロキシを使用してオブジェクトの同じ問題を解決する

補足: Array の代わりにオブジェクトがある場合でも、Proxy を使用してこの問題を解決できます。実際、これはあらゆる種類の要素で機能する一般的なソリューションですが、パフォーマンスの問題があるため、(回避できる場合は) お勧めしません。

    get myObject() { return Object.freeze(new Proxy(this._myObject, {})); }

これは、変更できないオブジェクトを返しますが、そのすべての読み取り専用機能を保持します。本当に必要な場合はObject.freeze、必要なトラップ (set、deleteProperty など) を Proxy にドロップして実装できますが、これには余分な労力がかかります。そのため、Object.freezeはプロキシで便利です。

于 2017-01-13T03:19:52.167 に答える
1

の唯一の実用的な使用Object.freezeは、開発中です。製品コードの場合、オブジェクトを凍結/封印するメリットはまったくありません。

愚かなタイプミス

開発中にこの非常に一般的な問題を把握するのに役立ちます。

if (myObject.someProp = 5) {
    doSomething();
}

myObject厳密モードでは、フリーズするとエラーがスローされます。

コーディング プロトコル/制限の適用

また、チーム内で特定のプロトコルを強制するのにも役立ちます。特に、他のメンバーと同じコーディング スタイルを持っていない可能性のある新しいメンバーに対しては有効です。

多くの Java 関係者は、オブジェクトに多くのメソッドを追加して、JS をより親しみやすくすることを好みます。オブジェクトをフリーズすると、それができなくなります。

于 2015-07-12T16:48:26.837 に答える
0

Object.freeze が非常に役立ついくつかの場所を思いつくことができます。

を使用できる最初の実世界の実装freezeブラウザの内容と一致するようにサーバーの「状態」を必要とするアプリケーションを開発する場合です。たとえば、関数呼び出しにアクセス許可のレベルを追加する必要があるとします。アプリケーションで作業している場合、開発者が気付かないうちにアクセス許可の設定を簡単に変更または上書きできる場所がある場合があります (特に、オブジェクトが参照によって渡されている場合)。ただし、パーミッションは概して変更できず、変更されたときにエラーが発生することが推奨されます。したがって、この場合、アクセス許可オブジェクトが凍結される可能性があるため、開発者が誤ってアクセス許可を誤って「設定」することを制限できます。ログイン名やメールアドレスなど、ユーザーに似たデータについても同じことが言えます。これらは、誤って、または悪意を持って悪いコードで壊れる可能性があります。

もう 1 つの典型的なソリューションは、ゲーム ループ コードです。ゲームの状態がサーバーと同期している状態を維持するためにフリーズしたいゲームの状態の設定はたくさんあります。

Object.freeze は、オブジェクトを定数として作成する方法と考えてください。変数定数が必要な場合はいつでも、同様の理由でフリーズを伴うオブジェクト定数を使用できます。

また、関数やデータの受け渡しを通じて不変オブジェクトを渡し、セッターで元のオブジェクトの更新のみを許可したい場合もあります。これは、「ゲッター」のオブジェクトを複製および凍結し、「セッター」でオリジナルのみを更新することで実行できます。

これらのうち、有効でないものはありますか? 動的変数がないため、凍結されたオブジェクトの方がパフォーマンスが向上する可能性があるとも言えますが、その証拠はまだ見たことがありません。

于 2015-02-18T21:55:40.177 に答える
0

インタラクティブなツールを使用しているときに、これが役立つことがわかりました。それよりも:

if ( ! obj.isFrozen() ) {
    obj.x = mouse[0];
    obj.y = mouse[1];
}

あなたは単に行うことができます:

obj.x = mouse[0];
obj.y = mouse[1];

プロパティは、オブジェクトが凍結されていない場合にのみ更新されます。

于 2013-02-09T20:43:32.867 に答える
0

これが役立つかどうかはわかりませんが、単純な列挙を作成するために使用しています。意図的にコードを壊そうとせずに、データのソースを変更できないようにしようとしたことを知ることで、データベース内のダフ データを取得しないようにすることができます。静的に型付けされた観点からは、コード構築に関する推論が可能になります。

于 2017-02-22T11:08:20.157 に答える