web-dev-qa-db-ja.com

JavaScriptでオブジェクトをフリーズする必要があるのはなぜですか?

JavaScriptでObject.freezeをいつ使用する必要があるのか​​は私にはわかりません。 MDNとMSDNは、有用な場合の実際の例を示していません。実行時に変更できないという部分がありますクラッシュによって強制されます。問題は、クラッシュをいつ感謝するかということです。

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

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

34
Trident D'Gao

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

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

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

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

15
Ian Atkin

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

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

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

10
Plynx

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

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

また、 Chromeは、フリーズされたオブジェクトでの作業でパフォーマンスが大幅に向上することを示しています。

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

簡単にするために、例は次のようになります。

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

return Object.freeze( US_COIN_VALUE );

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

7
PigBoT

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

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

JavaScriptでは、constと不変性を混同しないことが重要です。 constは、作成後に再割り当てできない変数名バインディングを作成します。 constは不変のオブジェクトを作成しません。バインディングが参照するオブジェクトを変更することはできませんが、オブジェクトのプロパティを変更することはできます。つまり、constで作成されたバインディングは不変ではなく、変更可能です。不変オブジェクトはまったく変更できません。オブジェクトをディープフリーズすることで、値を真に不変にすることができます。 JavaScriptには、オブジェクトを1レベル深くフリーズするというメソッドがあります。

const a = Object.freeze({
  foo: 'Hello',
  bar: 'world',
  baz: '!'
});
5
Hien Huynh

これは古い質問ですが、フリーズが役立つかもしれない良いケースがあると思います。私は今日この問題を抱えていました。


問題

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);

現在、myNodenewNodeにはchildrenがありますが、newNodemyNodeにはparentがありません。だからあなたはそれを壊した。

(OFF-TOPIC)どうして子供たちをさらけ出すのですか?

はい、たくさんのメソッドを作成できます:countChildren()、getChild(index)、getChildrenIterator()(ジェネレーターを返す)、findChildIndex(node)など...しかし、それは単に返すよりも本当に良いアプローチですか?すべてのJavaScriptプログラマーがすでに知っているインターフェースを提供する配列?

  1. lengthにアクセスして、子の数を確認できます。
  2. インデックス(つまり、children[i])で子にアクセスできます。
  3. for .. of;を使用して反復できます。
  4. また、配列によって提供される他のいくつかのNiceメソッドを使用できます。

注:配列のコピーを返すことは問題外です!線形時間のコストがかかり、元の配列への更新はコピーに伝播されません!


解決策

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

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

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

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

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


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

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

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

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

1
Rafael Perrella

V8リリースv7.6 を使用すると、フリーズ/シールされたアレイのパフォーマンスが大幅に向上します。したがって、オブジェクトをフリーズする理由の1つは、コードのパフォーマンスが重要な場合です。

1
Philippe

オブジェクトをフリーズしたい場合の実際的な状況は何ですか?一例として、アプリケーションの起動時に、アプリの設定を含むオブジェクトを作成します。その構成オブジェクトをアプリケーションのさまざまなモジュールに渡すことができます。ただし、その設定オブジェクトが作成されたら、それが変更されないことを知りたいと思います。

1
Stuart Hallows

Object.freezeの唯一の実用的な使用法は、開発中です。プロダクションコードの場合、オブジェクトをフリーズ/シールするメリットはまったくありません。

愚かなタイプミス

これは、開発中にこの非常に一般的な問題を見つけるのに役立ちます。

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

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

コーディングプロトコルの実施/制限

また、特に他の全員と同じコーディングスタイルを持っていない可能性のある新しいメンバーの場合、チームで特定のプロトコルを適用するのにも役立ちます。

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

1
light

Object.freezeが非常に役立つ場所がいくつか考えられます。

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

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

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

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

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

0
geedew

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

0
Alexander Mills

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

0
WeNeedAnswers

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

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

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

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

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

0
Wex