我有一个坐在原型上的对象,如果它有像bar这样的简单属性:3,那么任何实例都可以更改bar而不影响其他实例。但是,如果它有一个对象属性(baz),任何更改bar.x的实例都将反映在所有其他实例上,我认为因为对象是引用的。
坐在原型上的示例对象:
var foo = {
bar: 3, // single prop - this is okay
baz:{x: 4,y:5} // object prop - an instance can't change baz.x
};
我的问题 - 在下面的代码中,我如何制作保时捷秀"对象道具:V12"?
var vehicle = {
colour: 'blue',
info: function() {
console.log('wheels:' + this.wheels + ' colour:' + this.colour);
}
};
var engine = {
size: 'V12', // single prop
type:{size: 'V12',fuel:'petrol'}, // object prop
showSize: function() {
console.log('single prop: ' + this.engine.size );
console.log('object prop: ' + this.engine.type.size);
}
};
var car = Object.assign(Object.create(vehicle), {
wheels: 4,
drift: function() { console.log('drifting'); }
});
var ferrari = Object.assign(Object.create(car), {
colour:'red',
engine: Object.create(engine)
});
var porsche = Object.assign(Object.create(car), {
colour:'silver',
engine: Object.create(engine)
});
// ferrari owner changes his engine
ferrari.engine.size = '100cc';
ferrari.engine.type.size = '100cc';
console.log('ferrari:');
ferrari.engine.showSize.call(ferrari);
console.log('\nporsche:');
porsche.engine.showSize.call(porsche);
/*
OUTPUT
ferrari:
single prop: 100cc
object prop: 100cc
porsche:
single prop: V12
object prop: 100cc <------ WRONG, should be V12
*/
编辑:对于任何偶然发现的人,我将使用this pattern;对我来说,创建构造函数和使用call(this)更直观。具有讽刺意味的是,它非常接近阿米特的答案,但我觉得功能构造者并不具备原型继承/授权的真正精神。
必须在每个班级中做到这一点&#39;看起来很笨重:
car.prototype = Object.create(vehicle.prototype ); // <- new way
car.prototype = new vehicle(); // <- old way
car.prototype.constructor = car;
相反,我确切地知道使用这种模式会发生什么:
var car = Object.create(vehicle, {
constructor : { value: function (colour, wheels) {
vehicle.constructor.call(this, colour, wheels);
return this;
}}
});
一个中的六个,另一个中的六个;)
答案 0 :(得分:4)
你必须为每个实例提供自己的对象,没有办法解决这个问题。
你的代码实际上表明你已经熟悉了这个概念:你给每辆车都有自己的引擎。我们可以使用相同的模式为每个引擎创建自己的类型:
var engine = {
size: 'V12',
showSize: function() {
console.log('single prop: ' + this.engine.size );
console.log('object prop: ' + this.engine.type.size);
}
};
var enginetype = {
size: 'V12',
fuel: 'petrol'
};
…
var ferrari = Object.assign(Object.create(car), {
colour:'red',
engine: Object.assign(Object.create(engine), {
type: Object.create(enginetype);
})
});
var porsche = Object.assign(Object.create(car), {
colour:'silver',
engine: Object.assign(Object.create(engine), {
type: Object.create(enginetype);
})
});
(但我并不是说复制.size
.type.size
是一个很好的设计,我会假设它只是一个例子)
答案 1 :(得分:4)
首先让我们解开你所做的事情,以及为什么你得到你所做的结果......
Object.create(proto)
创建一个空对象({}
)并将其原型设置为proto
。在您的情况下,此方法用于为car
创建一个空对象,其中包含vehicle
原型,以及两个带有原型car
的意大利速度计。它还用于创建2个“引擎”。
您还要调用3个“car”create
调用中的每一个,调用Object.assign(target, source)
将新属性作为实例属性附加到目标对象(新创建的对象)。
因此,无论何时访问(读取或写入)对象的属性,如果该属性属于该实例,您将读取或写入该特定实例的值。但是,如果实例没有定义该属性,则遍历原型链直到找到该属性,然后在相关原型的上下文中使用它。在您的情况下,这意味着由于汽车的引擎是带有共享原型的空对象(您在顶部初始化的engine
对象),因此访问引擎的属性实际上是转到该特定的单个实例。如果您修改它,则可以为所有对象修改它。
说了这么多,你可能做的事情有点不同......我更喜欢使用正确的构造函数并使用new
关键字创建对象。
这是你重构的代码:
function vehicle(colour) {
this.colour = colour || 'blue'; // blue default if nothing else provided
};
vehicle.prototype.info = function() {
console.log('wheels:' + this.wheels + ' colour:' + this.colour);
};
function engine(size, fuel) {
this.size = size || 'V12'; // V12 default if nothing else provided
this.fuel = fuel || 'petrol'; // petroc default if nothing else provided
};
engine.prototype.showSize = function() {
console.log('size: ' + this.size );
};
function car(colour) {
vehicle.call(this, colour);
this.wheels = 4;
this.engine = new engine();
};
car.prototype = new vehicle();
car.prototype.constructor = car; // <-- otherwise, (new car()).constructor != car
car.prototype.drift = function() { console.log('drifting'); };
var ferrari = new car('red');
var porsche = new car('silver');
// ferrari owner changes his engine
ferrari.engine.size = '100cc';
console.log('ferrari:');
ferrari.engine.showSize();
ferrari.info();
console.log('\nporsche:');
porsche.engine.showSize();
porsche.info();