简体   繁体   中英

ES6 Instantiating a derived class from an existing instance

Consider the following scenario:

class A {
  constructor() {
    this.A = 'A';
  }

  createB() {
    //Create a B instance from this current instance
  }
}

class B extends A {
  constructor() {
    super();
    this.B = 'B';
  }
}

var base = new A();
var derived = new B();

base.A = 'C';

// Create a B instance from A so that
// a new A isn't instantiated and 
// fromInstance.A === 'C'
var fromInstance = base.createB();

I would like to be able to create an instance of B without having to create a new instance of A , but rather use the existing A instance.

My goal is to be able to spawn a B instance by calling a function within A , but also allow a B instance to be created directly and handle constructing a default A .

How can I achieve something like this when B extends A and requires super() to be called?

Not sure this is exactly what you want but it works for your example:

class A {
  constructor() {
    this.A = 'A';
  }
}

class B extends A {
  constructor() {
    super();
    this.B = 'B';
  }
}

var base = new A();
var derived = new B();

base.A = 'C';

// Create a B instance from A so that
// a new A isn't instantiated and 
// fromInstance.A === 'C'
var fromInstance = new B();
Object.assign(fromInstance, base);

console.log(fromInstance);

Here is an alternate solution. It is actually pretty common in C# and Java, but since JS has no method overloading, this is kind of cumbersome and not so nice compared to the above solution:

class A {
  constructor(source) {
    if(source){
      //use properties/stuff from source
      this.A=source.A;
    }
    else{
      //only perform initialization if source is not provided
      this.A = 'A';
    }
  }
}

class B extends A {
  constructor(source) {
    super(source);
    this.B = 'B';
  }
}

var base = new A();
var derived = new B();

base.A = 'C';

// Create a B instance from A so that
// a new A isn't instantiated and 
// fromInstance.A === 'C'
var fromInstance = new B(base);

console.log(fromInstance);

Basically, there are two versions of the constructor, one that creates a completely new object, and one that pretty much copies an old object.

I think there is a bit of a misunderstanding, every instance of B is by definition an instance of A , no matter what you do. If you want super to be called, you are calling the constructor of A , and thus "instantiating" A .

If I understand the problem, you want the new instance's A property to reflect the A property of the instance that created it, right? You can set this in createB since it will be called on A instance. This will allow the B instance to have a property that shadows the inherited A

 class A { constructor() { this.A = 'A'; } createB() { let b = new B() bA = this.A // set to the instances 'A' return b } } class B extends A { constructor() { super(); this.B = 'B'; } } var base = new A(); var derived = new B(); base.A = 'C'; // Create a B instance from A so that // a new A isn't instantiated and // fromInstance.A === 'C' var fromInstance = base.createB(); console.log(fromInstance)

A generic approach could copy all A instance properties to a new B instance. An even more generic approach would be to copy the B object's properties to temporary storage first, and write them back later, so that if A and B had the same property name, the B object's property would take precedence:

 class A { constructor() { this.A = 'A'; } createB() { let b = new B(); let temp = {}; let aProp, bProp; // save own properties of b Object.keys( b).forEach( bProp => (temp[bProp]=b[bProp])); // install own properties of A instance Object.keys( this).forEach( aProp => ( b[aProp]=this[aProp])); // write back b properties over A instance properties Object.keys( temp).forEach( bProp=> (this[bProp]=temp[bProp])); return b; } } class B extends A { constructor() { super(); this.B = 'B'; } } var base = new A(); var derived = new B(); base.A = 'C'; // Create a B instance from A so that // a new A isn't instantiated and // fromInstance.A === 'C' var fromInstance = base.createB(); console.log( "derived.A = %s", derived.A); console.log( "fromInstance.A = %s", fromInstance.A);

Note that in JavaScript terms avoiding Class constructs be syntactically easier because you can change the prototype properties of ordinary functions but not Class constructor functions. However you would probably lose the ability to reliably identify B instances using instanceof B . Using a class expression in CreateB has the same problem - returned objects would not share the same Class constructor.

Just create an instance of B , copy the properties of the current instance of A to it using Object.assign then return it:

createB() {
    var b = new B();
    Object.assign(b, this);
    return b;
}

Example:

 class A { constructor() { this.A = 'A'; } createB() { var b = new B(); Object.assign(b, this); return b; } } class B extends A { constructor() { super(); this.B = 'B'; } } var base = new A(); var derived = new B(); base.A = 'C'; var fromInstance = base.createB(); console.log(fromInstance instanceof B); console.log(fromInstance.A);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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