0

I am an experienced object oriented programmer but this got me! Why am I able to do new f() but not new a(). I will appreciate any pointers.

 // first a few facts 
if (Object instanceof Function) console.log("Object isa Function");
console.log("Function.prototype is " + Function.prototype);
/* output
 Object isa Function
  Function.prototype is function Empty() {}
*/

var f = new Function();
console.log("Prototype of f:" + f.prototype);
console.log("Constructor of f:" + f.constructor);
console.log("Prototype Link of f:" + f.__proto__);
if (f instanceof Function) console.log("f isa Function");
/* output
Prototype of f:[object Object]
Constructor of f:function Function() { [native code] }
Prototype Link of f:function Empty() {}
 f isa Function
*/


function A() {}
console.log("Prototype of A:" + A.prototype);
console.log("Constructor of A:" + A.constructor);
console.log("Prototype Link of A:" + A.__proto__);
if (A instanceof Function) console.log("A isa Function");
/*
Prototype of A:[object Object]
Constructor of A:function Function() { [native code] }
Prototype Link of A:function Empty() {}
A isa Function
*/

 // contruct a
var a = new A();
console.log("Prototype of a:" + a.prototype);
console.log("Constructor of a:" + a.constructor);
console.log("Prototype Link of a:" + a.__proto__);
if (a instanceof Function) console.log("a isa Function");
if (a instanceof A) console.log("a isa A");
/* output
 Prototype of a:undefined
Constructor of a:function A(){}
Prototype Link of a:[object Object]
a isa A
*/

console.log("~~~~~b constructed as new f()");
var b = new f();
console.log("Prototype of b:" + b.prototype);
console.log("Constructor of b:" + b.constructor);
console.log("Prototype Link of b:" + b.__proto__);
/* output
 ~~~~~b constructed as new f()
Prototype of b:undefined
Constructor of b:function anonymous() {}
Prototype Link of b:[object Object]
*/

console.log("~~~~~b constructed as new a()");
a.prototype = Object.prototype;
a.constructor = Function;
a.__proto__ = Function.prototype;
if (a instanceof Function) console.log("a isa Function");
console.log("Prototype of a:" + a.prototype);
console.log("Constructor of a:" + a.constructor);
console.log("Prototype Link of a:" + a.__proto__);
/* output
~~~~~b constructed as new a()
a isa Function
Prototype of a:[object Object]
Constructor of a:function Function() { [native code] }
Prototype Link of a:function Empty() {}     
*/
b = new a();
/* ERROR  Uncaught TypeError: object is not a function*/

I have done my best to provide the output as well. notice that f and a are identical in terms of prototype, constructor and prototype link. Why does the ERROR appear when I try to new a() in the last line?

Pointy
  • 405,095
  • 59
  • 585
  • 614
Al Merchant
  • 87
  • 1
  • 1
  • 9
  • 2
    "new " needs a function, _a_ is an object instance of the _A_ constructor – dandavis Dec 15 '14 at 23:00
  • You're expecting there to be a "prototype" property of an instantiated object, but that's incorrect. *edit* yes and as @dandavis says, objects aren't functions and can't be used as constructors. – Pointy Dec 15 '14 at 23:01
  • 1
    Constructors are Objects and Functions, when called as constructors they return a plain Object (by default), not a Function. Plain Objects have a private `[[Prototype]]` but no public *prototype* (by default), Functions have both. – RobG Dec 15 '14 at 23:04
  • Dandavis, a isa function as pointed ut in the code – Al Merchant Dec 15 '14 at 23:27
  • Pointy, I have explicitly set the prototype property of the instantiated object as you can see in the code. – Al Merchant Dec 15 '14 at 23:28
  • RobG, Please elaborate. I have explicitly set the prototype property. So how does the interpretor differentiate between an instance and an object definition? Especially because Object instance of Function is TRUE. – Al Merchant Dec 15 '14 at 23:29
  • As stated before; f is tyoeof function and a is not (case sensitive) only function types can be used with new to create new instances. Maybe the following can help: http://stackoverflow.com/questions/16063394/prototypical-inheritance-writing-up/16063711#16063711 – HMR Dec 15 '14 at 23:37
  • Object is a function because you can do new Object but the result of new Object is an Object instance, not a function. New Function will get you an instance of type Function so you can use new on the result of new Function – HMR Dec 15 '14 at 23:40
  • HMR, thanks for the link. Its a good one. – Al Merchant Dec 16 '14 at 04:22

1 Answers1

1

As the error points out, a is expected to be a function, that is, it must be callable. The new keyword requires a function object that knows how to construct an instance - but a does not. Letting it inherit from Function.prototype (by using __proto__) does not help anything, callability is an intrinsic property of objects.

You are able to call new f(), as f is such a constructor function, being created by the Function constructor.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Bergi, Thanks for some useful information. Is there a good place to read about the intrinsic property of javascript objects? I want to gain a fundamental understanding as opposed to a functional understanding. Thanks again! – Al Merchant Dec 16 '14 at 00:54
  • You can always [read the spec](http://es5.github.io/), specifically [§8.6.2 Object Internal Properties and Methods](http://es5.github.io/#x8.6.2) (`new` uses `[[construct]]`). If you need a more extensive explanation, Dmitry Soshnikov has written a very good series [*EcmaScript in detail*](http://dmitrysoshnikov.com/ecmascript/chapter-7-2-oop-ecmascript-implementation/), also (however apparantly unfinished) [on ES5](http://dmitrysoshnikov.com/ecmascript/es5-chapter-0-introduction/). – Bergi Dec 16 '14 at 01:05
  • Thanks very much. Surprised how little there is on this subject. Maybe a simple isCallable() implementation is necessary. from the document you suggested I interpret that [[Callable]] is an internal property that needs to be set for an object to be called as a function or through the new operator. [[Construct]] on the other hand is just what is usually implemented as a constructor pointer? in other words my original problem was that the constructor for a was not callable? or a itself is not callable or both? – Al Merchant Dec 16 '14 at 03:12
  • I might've oversimplified my answer. Calling `x()` (or using `.call()` etc) will result in `[[call]]` being used, or - if the object in question is not callable, i.e. has no `[[call]]` internal property - an exception being thrown. This can trivially be tested by using [`typeof x == "function"`](http://es5.github.io/#x11.4.3). However, writing `new x` will call the `[[construct]]` internal property. All user-defined functions (such as `f` or `A`) [have this property](http://es5.github.io/#x13.2.2). Plain user-created objects (`a`, `b`, `{}`) have neither [[call]] nor [[construct]]. – Bergi Dec 16 '14 at 03:31
  • According to the spec typeof return "function" if the argument implements [[Call]]. Is there a similar test for [[Construct]]? Thanks again. – Al Merchant Dec 16 '14 at 04:19
  • No, the only thing you can do is something like `try { new x; return true } catch (e) { return e instanceof TypeError }` – Bergi Dec 16 '14 at 05:42
  • Thank you Bergi. Your comments were great help. Most importantly you understood the question precisely. Congratulations, I think you have a very good grasp on the fundamentals of javascript and can express your knowledge very clearly. Best Regards, – Al Merchant Dec 17 '14 at 22:45
  • Oh, btw: https://esdiscuss.org/topic/add-reflect-isconstructor-and-reflect-iscallable and https://esdiscuss.org/topic/isconstructor :-) – Bergi Dec 17 '14 at 23:00