通过 Object.create 从原始值继承 JavaScript 对象

1 javascript inheritance object prototypal-inheritance prototype-chain

使用 Object.create() 继承 JavaScript?

或者

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

建议 Object.create() 是在 ES5 中设计的,旨在提供一种简单的方式来继承 JavaScript 对象。

这按预期工作,如下所示:

const A = function() {};
A.prototype.x = 10;
A.prototype.say = function() {
  console.log(this.x);
};

const a = new A();
a.say(); //10

const b = Object.create(a);
b.say(); //10
Run Code Online (Sandbox Code Playgroud)

但是,现在我想继承一个对象,例如 Object(3).

出于某种原因,人们(在大多数情况下,用某个问题很难解决问题的人)倾向于问“你为什么需要这个?” 这对规范来说真的无关紧要,因此,可能最好指定。

我尝试创建一种根据需要为任何 JavaScript 对象提供类型的方法。

幸运的是,每个原始值3都可以通过Object(3).

现在,我尝试了Object.create在这种情况下如何工作,但它不起作用。

const a = Object(3);

console.log(
  a
);//[Number: 3]
console.log(
  a.toString()
);// 3

const b = Object.create(a);

console.log(
  b
);//{}
console.log(
  Object.getPrototypeOf(b)
);
console.log(
  b.toString()
);
//TypeError: Number.prototype.toString requires that
// 'this' be a Number
Run Code Online (Sandbox Code Playgroud)

我想念什么?有什么解决方法吗?还是因为某种原因不可能?

谢谢。

编辑:我真的不想在这里扩展特定的主题或规范问题,但不幸的是,我无法避免人们问我“你为什么需要这个?” 我相信完全脱离主题的事情。

Ray*_*oal 5

那些在 JavaScript 中被称为“盒装原语”的对象绝对看起来很奇怪。让我们看看您发现了什么,以及如何通过原型使用派生来做您想做的事情。

首先,记住它做了什么y = Object.create(x)它创建了一个原型为 x 的新对象 y。

x = {name: "Rex"};
x.constructor;                     // [Function: Object]
typeof x;                          // "object"
y = Object.create(x);
Object.getPrototypeOf(y) === x;    // true
x.isPrototypeOf(y);                // true
y.name;                            // "Rex"
Run Code Online (Sandbox Code Playgroud)

Nice:x指一个对象,y是一个原型为 的新对象x。x 引用的对象有一个可枚举的、可配置的、可写的、名为 的值属性name,该属性由 y 继承。在 x 中,name自己的财产;in y,它是一个继承的属性。但在这两种情况下,该属性都非常“正常”。

现在让我们使用一个Number对象:

x = Object(3);                     // [Number: 3]
x.constructor;                     // [Function: Number]
typeof x;                          // "object"
x.valueOf();                       // 3
x + 0                              // 3
y = Object.create(x);
Object.getPrototypeOf(y) === x;    // true
x.isPrototypeOf(y);                // true

// So far so good, but now

y.valueOf()
    // TypeError: Number.prototype.valueOf requires that 
    // 'this' be a Number
y + 0
    // TypeError: Number.prototype.valueOf requires that 
    // 'this' be a Number
Run Code Online (Sandbox Code Playgroud)

哇刚刚发生了什么?这y是不是一个数字?让我们检查一下:

y.constructor                      // [Function: Number]
Run Code Online (Sandbox Code Playgroud)

好吧,它确实看起来像一个数字。由于原型的yx,和的原型y就是Number.prototypey肯定可以访问所有的功能Number.prototype。但似乎无论我们调用其中的哪一个,例如:

y.toFixed(2)
y.toLocaleString()
Run Code Online (Sandbox Code Playgroud)

等等,我们得到那个错误!这里发生的事情是,所有这些函数都在Number.prototype检查对象的内部属性,他们希望在那里看到一个原语。数字对象的这个内部槽不是继承的,所以当你做y = Object.create(x) x 的那个包含 3 的y槽没有被继承,所以在 中,那个槽不包含原始数字!所有的方法都Number.prototype期望内部槽(它被称为[[NumberData]]......请参阅关于数字对象的官方ECMAScript 规范部分以获得原始值。

现在稍微向下滚动到第 20.1.3 节,您可以看到数字操作如何通过抽象操作尝试提取槽中thisNumberValue的值,如果没有检出[[NumberData]],将抛出 a TypeError。这就是你所看到的!

所以这对于你来说意味着什么?

如果你想使用原型继承来创建一个新的数字对象,其原型是一个现有的数字对象,并以新对象的数值与原始对象相同的方式来做你不能直接在 JavaScript 中这样做。这不是Number对象的工作方式!但是,您可以创建自己的数字类型,其中原始值存储在可继承的属性中。

您可以尝试的另一件事是:为每个原语创建您自己的函数。例如:

function createNewNumber(original) {
    // n is expected to be a Number object
    const derived = new Number(original.valueOf());
    Object.setPrototypeOf(derived, original);
    return derived;
}
Run Code Online (Sandbox Code Playgroud)

现在

x = Object(5)           // [Number: 5]
y = createNewNumber(x)  // [Number: 5]
x.isPrototypeOf(y)      // true
Run Code Online (Sandbox Code Playgroud)

这可能会满足您的要求,但请记住,您不能直接使用Object.create! 对数字使用 Object.create 并不会继承[[NumberData]]我们亲眼所见的属性。需要自己实现推导函数,自己设置原型。这是一个黑客,但我希望它有所帮助!

附录

至于为什么[[NumberData]]插槽没有继承,这里是从ES9规格报价:

内部槽对应于与对象相关联并被各种 ECMAScript 规范算法使用的内部状态。内部槽不是对象属性,它们不是继承的。根据特定的内部插槽规范,这种状态可能由任何 ECMAScript 语言类型的值或特定 ECMAScript 规范类型的值组成。除非另有明确规定,内部插槽是作为创建对象过程的一部分分配的,不能动态添加到对象中。除非另有说明,内部插槽的初始值是未定义的值。本规范中的各种算法创建具有内部插槽的对象。但是,ECMAScript 语言没有提供将内部插槽与对象关联的直接方法。

虽然这种语言明确表示无法在对象上创建或设置插槽,但似乎我们甚至无法检查。因此,从物体抓取数值将需要使用编程器可访问的valueOf物业Object.prototype。除非一个人做了一些疯狂的事情,比如Object.create(null)。:)