如何在 JavaScript 中保持双向 object 关系的一致性?
How can I maintain consistency in a 2-way object relationship in JavaScript?
如果我在 2 objects 之间有双向关系,例如 A 与 B 和 B 与 A 相关,我如何保持这种一致性,以便 2 objects 始终相互引用?
我很难用语言表达我非常简单的问题,所以这里有一个非常简单的例子。我从 Husband
和 Wife
:
开始
function Husband() { this.wife; }
function Wife() { this.husband; }
var harry = new Husband();
var wendy = new Wife();
harry.wife = wendy;
wendy.husband = harry;
从逻辑上讲,如果哈利的妻子是温蒂,那么温蒂的丈夫就是哈利。
我需要一种方法来保持这种关系的一致性。因此,我在 Husband
上创建了一个 setter 方法,并通过在 wife
变量前加上下划线来表示应该将其视为私有变量。
function Husband() {
this._wife;
this.setWife = function(wife) {
this._wife = wife;
wife.husband = this;
}
}
现在描述这种关系很简单并且鼓励一致性:
harry.setWife(wendy);
同样,如果有相反的选项就好了:
wendy.setHusband(harry);
为此,我在 Wife
上创建了一个 setHusband
方法,并根据需要调整 Husband
。
function Husband() {
this._wife;
this.setWife = function(wife) {
this._wife = wife;
// wife.husband = this; // <-- husband is now _husband (private)...
wife.setHusband(this); // <-- switching to a public method
}
}
function Wife() {
this._husband;
this.setHusband = function(husband) {
this._husband = husband;
husband._wife = this; // <-- Oops! _wife is private!
husband.setWife(this); // <-- Oops! this results in an infinite loop!
}
}
此时我运行陷入困境。我的新 setHusband
方法需要能够保持一致性,但是 wife
现在是 _wife
(私有),并且调用 setWife
会导致无限循环,因为它们相互往复。
我可以创建另一组方法,例如 reallyJustSetHusband
,但这看起来很傻。
我的难题并不是 JavaScript 所特有的,但我已经在问题中提到了它,以防需要特殊的方法。
在这 2 objects 之间实现一致性的最佳方法是什么?有什么我忽略的吗?
DOM
中的相似模式
在DOM中,如果调用parent.appendChild(child)
,则child.parentNode === parent
。它们从不矛盾。如果 parent 有一个 child,则 child 有相同的 parent。 nextSibling
等其他关系也保持一致。
像这样的事情是完成您所要求的一种方法:
function Husband() {
this.marry = function(wife) {
this.wife = wife;
wife.husband = this;
}
this.say = function() {
if (this.wife) {
console.log('Hi! I\'m married to a wife!');
} else {
console.log('Hi! I\'m single, no wife.');
}
}
}
function Wife() {
this.marry = function(husband) {
this.husband = husband;
husband.wife = this;
}
this.say = function() {
if (this.husband) {
console.log('Hi! I\'m married to a husband!');
} else {
console.log('Hi! I\'m single. No husband.');
}
}
}
var h = new Husband();
var w = new Wife();
h.marry(w);
w.say();
h.say();
一种简单的方法是只检查冗余值并提前中止:
function Husband() {
this._wife;
this.setWife = function(wife) {
if(this._wife == wife) return; // prevents recursion
this._wife = wife;
wife.setHusband(this);
}
}
function Wife() {
this._husband;
this.setHusband = function(husband) {
if(this._husband == husband) return; // prevents recursion
this._husband = husband;
husband.setWife(this);
}
}
您还可以将外部状态管理器(redux、sql 等)与更新事件一起使用,或者使用不需要 setter 的直接属性并注意保持数据更新。
._wife
不一定是私人的 - 妻子认识她的丈夫,应该被允许设置他的属性。也可以轻松跳出死循环:
class Wife() {
constructor() {
this._husband=null;
}
get husband() { return this._husband }
set husband(h) {
if (this._husband) this._husband._wife = null;
this._husband = h;
if (h && h.wife != this) h.wife = this;
}
}
如果您不想要这些内幕知识,您需要使用一个管理关系(可以创建、查询和断开)的中间体 class Marriage
。
class Marriage {
constructor(h, w) {
this.husband = h;
this.wife = w;
}
divorce() {
this.husband.marriage = null;
this.wife.marriage = null;
}
}
class Wife() {
constructor() {
this.marriage = null;
}
get husband() {
return this.marriage && this.marriage.husband;
}
set husband(h) {
if (this.marriage) this.marriage.divorce();
if (h.marriage) h.marriage.divorce();
this.marriage = h.marriage = new Marriage(h, this);
}
}
如果我在 2 objects 之间有双向关系,例如 A 与 B 和 B 与 A 相关,我如何保持这种一致性,以便 2 objects 始终相互引用?
我很难用语言表达我非常简单的问题,所以这里有一个非常简单的例子。我从 Husband
和 Wife
:
function Husband() { this.wife; }
function Wife() { this.husband; }
var harry = new Husband();
var wendy = new Wife();
harry.wife = wendy;
wendy.husband = harry;
从逻辑上讲,如果哈利的妻子是温蒂,那么温蒂的丈夫就是哈利。
我需要一种方法来保持这种关系的一致性。因此,我在 Husband
上创建了一个 setter 方法,并通过在 wife
变量前加上下划线来表示应该将其视为私有变量。
function Husband() {
this._wife;
this.setWife = function(wife) {
this._wife = wife;
wife.husband = this;
}
}
现在描述这种关系很简单并且鼓励一致性:
harry.setWife(wendy);
同样,如果有相反的选项就好了:
wendy.setHusband(harry);
为此,我在 Wife
上创建了一个 setHusband
方法,并根据需要调整 Husband
。
function Husband() {
this._wife;
this.setWife = function(wife) {
this._wife = wife;
// wife.husband = this; // <-- husband is now _husband (private)...
wife.setHusband(this); // <-- switching to a public method
}
}
function Wife() {
this._husband;
this.setHusband = function(husband) {
this._husband = husband;
husband._wife = this; // <-- Oops! _wife is private!
husband.setWife(this); // <-- Oops! this results in an infinite loop!
}
}
此时我运行陷入困境。我的新 setHusband
方法需要能够保持一致性,但是 wife
现在是 _wife
(私有),并且调用 setWife
会导致无限循环,因为它们相互往复。
我可以创建另一组方法,例如 reallyJustSetHusband
,但这看起来很傻。
我的难题并不是 JavaScript 所特有的,但我已经在问题中提到了它,以防需要特殊的方法。
在这 2 objects 之间实现一致性的最佳方法是什么?有什么我忽略的吗?
DOM
中的相似模式在DOM中,如果调用parent.appendChild(child)
,则child.parentNode === parent
。它们从不矛盾。如果 parent 有一个 child,则 child 有相同的 parent。 nextSibling
等其他关系也保持一致。
像这样的事情是完成您所要求的一种方法:
function Husband() {
this.marry = function(wife) {
this.wife = wife;
wife.husband = this;
}
this.say = function() {
if (this.wife) {
console.log('Hi! I\'m married to a wife!');
} else {
console.log('Hi! I\'m single, no wife.');
}
}
}
function Wife() {
this.marry = function(husband) {
this.husband = husband;
husband.wife = this;
}
this.say = function() {
if (this.husband) {
console.log('Hi! I\'m married to a husband!');
} else {
console.log('Hi! I\'m single. No husband.');
}
}
}
var h = new Husband();
var w = new Wife();
h.marry(w);
w.say();
h.say();
一种简单的方法是只检查冗余值并提前中止:
function Husband() {
this._wife;
this.setWife = function(wife) {
if(this._wife == wife) return; // prevents recursion
this._wife = wife;
wife.setHusband(this);
}
}
function Wife() {
this._husband;
this.setHusband = function(husband) {
if(this._husband == husband) return; // prevents recursion
this._husband = husband;
husband.setWife(this);
}
}
您还可以将外部状态管理器(redux、sql 等)与更新事件一起使用,或者使用不需要 setter 的直接属性并注意保持数据更新。
._wife
不一定是私人的 - 妻子认识她的丈夫,应该被允许设置他的属性。也可以轻松跳出死循环:
class Wife() {
constructor() {
this._husband=null;
}
get husband() { return this._husband }
set husband(h) {
if (this._husband) this._husband._wife = null;
this._husband = h;
if (h && h.wife != this) h.wife = this;
}
}
如果您不想要这些内幕知识,您需要使用一个管理关系(可以创建、查询和断开)的中间体 class Marriage
。
class Marriage {
constructor(h, w) {
this.husband = h;
this.wife = w;
}
divorce() {
this.husband.marriage = null;
this.wife.marriage = null;
}
}
class Wife() {
constructor() {
this.marriage = null;
}
get husband() {
return this.marriage && this.marriage.husband;
}
set husband(h) {
if (this.marriage) this.marriage.divorce();
if (h.marriage) h.marriage.divorce();
this.marriage = h.marriage = new Marriage(h, this);
}
}