Ich habe „Javascript: The Good Parts“ von Douglas Crockford gelesen – und obwohl es ein bisschen extrem ist, bin ich mit vielem, was er zu sagen hat, an Bord.
In Kapitel 3 diskutiert er Objekte und legt an einer Stelle ein Muster (auch hier gefunden) zur Vereinfachung und Vermeidung einiger Verwirrungen/Probleme, die mit der Verwendung des integrierten Schlüsselworts „neu“ einhergehen.
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
newObject = Object.create(oldObject);
Also habe ich versucht, dies in einem Projekt zu verwenden, an dem ich arbeite, und ich habe ein Problem festgestellt, als ich versuchte, von verschachtelten Objekten zu erben. Wenn ich einen Wert eines verschachtelten Objekts überschreibe, das mit diesem Muster geerbt wurde, überschreibt es das verschachtelte Element ganz nach oben in der Prototypkette.
Crockfords Beispiel ist wie das flatObj
im folgenden Beispiel, das gut funktioniert. Das Verhalten ist jedoch bei verschachtelten Objekten inkonsistent:
var flatObj = {
firstname: "John",
lastname: "Doe",
age: 23
}
var person1 = Object.create(flatObj);
var nestObj = {
sex: "female",
info: {
firstname: "Jane",
lastname: "Dough",
age: 32
}
}
var person2 = Object.create(nestObj);
var nestObj2 = {
sex: "male",
info: {
firstname: "Arnold",
lastname: "Schwarzenneger",
age: 61
}
}
var person3 = {
sex: "male"
}
person3.info = Object.create(nestObj2.info);
// now change the objects:
person1.age = 69;
person2.info.age = 96;
person3.info.age = 0;
// prototypes should not have changed:
flatObj.age // 23
nestObj.info.age // 96 ???
nestObj2.info.age // 61
// now delete properties:
delete person1.age;
delete person2.info.age;
delete person3.info.age;
// prototypes should not have changed:
flatObj.age // 23
nestObj.info.age // undefined ???
nestObj2.info.age // 61
(auch auf einem Geige)
Mache ich etwas falsch oder ist das eine Einschränkung dieses Musters?
Es gibt keine Inkonsistenz. Denken Sie nur nicht an verschachtelte Objekte: a Direkte Eigenschaft eines Objekts ist immer entweder sein Prototyp oder eine eigene Eigenschaft. Dabei ist es unerheblich, ob der Eigenschaftswert ein Primitiv oder ein Objekt ist.
Also, wenn du es tust
var parent = {
x: {a:0}
};
var child = Object.create(parent);
child.x
wird auf dasselbe Objekt verweisen wie parent.x
– Das hier {a:0}
Objekt. Und wenn Sie eine Eigenschaft davon ändern:
var prop_val = child.x; // == parent.x
prop_val.a = 1;
beide werden davon betroffen sein. Um eine “verschachtelte” Eigenschaft unabhängig zu ändern, müssen Sie zuerst ein unabhängiges Objekt erstellen:
child.x = {a:0};
child.x.a = 1;
parent.x.a; // still 0
Was Sie tun können, ist
child.x = Object.create(parent.x);
child.x.a = 1;
delete child.x.a; // (child.x).a == 0, because child.x inherits from parent.x
delete child.x; // (child).x.a == 0, because child inherits from parent
das heißt, sie sind nicht absolut unabhängig – aber immer noch zwei verschiedene Objekte.
Ich denke, was passiert, ist das, wenn Sie etwas erschaffen person2
der sex
und info
Eigenschaften davon beziehen sich auf die in nestObj
. Wenn Sie referenzieren person2.info
seit person2
definiert das nicht neu info
Eigenschaft, geht es bis zum Prototyp durch und modifiziert dort das Objekt.
Es sieht so aus, als ob der “richtige” Weg, es zu tun, die Art und Weise ist, wie Sie bauen person3
damit das Objekt seine eigene hat info
zu modifizierendes Objekt und reicht nicht bis zum Prototyp.
Ich lese das Buch auch (langsam), also sympathisiere ich mit Ihnen. 🙂
Ich habe die Beispiele geändert, um Ihnen eine bessere Demonstration dessen zu geben, was hier passiert. Demo
Zuerst erstellen wir ein Objekt mit drei Eigenschaften; Eine Zahl, eine Zeichenfolge und ein Objekt mit einer Eigenschaft mit einem Zeichenfolgenwert.
Dann erstellen wir ein zweites Objekt aus der ersten Verwendung Object.create()
;
var obj1 = {
num : 1,
str : 'foo',
obj : { less: 'more' }
};
var obj2 = Object.create( obj1 );
console.log( '[1] obj1:', obj1 );
console.log( '[1] obj2:', obj2 );
"[1] obj1:"
[object Object] {
num: 1,
obj: [object Object] {
less: "more"
},
str: "foo"
}
"[1] obj2:"
[object Object] {
num: 1,
obj: [object Object] {
less: "more"
},
str: "foo"
}
Sieht gut aus, oder? Wir haben unser erstes Objekt und ein zweites kopiertes Objekt.
Nicht so schnell; Mal sehen, was passiert, wenn wir einige der Werte für das erste Objekt ändern.
obj1.num = 3;
obj1.str="bar";
obj1.obj.less="less";
console.log( '[2] obj1:', obj1 );
console.log( '[2] obj2:', obj2 );
"[2] obj1:"
[object Object] {
num: 3,
obj: [object Object] {
less: "less"
},
str: "bar"
}
"[2] obj2:"
[object Object] {
num: 3,
obj: [object Object] {
less: "less"
},
str: "bar"
}
Jetzt haben wir wieder unser erstes Objekt mit Änderungen und eine Kopie dieses Objekts. Was passiert hier?
Lassen Sie uns überprüfen, ob die Objekte ihre eigenen Eigenschaften haben.
for( var prop in obj1 ) console.log( '[3] obj1.hasOwnProperty( ' + prop + ' ): ' + obj1.hasOwnProperty( prop ) );
for( var prop in obj2 ) console.log( '[3] obj2.hasOwnProperty( ' + prop + ' ): ' + obj2.hasOwnProperty( prop ) );
"[3] obj1.hasOwnProperty( num ): true"
"[3] obj1.hasOwnProperty( str ): true"
"[3] obj1.hasOwnProperty( obj ): true"
"[3] obj2.hasOwnProperty( num ): false"
"[3] obj2.hasOwnProperty( str ): false"
"[3] obj2.hasOwnProperty( obj ): false"
obj1
hat alle seine eigenen Eigenschaften, genau wie wir sie definiert haben, aber obj2
nicht.
Was passiert, wenn wir einige ändern obj2
Eigenschaften?
obj2.num = 1;
obj2.str="baz";
obj2.obj.less="more";
console.log( '[4] obj1:', obj1 );
console.log( '[4] obj2:', obj2 );
for( var prop in obj1 ) console.log( '[4] obj1.hasOwnProperty( ' + prop + ' ): ' + obj1.hasOwnProperty( prop ) );
for( var prop in obj2 ) console.log( '[4] obj2.hasOwnProperty( ' + prop + ' ): ' + obj2.hasOwnProperty( prop ) );
"[4] obj1:"
[object Object] {
num: 3,
obj: [object Object] {
less: "more"
},
str: "bar"
}
"[4] obj2:"
[object Object] {
num: 1,
obj: [object Object] {
less: "more"
},
str: "baz"
}
"[4] obj1.hasOwnProperty( num ): true"
"[4] obj1.hasOwnProperty( str ): true"
"[4] obj1.hasOwnProperty( obj ): true"
"[4] obj2.hasOwnProperty( num ): true"
"[4] obj2.hasOwnProperty( str ): true"
"[4] obj2.hasOwnProperty( obj ): false"
Damit, num
und str
eingeschaltet obj2
und nicht an obj1
so wie wir wollten, aber obj1.obj.less
geändert, wenn es nicht hätte sein sollen.
Von dem hasOwnProperty()
Kontrollen können wir das sehen, obwohl wir uns geändert haben obj2.obj.less
wir haben nicht gesetzt obj2.obj
Erste. Dies bedeutet, dass wir uns immer noch auf beziehen obj1.obj.less
.
Lassen Sie uns ein Objekt aus erstellen obj1.obj
und zuordnen obj2.obj
und sehen, ob das uns das gibt, wonach wir suchen.
obj2.obj = Object.create( obj1.obj );
console.log( '[5] obj1:', obj1 );
console.log( '[5] obj2:', obj2 );
for( var prop in obj1 ) console.log( '[5] obj1.hasOwnProperty( ' + prop + ' ): ' + obj1.hasOwnProperty( prop ) );
for( var prop in obj2 ) console.log( '[5] obj2.hasOwnProperty( ' + prop + ' ): ' + obj2.hasOwnProperty( prop ) );
"[5] obj1:"
[object Object] {
num: 3,
obj: [object Object] {
less: "more"
},
str: "bar"
}
"[5] obj2:"
[object Object] {
num: 1,
obj: [object Object] {
less: "more"
},
str: "baz"
}
"[5] obj1.hasOwnProperty( num ): true"
"[5] obj1.hasOwnProperty( str ): true"
"[5] obj1.hasOwnProperty( obj ): true"
"[5] obj2.hasOwnProperty( num ): true"
"[5] obj2.hasOwnProperty( str ): true"
"[5] obj2.hasOwnProperty( obj ): true"
Das ist gut, jetzt obj2
hat sein eigenes obj
Eigentum. Mal sehen, was passiert, wenn wir wechseln obj2.obj.less
jetzt.
obj2.obj.less="less";
console.log( '[6] obj1:', obj1 );
console.log( '[6] obj2:', obj2 );
"[6] obj1:"
[object Object] {
num: 3,
obj: [object Object] {
less: "more"
},
str: "bar"
}
"[6] obj2:"
[object Object] {
num: 1,
obj: [object Object] {
less: "less"
},
str: "baz"
}
Das alles sagt uns also, dass, wenn die Eigenschaft am erstellten Objekt noch nicht geändert wurde, alle get
Anforderungen an das erstellte Objekt für diese Eigenschaft werden an das ursprüngliche Objekt weitergeleitet.
Die set
Anfrage für obj2.obj.less="more"
aus dem vorherigen Codeblock erfordert zunächst a get
Anfrage für obj2.obj
die es in nicht gibt obj2
an diesem Punkt, also leitet es weiter obj1.obj
und wiederum obj1.obj.less
.
Dann endlich, wenn wir lesen obj2
wieder, wir haben immer noch nicht festgelegt obj2.obj
damit get
Anfrage wird weitergeleitet an obj1.obj
und geben die Einstellung zurück, die wir zuvor geändert hatten, was den Effekt verursacht, dass das Ändern einer Eigenschaft des zweiten Objekts, Objektkind, beide zu ändern scheint, aber in Wirklichkeit nur das erste ändert.
Sie können diese Funktion verwenden, um ein neues Objekt vollständig getrennt vom Original rekursiv zurückzugeben.
Demo
var obj1 = {
num : 1,
str : 'foo',
obj : { less: 'more' }
};
var obj2 = separateObject( obj1 );
function separateObject( obj1 ) {
var obj2 = Object.create( Object.getPrototypeOf( obj1 ) );
for(var prop in obj1) {
if( typeof obj1[prop] === "object" )
obj2[prop] = separateObject( obj1[prop] );
else
obj2[prop] = obj1[prop];
}
return obj2;
}
console.log( '[1] obj1:', obj1 );
console.log( '[1] obj2:', obj2 );
for( var prop in obj1 ) console.log( '[1] obj1.hasOwnProperty( ' + prop + ' ): ' + obj1.hasOwnProperty( prop ) );
for( var prop in obj2 ) console.log( '[1] obj2.hasOwnProperty( ' + prop + ' ): ' + obj2.hasOwnProperty( prop ) );
"[1] obj1:"
[object Object] {
num: 1,
obj: [object Object] {
less: "more"
},
str: "foo"
}
"[1] obj2:"
[object Object] {
num: 1,
obj: [object Object] {
less: "more"
},
str: "foo"
}
"[1] obj1.hasOwnProperty( num ): true"
"[1] obj1.hasOwnProperty( str ): true"
"[1] obj1.hasOwnProperty( obj ): true"
"[1] obj2.hasOwnProperty( num ): true"
"[1] obj2.hasOwnProperty( str ): true"
"[1] obj2.hasOwnProperty( obj ): true"
Mal sehen, was passiert, wenn wir jetzt einige Variablen ändern.
obj1.num = 3;
obj1.str="bar";
obj1.obj.less="less";
console.log( '[2] obj1:', obj1 );
console.log( '[2] obj2:', obj2 );
"[2] obj1:"
[object Object] {
num: 3,
obj: [object Object] {
less: "less"
},
str: "bar"
}
"[2] obj2:"
[object Object] {
num: 1,
obj: [object Object] {
less: "more"
},
str: "foo"
}
Alles funktioniert genau so, wie Sie es erwartet haben.
verwandt: JavaScript Object.create — vererbt verschachtelte Eigenschaften
– Bergi
8. Juni 2014 um 13:00 Uhr