原型属性的不可写性如何影响具有该原型的对象?

问题描述 投票:0回答:3

MDN 有关“可写”描述符属性的文档指出:

writable
true
如果与属性关联的值可以通过赋值运算符更改。 默认为
false

官方 ECMA-262 第 6 版或多或少也有相同的规定。 含义很明确,但据我了解,它仅限于原始属性(即该特定对象的属性)

但是,请考虑以下示例(JSFiddle):

// works as expected, overloading complete      
var Parent = function () {};
Object.defineProperty(Parent.prototype, "answer", {
  value: function() { return 42; }
});

var Child = function() {};
Child.prototype = Object.create(Parent.prototype, {
  answer: {
    value: function() { return 0; }
  }
});

var test1 = new Parent();
console.log(test1.answer()); // 42
var test2 = new Child();
console.log(test2.answer()); // 0

//does not work as expected
var Parent2 = function() {};
Object.defineProperty(Parent2.prototype, "answer", {
  value: function() { return 42; }
});

var Child2 = function () {};
Child2.prototype = Object.create(Parent2.prototype);

test3 = new Parent2();
console.log(test3.answer()); // 42
test4 = new Child2();
test4.answer = function () { return 0; };
console.log(test4.answer()); // 42

按照这个例子,我们看到,虽然该属性不可写,但它可以在子类的原型上重载(

test2
),正如我所期望的。

但是,当尝试在子类(

test4
)的实例上重载该方法时,它会默默地失败。我本来希望它能像
test2
一样工作。当尝试重载
Parent
实例上的属性时,也会发生同样的情况。

同样的事情发生在 NodeJS 和 JSFiddle 中,并且在某些情况下,实例上的重载会引发关于属性只读性质的

TypeError

您能否向我确认这是预期的行为?如果是的话,解释是什么?

javascript inheritance prototype-chain writable propertydescriptor
3个回答
4
投票

是的,这是预期的行为。

它默默地失败了。

不完全是。或者:仅在草率模式下。如果您选择

"use strict"
模式,您将得到一个

Error { message: "Invalid assignment in strict mode", … }

上线了

test4.answer = function() { return 0; };

它可以在子类(test2)的原型上重载,但不能在子类(test4)的实例上重载

这与实例与原型无关。您没有注意到的是,您正在使用不同的方法来创建重载属性:

  • 对“继承且不可写”的属性的赋值失败
  • Object.defineProperty
  • 调用只会创建一个新属性,除非该对象是不可扩展的
    
    
    
  • 您可以对您的实例执行相同的操作:

Object.defineProperty(test4, "answer", { value: function() { return 42; } });



3
投票
EcmaScript-262

第 9.1.9 节 1.c.i 4. If ownDesc is undefined, then a. Let parent be O.[[GetPrototypeOf]](). b. ReturnIfAbrupt(parent). c. If parent is not null, then i. Return parent.[[Set]](P, V, Receiver).

但是,如果您想解决这个问题,您可以设置实例本身的描述符。

var proto = Object.defineProperties({}, { foo: { value: "a", writable: false, // read-only configurable: true // explained later } }); var instance = Object.create(proto); Object.defineProperty(instance, "foo", {writable: true}); instance.foo // "b"



0
投票
https://jsfiddle.net/s7wdmqdv/1/

var Parent = function() {}; Object.defineProperty(Parent.prototype,"type", { value: function() { return 'Parent'; } }); var oParent = new Parent(); console.log('parent', oParent.type()); // Parent var Child1 = function() {}; Child1.prototype = Object.create(Parent.prototype, { type: { value: function() { return 'Child1'; } } }); var oChild1 = new Child1(); console.log('child1', oChild1.type()); // Child1 var Child2 = function() {}; Child2.prototype = Object.create(Parent.prototype); Object.defineProperty(Child2.prototype, 'type', { value: function() { return 'Child2'; } }); var oChild2 = new Child2(); console.log('child2', oChild2.type()); // Child2 var Child3 = function() {}; Child3.prototype = Object.create(Parent.prototype); var oChild3 = new Child3(); oChild3.type = function() { return 'Child3'; }; console.log('child3', oChild3.type()); // Parent var Child4 = function() {}; Child4.prototype = Object.create(Parent.prototype); Child4.prototype.type = function() { return 'Child4'; }; var oChild4 = new Child4(); console.log('child4', oChild4.type()); // Parent Object.defineProperty(Parent.prototype,"type", { value: function() { return 'Parent2'; } }); var oParent2 = new Parent(); console.log('parent2',oParent2.type());

当您使用 Object.create(...) 克隆原型时,原始描述符仍然附加在原型链的更高位置。

将某些内容分配给

child.answer = 10

时,它将使用

Child.prototype.answer.writable
。如果不存在,它将尝试
Child.prototype.constructor.prototype.answer.writable
(如果存在)。

但是,如果你尝试

Object.defineProperty(Child.prototype, ...)

,它不会检查原型链。它将测试它是否在 Child.prototype 上定义。如果没有定义,那么它将定义它。

    

© www.soinside.com 2019 - 2024. All rights reserved.