JS对象中的数组属性是如何工作的?

11

下面的代码中,为什么myObjpropB被更新了?test.childObj为什么没有拥有自己的propB属性?

var myObj = { propA: '', propB: [] }
var fatherObj = {
    childObj: null,
    init: function() {
        this.childObj = Object.create(myObj);
        this.childObj.propA = 'A';
        this.childObj.propB.push(2);
    }
}

var test = Object.create(fatherObj);
test.init();

console.log(myObj.propB.length);
console.log(test.childObj.hasOwnProperty('propA'));
console.log(test.childObj.hasOwnProperty('propB'));

4个回答

11

使用Object.create时,你不会复制一个对象,而是创建一个继承传递的对象的新对象:

  this.childObj { } ->  myObj { propA: "", propB: [] }

现在,当你获取一个属性时,它会在继承链中进行查找。由于在子对象中找不到它,所以会在myObj中查找。通常这不是问题,因为直接设置对象的属性会将其设置在对象本身上,而不使用继承链,因此:

  this.childObj.propA += "test";

查找myObj上的propA,但将propA设置在childObj上。然而,对于引用类型,您不会重写该属性,因此:

  this.childObj.propB.push(1);

查找myObj中的propB,并将结果推入其中,最终结果为:

 this.childObj { propA: "test" } ->  myObj { propA: "", propB: [1] }
为了解决这个问题,childObj必须有自己的propB数组:
 this.childObj.propB = this.childObj.propB.slice();

这导致:

 this.childObj { propA: "test", propB: [1] } ->  myObj { propA: "", propB: [1] }

现在推送到 propB,会将值推送到 childObj 中的数组。


3

2
通过使用Object.create(myObj);myObj成为childObj的原型。如果在一个对象中未找到属性但在原型中找到了,则使用原型中的属性。同时注意,hasOwnProperty可以告诉您对象是否拥有该属性本身,如果该属性仅存在于原型而不在对象中,则返回false。通过直接在对象上分配属性,将其设置在对象上而不是原型上,但当您使用push修改属性propB时,该属性在childObject中找不到,因此您会修改原型。

您必须小心处理,因为以这种方式创建的所有对象都将共享相同的原型对象,通过修改一个对象,您将为所有实例进行修改。

您还必须非常小心,因为在javascript中很难知道属性从原型链的哪个位置来,因为myObj也有一个原型。

var myObj = { propA: '', propB: [] }
var fatherObj = {
    childObj: null,
    init: function() {
        this.childObj = Object.create(myObj);
        this.childObj.propA = 'A';
        this.childObj.propB.push(2);
    }
}

var test = Object.create(fatherObj);
test.init();

console.log('test: ', test);
console.log('test prototype: ', test.__proto__);
console.log('test.childObj: ', test.childObj);
console.log('test.childObj prototype: ', test.childObj.__proto__);
console.log(test.childObj.hasOwnProperty('propA'));
console.log(test.childObj.hasOwnProperty('propB'));


1
Javascript继承不像大多数其他语言那样工作。当使用var x = Object.create(someObj)时,新对象x实际上是空的(它没有自己的属性),并带有对其原型的引用:单独的对象someObj

任何您尝试从x访问但未解析的属性函数都将在someObj中查找,甚至更高层次,如果someObj也有一个原型对象等。

当继承的属性是可修改对象时,情况可能变得混乱,就像您的示例一样。正如我们所见,尝试通过将项目推入其中来修改数组x.propB,实际上会修改包含在someObj中的继承数组。

我坚信,在原型对象中具有可修改对象(数组和常规对象)是不好的风格。相反,原型对象应仅包含函数无数据,或者至少没有超出简单字符串、数字和布尔值的可修改数据。

总结:

  • 函数对于继承非常有用,它们不可修改(无法更改函数体),但仍可在需要时进行覆盖/替换。
  • 数据成为所有继承者共享的数据,除非/直到它们通过重新分配来覆盖它,这本身就是一个负担。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接