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

ken*_*ken 5 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};
Run Code Online (Sandbox Code Playgroud)

预期:

> new Obj
x: 1, o.x: 1
> new Obj
x: 1, o.x: 1
> new Obj
x: 1, o.x: 1
Run Code Online (Sandbox Code Playgroud)

实际:

> new Obj
x: 1, o.x: 1
> new Obj
x: 1, o.x: 2
> new Obj
x: 1, o.x: 3
Run Code Online (Sandbox Code Playgroud)

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

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

function Obj() {
    this.o = {x: 1};
}
Run Code Online (Sandbox Code Playgroud)

这似乎真的非正统(我需要手动重新初始化属性,但只有当它们是参考对象时).

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

I H*_*azy 3

这样想吧。无论值来自哪里,您总是会修改正在操作的对象。

当您第一次执行此操作时:

this.x++;
Run Code Online (Sandbox Code Playgroud)

x它正在获取from的值Obj.prototype.x,因为该对象上没有x直接的属性this,但++仍在对该对象进行操作this,因此该值是在该对象上设置的。该值现在直接位于实例上以供将来修改。( 一直prototype.x处于阴影状态,直到直接属性为deleted。)

但是,当您这样做时:

this.o.x++
Run Code Online (Sandbox Code Playgroud)

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

返回引用后,您可以查找对象x的属性Obj.prototype.o并修改它。

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


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