4

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!

6
  • 5
    Why do you need to use Object.create rather than just calling the constructor via new as it's intended to be used? Commented Aug 25, 2016 at 21:29
  • If you're doing all this in order to pass on the arguments with .apply(), you can use the spread operator instead. Commented Aug 25, 2016 at 21:32
  • @T.J.Crowder because I can't do new <variable> when it's a variable, not a class. I got on the internet and SO and suggestions were to use object.create instead. I'm attempting to do something alike to C# Generics (<T> types) if you've ever handled them. Commented Aug 25, 2016 at 22:18
  • @squint spread operator? how so? can you shed me some light please? :) Commented Aug 25, 2016 at 22:18
  • 1
    @ DARKGuy: "because I can't do new <variable> when it's a variable, not a class." Yes, you can. See my answer. The surprising thing about JavaScript, when you're coming to it from Java or C# or C++, is how simple it is. A class is just a constructor. A constructor is just a function (okay, that's not quite true, but it's really close). You can refer to a function from a variable or argument (which are basically the same thing). :-) Commented Aug 25, 2016 at 22:42

2 Answers 2

11

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);
}
Sign up to request clarification or add additional context in comments.

2 Comments

I created my Factory this way, and everything is ok, except instanceof ParentClass. Do you know how to fix it?
@Crusader - Not without code, no. Neither the question nor the answer has any inheritance, and it'll depend on how you set that inheritance up. (And whether you're doing what the OP is doing -- which I don't recommend -- or what I suggest at the end of the answer.)
0

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.