实例如何更新原型上的对象属性?

时间:2015-08-18 21:14:02

标签: javascript prototypal-inheritance

我有一个坐在原型上的对象,如果它有像​​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"?

jsfiddle

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;
    }}

});

一个中的六个,另一个中的六个;)

2 个答案:

答案 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();