作为引用的实例化对象和属性

时间:2012-10-18 18:05:24

标签: javascript

就在我以为我有JS想出来的时候,我就挂断了这个:

function Obj() {
    console.log('x: %s, o.x: %s', this.x++, this.o.x++);
}    
Obj.prototype.x = 1;
Obj.prototype.o = {x: 1};

预期:

> new Obj
x: 1, o.x: 1
> new Obj
x: 1, o.x: 1
> new Obj
x: 1, o.x: 1

实际值:

> new Obj
x: 1, o.x: 1
> new Obj
x: 1, o.x: 2
> new Obj
x: 1, o.x: 3

因此,似乎如果原型属性是引用类型,那么它将在所有实例之间共享,但如果它是非引用类型,那么它将针对每个实例重新初始化;为了证实这个假设,我测试了一些其他类型,例如string(行为类似number)和array(行为类似object)。

如果我重新初始化ctor中的object属性,我已经确定可以避免这个陷阱,如下所示:

function Obj() {
    this.o = {x: 1};
}

这似乎非常不正统(我需要手动重新初始化属性,但前提是它们是参考对象)。

任何人都可以了解正在发生的事情吗?

2 个答案:

答案 0 :(得分:3)

这样想。无论价值来自何处,您总是会修改您正在操作的对象。

第一次执行此操作时:

this.x++;

x获取Obj.prototype.x的值是因为x对象上没有this属性,但++仍然是<{1}} em>在<{1}}对象上运行,因此在该对象上设置了值。该值现在直接在实例上以供将来修改。 this被遮蔽,直到直接属性为prototype.x d。)

但是,当你这样做时:

delete

this.o.x++ 对象上的唯一操作是查找this。由于o上没有o属性,您将获得对存储在this的对象的引用。此时,Obj.prototype.o对象没有实际修改。

返回引用后,然后在this对象上查找属性x,并对其进行修改。

所以它实际上非常一致。使用Obj.prototype.o,您已对this.o.x++执行了查找,但没有突变。突变在引用的对象上。但是使用this,您可以直接改变对象。


是的,除非您希望在从构造函数创建的所有实例之间共享引用类型,否则应将引用类型直接放在实例上而不是this.x++上。

答案 1 :(得分:0)

我认为第一个答案有它的要点,但我的答案略有不同。

理解它的关键是分配给对象的对象行为。

如果没有实例属性,查找将回退到原型。总是

但是,

分配每次都会自动创建一个新的实例属性。如果将by-reference prototyped属性分配给新的实例属性,这并不重要,因为它们只是指向同一事物的指针。如果你就地变异,也不会创建新的实例属性。

function Obj(){
    //this.refArr resolves to what's in the prototype and that's what we change
    this.refArr.push(1);

    //now copy sliceArray and assign a new but identical array
    this.sliceArray = this.sliceArray.slice(0);

    //no length increase would mean a new instance property is created
    //... and then the assigment took place.
    this.sliceArray.push(1);
    console.log( 'ref:'+this.refArr.length +', sliceArray:'+this.sliceArray.length);
}
Obj.prototype.refArr = [];
Obj.prototype.sliceArray = [];
new Obj; new Obj;
//ref:1, sliceArray:1
//ref:2, sliceArray:1

这实际上与你正在做的事情有所不同。 this.x = this.x + 1创建一个新属性。由于您指定了this.constructor.prototype.o的属性,因此不会创建新属性。如果你增加然后将this.o分配给this.o无论如何都无关紧要,因为原型和新属性版本只会指向同一个对象。