1

I've read this great question about using apply and new in order to create objects on the fly.

The exact question was:

I want to create an object instance (via the new operator), but pass an arbitrary number of arguments to the constructor. Is this possible

I have a question regarding the selected answer :

/*1*/   function construct(constructor, args) {
/*2*/       function F() {
/*3*/           return constructor.apply(this, args);
/*4*/       }
/*5*/       F.prototype = constructor.prototype;
/*6*/       return new F();
/*7*/   }

(working jsbin)

All ok.

But

I have tried to move line #3 to line #6 : so the final output is :

/*1*/   function construct(constructor, args) {
/*2*/      // function F() {
/*3*/      //     return constructor.apply(this, args);
/*4*/      // }
/*5*/       //F.prototype = constructor.prototype;
/*6*/       return new constructor.apply(this, args);
/*7*/   }

(non-working jsbin)

My question is why the second example doesnt work ? what's the difference ?

All I did was to move the code from one place to other place .

example:

function calc()
{
   function a() {return 4};

 return a(); // I could easily do `return 4` instead...;
}

Question:

Why does the moved code , isn't working ?

Community
  • 1
  • 1
Royi Namir
  • 144,742
  • 138
  • 468
  • 792

3 Answers3

1

You state that:

function construct(constructor, args) {
  function F() {
    return constructor.apply(this, args);
  }
  F.prototype = constructor.prototype;
  return new F();
}

should be the same as:

function construct(constructor, args) {
  return new constructor.apply(this, args);
}

But it actually differs in a few fundamental ways:

1. constructor.apply is a native non-constructor function. New can only create objects from constructors:

[...] objects may be created [...] via constructors which create objects and then execute code that initialises all or part of them by assigning initial values to their properties. Each constructor is a function that has a property named “prototype” that is used to implement prototype-based inheritance and shared properties. Objects are created by using constructors in new expressions [...]

http://www.ecma-international.org/ecma-262/5.1/#sec-4.2.1

F on the other hand is a function object. All function objects can be used as constructors.

Note: Alon Gubkin said new constructor.apply(this, args) doesn't work because constructor.apply(this, args).prototype is undefined. This is not true. If it were it would imply that the function call operator's precedence is higher than the new operator (i.e. that 'new constructor.apply(this, args) is the same as new (constructor.apply(this, args))`) which is not true.

2. Even if constructor.apply would be a constructor function it would not be the same as constructor.prototype. The prototype of F is set to the constructor's prototype to make sure all objects that are created through the new operator in combination with F are the same as those created in combination with constructor. The only difference between F and constructor being that they are different functions, thus making it possible for F to call constructor in a more flexible way.

constructor.apply.prototype can actually be set to constructor.prototype, but constructor.apply can still not be used a constructor function.

3. In your new constructor.apply(this, args) statement the this argument is not pointing to the newly created object, but to the this in local scope. So even if you could use the apply function as a constructor with the apply's prototype set to the constructor's prototype you would still be passing the wrong this to the constructor function.

Note: In your example

function calc()
{
   function a() {return 4};
   return a(); // I could easily do `return 4` instead...;
}

return can be used with any kind of expression, while new cannot.

Community
  • 1
  • 1
Lodewijk Bogaards
  • 19,777
  • 3
  • 28
  • 52
0

You still have to say to it to use the parent prototype.

Also it is a good ideia to specify it to use the extended constructor

Outside the function,

construct.prototype = Object.create(constructor.prototype);
construct.prototype.constructor = construct;

Edit:

On your second function, you are trying to create a new instance an already instantiated object.

The construct function should be called with new.

http://jsbin.com/modiqoha/7/edit

hawaii
  • 328
  • 4
  • 12
  • You are not defining a new object on the second one. – hawaii Mar 15 '14 at 17:08
  • hawaii i put so much effort in asking a precise question with code and references. I dont think i deserve this poor explanation. _You are not defining a new object on the second one_ I know it doesnt work . that's why I asked ! what's the difference between those 2 ? it's just moving code – Royi Namir Mar 15 '14 at 17:10
0

When the code new foo(...) is executed, a new object is created, inheriting from foo.prototype.

What you have tried to do:

return new constructor.apply(this, args);

... doesn't work because constructor.apply(this, args).prototype is undefined.


The reason why the original construct function still works without:

F.prototype = constructor.prototype;

is because when you do return new F();, F still has a prototype.

Alon Gubkin
  • 56,458
  • 54
  • 195
  • 288
  • i know this already. I followed the console. but how come the first code does work ? `return new f()` which is return `new ctor.apply(this,..)` which doesnt work.... tha'ts my problem. it's the same but it's not – Royi Namir Mar 15 '14 at 17:15
  • I deleted the comment. I still dont understand the explanation. read my first comment – Royi Namir Mar 15 '14 at 17:17
  • Because `F.prototype` is defined. Every function has a prototype. When you do `ctor.apply(this,..)`, it doesn't have a prototype. But when you wrap it with a function, it does. – Alon Gubkin Mar 15 '14 at 17:17
  • `new F()` does not simply replace F with the value returned by F. It firsts checks for `F.prototype`. – Alon Gubkin Mar 15 '14 at 17:20
  • _But when you wrap it with a function, it does_ : so why does this is not working ? http://jsbin.com/kifumaja/2/edit – Royi Namir Mar 15 '14 at 17:30
  • function without prototype- also work `function g() { } g.prototype=undefined; alert ( new g())` – Royi Namir Mar 15 '14 at 17:42