将新的 属性 分配给具有冻结原型的空对象

Assign new property to empty object which has frozen prototype

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

没有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

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

如果您在 specificObject 上创建新的 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 = {} 将创建新的自己的 属性 特定对象,不会为原型的 属性 分配新值,但它可能会进行查找对于 属性 检查可写性,在这种情况下它将 运行 冻结为对象。我以前不知道这个。

What I don't get is why can't I assign to specificObject if its prototype is frozen?

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

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

如果您分配给一个对象 属性,但对象上不存在 属性,它会在原型上查找它 - 它可能被定义为 [=35] =] 那里。当此查找将 return 并说存在该名称的 属性 但它不可写时,您的分配将失败(并在严格模式下抛出)。

你能做些什么来解决这个问题?

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