#javascript #object #inheritance
#javascript #объект #наследование
Вопрос:
Сейчас я разрабатываю какой-то игровой движок, работающий в canvas. У меня есть некоторые базовые, но теперь я хочу добавить родителей и дочерних элементов… Все работает нормально, пока я не попытаюсь добавить ссылку на дочерний объект к родительскому объекту. Это очень короткая часть моей проблемы:
// Object that will be parent of obj1
var obj0 = {
childs: [],
someValue: 10,
parent: null
}
// obj1 is defined from obj0 throught Object.assign(), so any change in
// obj1 will NOT reflect to obj0 (I added console.log() to show it)
var obj1 = Object.assign({}, obj0);
console.log("Expected result: false; Result: " (obj0 == obj1));
obj1.someValue = 5;
console.log("Expected result: false; Result: " (obj0.someValue == obj1.someValue));
// Now add obj0 as parent to obj1...
obj1.parent = obj0;
// ... and add obj1 as child to obj0 - I need to do this throught .parent
// (I can't directly do some change in obj1)
obj1.parent.childs.push(obj1);
// Everything seems be alright, but... If I look into obj1.childs...
console.log(obj1.childs)
// I see, that .push applied to all objects that are...
Как вы можете видеть, .push()
метод применяется ко всем объектам (в данном случае к обоим объектам).
Итак, мои вопросы:
1. Почему?
2. Как этого избежать?
Заранее благодарю вас…
Комментарии:
1. В результате
Object.assign()
вызова оба объекта получают копию одного и того жеchilds
массива. Это «неглубокая» копия.2. @Pointy Вы правы, спасибо.. Но, есть ли у вас какие-либо идеи, как этого избежать?
3. Ну, вам пришлось бы написать свой собственный код, чтобы скопировать один из ваших объектов в другой. По моему опыту, желание выполнить «глубокую копию» указывает на проблему архитектуры, и в общем случае глубокие копии объектов даже не всегда возможны.
4. @Явно Да… Я знаю, что создать точную копию объекта очень сложно, но если у меня там только пустой массив (и он будет пустым каждый раз, когда я создаю новый объект), могу ли я делать что-то такое
obj1.childs = []
всякий раз, когда я создаю новый? Ссылки не будет, верно?5.
Object.assign
не имеет ничего общего с наследованием. Ты имел в видуObject.create
?
Ответ №1:
После obj1 = Object.assign({}, obj0)
оба obj0
и obj1
ссылаются на один и тот же childs
массив. Таким образом, какую бы мутацию вы ни внесли в этот массив (например, с помощью push
), она будет видна через оба объекта.
Один из способов избавиться от таких нежелательных эффектов — создать конструктор (особенность JavaScript), возможно, даже используя class
обозначение:
class MyClass {
constructor(value) {
this.childs = [];
this.someValue = value;
this.parent = null;
}
}
// Object that will be parent of obj1
var obj0 = new MyClass(10);
var obj1 = new MyClass(5);
console.log("Expected result: false; Result: " (obj0 == obj1));
console.log("Expected result: false; Result: " (obj0.someValue == obj1.someValue));
// Now add obj0 as parent to obj1...
obj1.parent = obj0;
obj1.parent.childs.push(obj1);
// Everything is alright.
console.log(obj0.childs);
// Nothing changed here:
console.log(obj1.childs);
Ответ №2:
Согласно комментариям, проблема в том, что Object.assign
это неглубокая копия, и в конечном итоге childs
свойство обоих объектов является одним и тем же массивом, так что, когда вы push
обращаетесь к одному childs
массиву, другой также обновляется (оба childs
массива являются одним и тем же).
Возможно, вы хотели бы использовать класс для определения ваших объектов:
// Object that will be parent of obj1
class MyObj {
constructor() {
this.childs = [];
this.someValue = 10;
this.parent = null;
}
}
var obj0 = new MyObj();
var obj1 = new MyObj();
console.log("Expected result: false; Result: " (obj0 == obj1));
obj1.someValue = 5;
console.log("Expected result: false; Result: " (obj0.someValue == obj1.someValue));
// Now add obj0 as parent to obj1...
obj1.parent = obj0;
// ... and add obj1 as child to obj0 - I need to do this throught .parent
// (I can't directly do some change in obj1)
obj1.parent.childs.push(obj1);
console.log("obj0's children:")
console.log(obj0.childs)
console.log("obj1's children:")
console.log(obj1.childs)