简体   繁体   English

如何克隆 Javascript class

[英]How to clone a Javascript class

During unit testing, I want to Mock class Foo with a class called Bar.在单元测试期间,我想用名为 Bar 的 class 来模拟 class Foo。 I have to change a propriety in the class Foo for the test.我必须更改 class Foo 中的适当性以进行测试。

This is what I tried.这是我尝试过的。 It should print:它应该打印:

Foo

Bar酒吧

 class Foo { id = 'foo' constructor() { console.log(this.id) } } const Bar = {...Foo } Bar.prototype.id = 'Bar' new Foo() new Bar()

I get instead:我得到了:

error: Uncaught TypeError: Cannot set property 'id' of undefined

I know I can achieve this by creating a new class Bar extends Foo but is there other possible solutions?我知道我可以通过创建一个新的 class Bar extends Foo 来实现这一点,但是还有其他可能的解决方案吗?

Object Spread {...o} only considers enumerable properties. Object Spread {...o}仅考虑可枚举属性。

From MDN (emphasis mine):来自MDN (重点是我的):

Spread in object literals The Rest/Spread Properties for ECMAScript proposal (ES2018) added spread properties to object literals.在 object 文字中传播 ECMAScript 提案 (ES2018) 的 Rest/Spread 属性为 object 文字添加了传播属性。 It copies own enumerable properties from a provided object onto a new object.它将自己的可枚举属性从提供的 object 复制到新的 object 上。

The prototype property of classes and function is not enumerable.类和 function 的prototype属性不可枚举。 Therefore, in const Bar = {...Foo} , Bar will not have a prototype property.因此,在const Bar = {...Foo}中, Bar将没有prototype属性。 That is precisely why you get这正是你得到的原因

error: Uncaught TypeError: Cannot set property 'id' of undefined

Now, to get the code closer to what you want, you could write现在,为了使代码更接近您想要的,您可以编写

const Bar = {...Foo, prototype: Foo.prototype};

However, now Bar will behave differently from Foo because its prototype property will be enumerable.但是,现在Bar的行为与Foo不同,因为它的prototype属性是可枚举的。

So we could revise this and write所以我们可以修改这个并写

const Bar = {...Foo};
Object.defineProperty(Bar, 'prototype', {value: Foo.protoype, enumerable: false});
// `enumerable` defaults to `false` using this API, just being explicit for exposition.

However, your next statement, new B will still fail because B is still not a class object or a function object and therefore cannot be used with the new operator. However, your next statement, new B will still fail because B is still not a class object or a function object and therefore cannot be used with the new operator.

Without more context it is difficult to tell why you actually want to do this.如果没有更多的上下文,很难说出你为什么要这样做。

The most straightforward approach would be to write the following最直接的方法是编写以下内容

class Foo {
  id = 'Foo';
  constructor() {
    console.log(this.id);
  }
}
class Bar extends Foo {
  id = 'Bar';
}
new Foo() // logs 'Foo'
new Bar() // logs 'Bar'

If you would like to avoid using inheritance, there are other alternatives, but it is not clear which ones are appropriate because the question lacks sufficient context to determine this.如果您想避免使用 inheritance,还有其他选择,但尚不清楚哪些是合适的,因为该问题缺乏足够的上下文来确定这一点。 For example, if you only want to mock objects created via new Foo , then you could mock them trivially using a helper function that just overwrites the property with the test value, if you want to mock Foo itself, that is to say the class object, then the alternatives would be different.例如,如果您只想模拟通过new Foo创建的对象,那么您可以使用仅用测试值覆盖属性的助手 function 来模拟它们,如果您想模拟Foo本身,也就是说 class5B266966 ,那么替代方案会有所不同。

Instance properties in NodeJS and Babel are not actually set to the prototype. NodeJS 和 Babel 中的实例属性实际上并未设置为原型。 They are set in the constructor.它们在构造函数中设置。

Check this:检查这个:

class Foo {
  id = 1
}

new Foo().hasOwnProperty('id') // => true

An object will only lookup a property on its prototype if it's not defined within itself, so overriding the prototype won't work. object 只会在其原型上查找未在其内部定义的属性,因此覆盖原型将不起作用。

If you want to avoid class syntax, you could do something like this:如果你想避免class语法,你可以这样做:

function Bar() {
  const o = new Foo()
  o.id = 'bar'
  return o
}

new Bar().id // => 'bar'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM