0

The following code does not work as desired (jsFiddle):

function makeFoo(a, b) {
  var foo = new Foo();
  Foo.apply(foo, arguments);
  return foo;
}
var Foo = function(a, b) {
  console.log(
    "This should be called once. "+
    "a=\"" + a + "\", " + 
    "b=\"" + b + "\", "
  );
  this.a = a;
  this.b = b;
}
Foo.prototype.go = function() {
  console.log("a: " + this.a);
  console.log("b: " + this.b);
};
var foo = makeFoo("Hello", "World");
foo.go();

Expected output:

This should be called once. a="Hello", b="World"
a: Hello
b: World

Actual output:

This should be called once. a="undefined", b="undefined"
This should be called once. a="Hello", b="World"
a: Hello
b: World

1

2 Answers 2

6

That's because you call Foo twice: via new and via function call.

I think with new Foo() you only wanted to create an object which inherits from Foo.prototype. To achieve that, use Object.create(Foo.prototype) instead.

function makeFoo(a, b) {
  var foo = Object.create(Foo.prototype);
  var result = Foo.apply(foo, arguments);
  return Object(result) === result ? result : foo;
}
var Foo = function(a, b) {
  console.log(
    "This should be called once. "+
    "a=\"" + a + "\", " + 
    "b=\"" + b + "\", "
  );
  this.a = a;
  this.b = b;
}
Foo.prototype.go = function() {
  console.log("a: " + this.a);
  console.log("b: " + this.b);
};
var foo = makeFoo("Hello", "World");
foo.go();

But that's just a hack, which you are required to use in ECMAScript 5 because there is no way to instantiate a constructor with an arbitrary number of arguments.

Constructors should be instantiated, not called as functions. In ECMAScript 6, you can do it with Reflect.construct.

function makeFoo(a, b) {
  return Reflect.construct(Foo, arguments);
}
var Foo = function(a, b) {
  console.log(
    "This should be called once. "+
    "a=\"" + a + "\", " + 
    "b=\"" + b + "\", "
  );
  this.a = a;
  this.b = b;
}
Foo.prototype.go = function() {
  console.log("a: " + this.a);
  console.log("b: " + this.b);
};
var foo = makeFoo("Hello", "World");
foo.go();

Sign up to request clarification or add additional context in comments.

3 Comments

Brilliant, thanks. Why is the first snippet a hack? What is the problem with using a constructor as a function to achieve this result? And why must the result of apply () be checked?
@Jodes the check on the return value is to handle the fact that the constructor function might return some primitive value when not invoked via new. A constructor invocation via new will ignore such a return value, so that test ensures that the right thing is returned from the "makeFoo" function.
@Jodes The first one is a hack because it uses [[Call]] instead of [[Construct]]. As Pointy said, I check the value returned by the constructor to emulate what the new operator does.
0

Try this:

function makeFoo(a, b){
  var foo = new Foo(a,b);
  return foo;
}

1 Comment

The point seems to be able to pass an arbitrary number of arguments.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.