简体   繁体   中英

Store ES6 class in variable and instance it through Object.Create

I'm currently needing to pass a class through a variable to a function which dynamically calls their constructor and so on. Short example:

class MyClass {
  constructor ($q) {
    console.log($q);
  }

  expose () {
    return {};
  }
}

const myclass = function () {
  // My concern is this ------.
  // I need to pass it        |
  // as a variable           \|/
  //                          '
  let obj = Object.create(MyClass.prototype);
  obj.constructor.apply(obj, arguments);
  return {}; // other stuff goes here
};
myclass.$inject = ['$q'];
export {myclass};

My idea is to pass MyClass as a variable, to have it this way:

const myclass = function (classVariable) {
  let obj = Object.create(classVariable.prototype);
  obj.constructor.apply(obj, arguments);
  return {}; // other stuff goes here
};

So that I can call it like let newclass = myclass(MyClass); . This way if I have different classes (MyClass2, MyClass3, and so on) I don't have to repeat code in each file. If I try to do it this way though, Chrome throws an error saying that it cannot use a class as variable or something like that.

So, what would be the best approach to do this and avoid repeating code? it's actually a redesign I'm working on to avoid the copy-paste disaster made by someone else I need to fix. Thanks in advance!

I think your confusion is that you think that class constructors cannot be referenced by variables. They can, they're just functions. So:

 class Foo { message() { console.log("I'm a Foo"); } } class Bar { message() { console.log("I'm a Bar"); } } function test(C) { let obj = new C(); obj.message(); // "I'm a Foo" or "I'm a Bar", depending on // whether Foo or Bar was passed in } test(Foo); test(Bar); 

Your pattern of calling var obj = Object.create(X.prototype) followed by X.apply(obj, /*...args here...*/) would work in ES5 and earlier, but ES2015's classes don't allow it. To construct instances from them, you have to use the new operator. The reason for that has to do with subclassing and setting new.target , so that if the instance has reason to create new instances (as Promise does), it can do that in an unambiguous way.

Which seems like it could be a step back, but if for some reason you have the constructor arguments as an array rather than discrete items, spread notation lets you use new anyway:

let obj = new C(...args);

So if you need a generic function that accepts a class constructor and an array of arguments and needs to return an instance of that class using those arguments, it would look like this:

function createInstanceOf(C, args) {
    return new C(...args);
}

I believe what you are looking for is a closure :

function makeFactory(constructor) {
  return function(...args) {
    let obj = new constructor(...args);
    return {}; // other stuff goes here
  };
}
const myclass = makeFactory(MyClass);
// then use
myClass().expose() // or whatever you were going to do with it

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