これは古い質問ですが、フリーズが役立つ良いケースがあると思います。今日はこの問題がありました。
問題
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
ありません。だからあなたはそれを壊しただけです。myNode
parent
(オフトピック) どうして子供たちをさらけ出しているのですか?
そうです、countChildren()、getChild(index)、getChildrenIterator() (ジェネレーターを返す)、findChildIndex(node) など、たくさんのメソッドを作成することもできます。すべての JavaScript プログラマーが既に知っているインターフェイスを提供する配列?
- それにアクセスし
length
て、子の数を確認できます。
children[i]
インデックス (つまり)で子にアクセスできます。
for .. of
;を使用して反復処理できます。
- また、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;
}
終わり!
Object.create
: から継承するオブジェクトを作成しますthis._children
(つまりthis._children
、その として持っています__proto__
)。これだけで、ほぼすべての問題が解決します。
- シンプルで速い(一定時間)
- Array インターフェイスによって提供されるものは何でも使用できます
- 返されたオブジェクトを変更しても、元のオブジェクトは変更されません!
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
はプロキシで便利です。