JavaScript inheritance with Object.create()?
或
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
但是,现在我想继承一个原始对象,例如
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
我想念什么?有什么解决方法吗?还是由于某种原因不可能?
谢谢。
编辑: 我确实不想在这里扩展特定主题或规范问题,但是不幸的是,我无法避免人们问我“为什么需要这个?”我认为这完全超出了主题。
答案 0 :(得分:3)
在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"
很好:x
引用一个对象,y
是一个原型为x
的新对象。 x引用的对象具有称为name
的可枚举,可配置,可写的值属性,该属性由y继承。在x中,name
是自有财产;在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
哇,刚才发生了什么?这是说y
不是数字吗?让我们检查一下:
y.constructor // [Function: Number]
确定它看起来像一个数字。由于y
的原型为x
,而y
的原型为Number.prototype
,因此y
当然可以访问Number.prototype
中的所有功能。但似乎无论我们称呼哪个,例如:
y.toFixed(2)
y.toLocaleString()
,依此类推,我们得到了这个错误!这里发生的事情是Number.prototype
中的所有这些函数都在检查对象的内部属性,他们希望在其中看到原语。数字对象的此内部插槽不被继承,因此当您进行y = Object.create(x)
时,包含3的x的插槽未被继承,因此在{{1 }},该插槽不包含原始数字! y
中的所有方法都希望内部插槽(称为Number.prototype
...请参阅官方ECMAScript Specification Section on Number Objects具有原始值。
现在向下滚动至20.1.3节,您可以看到number操作如何尝试通过抽象操作[[NumberData]]
提取thisNumberValue
插槽中的值,并抛出一个[[NumberData]]
(如果未签出)。这就是您所看到的!
那对您意味着什么?
如果要使用原型继承来创建其原型是现有数字对象的新数字对象,并以使新对象的数值与原始对象相同的方式执行此操作,则直接在JavaScript中。 TypeError
对象不是这样工作的!但是,您可以创建自己的数字类型,将原始值存储在可继承的属性中。
您可以尝试的另一件事:为每个基元创建自己的函数。例如:
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
属性。您需要实现自己的派生函数并自行设置原型。这是一个hack,但我希望它能有所帮助!
添加
关于为什么 [[NumberData]]
插槽未继承,这是ES9 Spec的引文:
内部插槽对应于与对象关联并由各种ECMAScript规范算法使用的内部状态。内部插槽不是对象属性,也不会继承。根据特定的内部插槽规范,这种状态可以由任何ECMAScript语言类型的值或特定的ECMAScript规范类型的值组成。除非另有明确说明,否则内部插槽将作为创建对象过程的一部分进行分配,并且可能不会动态添加到对象中。除非另有说明,否则内部插槽的初始值为未定义的值。本规范中的各种算法都会创建具有内部插槽的对象。但是,ECMAScript语言没有提供将内部插槽与对象关联的直接方法。
虽然这种语言明确表明无法在对象上创建或设置插槽,但似乎我们甚至无法检查一个插槽。因此,从对象中获取数值将需要使用[[NumberData]]
中程序员可访问的valueOf
属性。除非有人做类似Object.prototype
这样的疯狂事情。 :)