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

JavaScript Object inheritance by Object.create from primitive values

JavaScript inheritance with Object.create()?

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

建议在 ES5 中设计 Object.create() 以提供一种简单的方式来继承 JavaScript Object.

其工作原理如下:

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

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

出于某种原因,人们(在大多数情况下,他们觉得很难用某个问题解决问题)倾向于问 "Why do you need to this?" 这对规范来说真的无关紧要,所以,可能是最好具体说明。

我尝试创建一种方法来根据需要为任何 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

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

谢谢。

编辑: 我真的不想在这里扩展特定主题或规范问题,但不幸的是,我无法避免有人问我 "Why do you need this?" 我认为完全不合主题的事情。

那些在JavaScript中被称为"boxed primitives"的对象确实看起来很奇怪。让我们看看您有什么发现,以及如何通过原型使用派生来做您想做的事情。

首先,记住 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"

nice:x引用了一个对象,y是一个原型为x的新对象。 x 引用的对象有一个可枚举、可配置、可写的值 属性 称为 name,它被 y 继承。在x中,name是一个自己的属性;在 y 中,它是 继承的 属性。但在这两种情况下,属性 都很好 "normal."

现在让我们使用一个 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

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

y.constructor                      // [Function: Number]

嗯,它确实看起来像一个数字。由于y的原型是x,而y的原型是Number.prototype,所以y当然可以访问Number.prototype中的所有函数.但似乎无论我们调用其中的哪一个,例如:

y.toFixed(2)
y.toLocaleString()

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

现在稍微向下滚动到第 20.1.3 节,您可以看到数字操作如何尝试通过抽象操作 thisNumberValue 提取 [[NumberData]] 槽中的值并将如果未签出,则抛出 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;
}

现在

x = Object(5)           // [Number: 5]
y = createNewNumber(x)  // [Number: 5]
x.isPrototypeOf(y)      // true

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

附录

至于为什么不继承[[NumberData]]插槽,引用ES9规范:

Internal slots correspond to internal state that is associated with objects and used by various ECMAScript specification algorithms. Internal slots are not object properties and they are not inherited. Depending upon the specific internal slot specification, such state may consist of values of any ECMAScript language type or of specific ECMAScript specification type values. Unless explicitly specified otherwise, internal slots are allocated as part of the process of creating an object and may not be dynamically added to an object. Unless specified otherwise, the initial value of an internal slot is the value undefined. Various algorithms within this specification create objects that have internal slots. However, the ECMAScript language provides no direct way to associate internal slots with an object.

虽然这种语言清楚地表明没有办法在对象上创建或设置插槽,但似乎我们甚至无法检查一个。因此,从对象中获取数值将需要使用程序员可访问的 valueOf 属性 来自 Object.prototype。除非有人做一些疯狂的事情,比如 Object.create(null)。 :)