【问题标题】:JavaScript - uniquely updating properties in prototyped object literalsJavaScript - 唯一更新原型对象文字中的属性
【发布时间】:2011-02-02 01:06:18
【问题描述】:

我刚刚开始使用原型构造函数实例化 JavaScript 对象字面量,但对适用于我的案例的继承/反射规则有点困惑。

基本上我的问题是原型化的原始对象有几层属性/值:

var protoObj = {  
  prop1: {  
    first : 1,
    second : 2,
    third : {  
      a : 'foo',
      b : 'bar',
      c : {  
        'love': true,  
        'babies': false,
        'joy': undefined
      }
    } 
  },  
  prop2 : true       
};

...在所有子对象中,除了少数这些属性之外,所有属性都将保持不变(并且应该动态更新)。

我可以在不影响原始原型对象的情况下为子对象更新或创建新的顶级属性:

if (typeof Object.create !== 'function') {
  Object.create = function(o){
    var F = function(){};
    F.prototype = o;
    return new F();
  };
}

var someObj = Object.create(protoObj);

someObj.prop2 = false;   
//protoObj.prop2 remains true

someObj.prop3 = [x,y,z];
//new property, unique to someObj (i.e. protoObj remains untouched)

但是,如果我想为 someObj 中根深蒂固的属性 'joy' 设置一个唯一值,我似乎无法在不重新定义 protoObj 本身或破坏 someObj 的委托链的情况下这样做回到它的原型:

someObj.prop1.third.c['joy'] = 'found';
//does not *directly* update someObj
//instead ends up setting the value of protoObj's property 'joy' to be 'found'


someObj.prop1 = {
  first : 1,
  second : 2,
  third : {  
    a : 'foo',
    b : 'bar',
    c : {  
      'love': true,  
      'babies': false,
      'joy': 'found'
    }
  }
};
//explicitly sets the above values in someObj.prop1 (including 'joy'), but breaks
//the prototypal link back to protoObj for all data in someObj.prop1 as well

我怀疑我要么忽略了一个非常简单的解决方案,要么完全不走运,但无论如何我都非常有义务听取大家的意见。提前致谢。

【问题讨论】:

  • 你能提供从 protoObj 产生 someObj 的代码吗?事实上,你如何制作所有这些东西的原型?
  • @Pierre - 在第二个代码块中更新。
  • 他不需要改变任何东西,他的问题很清楚。问题是他的原型包含对一个对象的引用,因此对该对象的更改将在该类的所有实例中可见。
  • 我很确定你完全不走运。

标签: javascript object prototype


【解决方案1】:

简短回答:您部分运气不佳。

长答案。当您从另一个对象创建原型时,您是在告诉 JavaScript 解释器派生对象“看起来像”祖先对象。当您从属性中读取时,解释器知道如果需要它可以遍历原型链。所以对于:

var x = someObj.prop2;

它首先查看someObj 对象。它有一个名为prop2 的属性吗?不,所以去看看原型对象。它是否有一个名为prop2 的属性?是的,所以返回那个值。

当您设置一个属性时,它会直接在对象中设置。没有在这里走原型链。所以:

someObj.prop2 = true;

表示prop2 属性在someObj 中创建并设置为true。在此之后,当您阅读prop2 时,您将获得直接值,并且无需遍历原型链。

现在prop1 也会发生同样的事情。从someObj阅读它

var x = someObj.prop1;

您将从原型对象中获取对象prop1,因为它在someObj 中不存在。

但请注意:这是对propObj 属性的引用。更改其属性,您将更改它们为propObj。如果您确实需要对 prop1 进行更改以使这些更改仅在 someObj 中可见,则您也必须对该对象进行原型设计:

var someObj = Object.create(propObj);
someObj.prop1 = Object.create(propObj.prop1);
someObj.prop1.first = 42;

这里,最后一行将更改someObj.prop1first 的值,而不是propObj 中的值。如果您需要更深入,则将原型设计到下一个级别,依此类推。

【讨论】:

  • 有点烦人的怪癖,但我想它的存在是有充分理由的,而且解决起来也不会太麻烦。无论如何,这很棒 - 非常感谢!
【解决方案2】:

我想我明白了。

原型用于与所有实例共享方法。您似乎正在尝试使用原型将特定实例成员赋予派生的 someObj 对象。

但要了解为什么它是一个糟糕的解决方案,请了解原型设计的工作原理:

当 JS 尝试解析(保留)对象上的成员引用时,如果它在对象中找不到该成员,它会在其原型中查找(依此类推) )。

所以当你要读取 someObj.prop1 时,JS 确实在 someObj 中找到 prop1,它会在其原型 protoObj 中查找,并找到 protoObj.prop1:

这样说,这是完全正常的:

someObj.prop1.third.c['joy'] = 'found'

...修改原型,因为 someObj.prop1 被解析为 protoObj.prop1

您忽略的一个小细节是,在做作中,JS 不需要 解析 成员。当你写这样的东西时:

someObj.stuff.other = 3

JS 解析 someObj.stuff 是因为你在问 .other

但是当你输入时:

someObj.stuff = 3

JS 确实解决任何问题!!它只影响 3someObjstuff 成员,根本不关心 stuff 的存在与否在 someObj 或其原型中。

如果 stuff 在原型中有效,则同名的新成员会掩盖原型。

从现在开始,您可能会明白,使用 JS 的原型为实例赋予成员值根本不是一个好的解决方案。相反,您应该考虑在函数构造函数本身中设置这些成员。例如:

function MyObj() {
  this.prop1 = {first: 1, second: 2, third: {a: ...} }
  this.prop2 = true;
}

显然,每个实例都有自己的成员,不同于 protoObj 的成员。

如果您想使用原型设计为您的对象提供一些默认值和可变值,并能够在您通过实例修改它们时“分叉”这些值的一部分(一种写时复制),这在 JavaScript 中是不可能的。

【讨论】:

  • “这在 JavaScript 中是不可能的”
  • 很高兴他做到了!谢谢你给它一个裂缝。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-26
  • 2011-12-17
  • 1970-01-01
  • 2020-06-21
  • 2012-03-16
  • 2016-07-13
相关资源
最近更新 更多