【问题标题】:Assign new property to empty object which has frozen prototype将新属性分配给已冻结原型的空对象
【发布时间】:2015-04-13 09:59:06
【问题描述】:

为什么我不能为具有冻结原型的非冻结对象分配新属性:

在没有 Object.freeze 的情况下工作:

'use strict'
//This object will be prototype of next objects
var defaults = {
  name: 'def name', 
  sections: {
    1: {
      secName: 'def sec name'
    }
  }
};

//So we have an empty object with prototype set to our default object.
var specificObject = Object.create(defaults);


specificObject.sections = {};
console.log(specificObject.hasOwnProperty('sections')); //true

specificObject.sections['1'] = Object.create(defaults.sections['1']);

以上代码按预期工作,但我想确保默认值不会被意外更改。所以我想冻结我的默认对象:

'use strict'
//This object will be prototype of next objects
var defaults = {
  name: 'def name', 
  sections: {
    1: {
      secName: 'def sec name'
    }
  }
};

//!!!!!!!!!!!!
Object.freeze(defaults);

//So we have an empty object with prototype set to our default object.
var specificObject = Object.create(defaults);

//TypeError: Cannot assign to read only property 'sections' of #<Object>
specificObject.sections = {};
console.log(specificObject.hasOwnProperty('sections')); //true

specificObject.sections['1'] = Object.create(defaults.sections['1']);

我不明白的是,如果它的原型被冻结,为什么我不能分配给 specificObject?

//编辑: 请注意,特定对象未冻结:

'use strict'
//This object will be prototype of next objects
var protoObj = {a: 1, o: {}};
Object.freeze(protoObj);

console.log(Object.isFrozen(protoObj)); //true

var n = Object.create(protoObj);
console.log(Object.isFrozen(n)); //false

【问题讨论】:

  • 可能是因为如果原型被冻结,那么它是只读的。你不能对其进行任何更改
  • 这就是 Object.freeze 的重点——防止对其进行任何修改。
  • OP 正在尝试修改继承对象,而不是原型。
  • specificObject.__proto__ 指向原型,它是被冻结的默认值
  • 您已经使用冻结对象创建了specificObject。因此,它的原型被冻结,您正在尝试修改冻结的属性。你认为它会如何运作?

标签: javascript prototype ecmascript-5


【解决方案1】:

我不明白,如果 specificObject 的原型被冻结,为什么我不能分配给它?

因为属性属性是继承的。是的,这很奇怪。

如果你冻结一个对象,它会将所有数据属性的[[writable]]属性设置为false

如果您分配给一个对象属性,但该属性在该对象上不存在,它将去原型上查找它 - 它可能在那里被定义为 setter。当此查找返回并说存在该名称的属性但不可写时,您的分配将失败(并进入严格模式)。

你能做些什么呢?

  • 使用Object.defineProperty而不是赋值,它不检查原型
  • 或者类似地,使用Object.create的第二个参数来创建自己的属性
  • 仅在您分配给specificObject 后才冻结defaults

【讨论】:

  • 这是否包含在某些规范中?看来 V8 和 Firefox 确实都是这样的。
  • @KubaWyrostek:是的,Put 确实调用了 CanPut,这正是这样做的。
【解决方案2】:

我的理解是specificObject.sections 指向它的原型defaults,它是冻结对象。您定义了一个新对象{},但您尝试将其分配给defaults.sectionsSpecificObject.sections 正好指向那里。

如果您在特定对象上创建新的ownProperty,它将起作用:

'use strict'
//This object will be prototype of next objects
var defaults = {
  name: 'def name',
  sections: {
    1: {
      secName: 'def sec name'
    }
  }
};

//!!!!!!!!!!!!
Object.freeze(defaults);

//So we have an empty object with prototype set to our default object.
var specificObject = Object.create(defaults);

// this will create new property
Object.defineProperty(specificObject, 'sections',{
    enumerable: true,
    writable: true,
    configurable: true,
    value: {}
});
console.log(specificObject.hasOwnProperty('sections')); //true

specificObject.sections['1'] = Object.create(defaults.sections['1']);

解释:

如果您尝试访问 obj.prop = val,则 javascript 会查看 obj 自己的属性,如果没有找到,则它会查看 obj 的原型自己的属性。如果在那里找到,那么它 / 尝试将 val 分配给 / 查找该属性。如果在那里没有找到,那么它会尝试查看obj 的原型的原型等等。如果在prototype 树中找不到prop,则它会在obj 上创建新的自己的属性 并分配val

因此,如果在原型上找到prop 并且它被冻结,您将收到类型错误。希望它带来一些光明。:)

编辑:

正如@KubaWyrostek specificObj.sections = {} 正确指出的那样,将为特定对象创建新的自己的属性,不会为原型的属性分配新值,但它可能会查找属性以检查可写性,在这种情况下它会碰到冻结的物体。我以前不知道这件事。

【讨论】:

  • 我喜欢你的回答,它解决了我的问题,但它仍然没有解释为什么它试图修改原型上的部分,而不是特定对象上的部分。
  • 看来Object.defineProperty(x, 'y', …) 使用的机制与x['y'] = … 不同。在第二种情况下,原型仍然涉及?
  • @KubaWyrostek 我添加了如何访问该属性的说明。
  • 谢谢,但这个声明:“如果在那里找到,那么它会尝试将 val 分配给该属性”恕我直言不正确。咨询:jsfiddle.net/mw9xv0oy
  • @KubaWyrostek,你是对的。有点不一致。
猜你喜欢
  • 2020-05-22
  • 1970-01-01
  • 2014-04-05
  • 1970-01-01
  • 2011-09-22
  • 1970-01-01
  • 1970-01-01
  • 2012-06-21
  • 1970-01-01
相关资源
最近更新 更多