ECMA-262 states that a constructor is an object that supports the [[Construct]] internal method. I suspect that BigInt and Symbol support [[Construct]] because they are included in Constructor Properties of the Global Object. However, both return a TypeError when called with the new operator:
try {
console.log(typeof new BigInt(5));
} catch (e) {
console.log(`${e.name}: ${e.message}`); // TypeError: BigInt is not a constructor
}
try {
console.log(typeof new Symbol(5));
} catch (e) {
console.log(`${e.name}: ${e.message}`); // TypeError: Symbol is not a constructor
}
Note that the error includes is not a constructor.
It is confusing to me and perhaps to others that a constructor is an object that supports the [[Construct]] internal method, and BigInt and Symbol support this internal method, but BigInt and Symbol are not constructors (per the error messages), and neither support the new operator.
(Note: I do understand why BigInt and Symbol don't support the new operator, and why Boolean and perhaps others really shouldn't either.)
Why aren't BigInt and Symbol just function objects that do not support [[Construct]] and do not have a seemingly unnecessary prototype property? If they were, then the current definition would suffice.
From the point of view of the spec, BigInt and Symbol are constructors, and have a [[Construct]] internal method, but its behavior is to return a TypeError. So for (most?) practical purposes, they look like not-constructors.
Like all objects, BigInt and Symbol have a [[Prototype]] internal slot which points to a parent object from which the objects inherit properties. [[Prototype]] is different than a prototype property which constructors have. A constructor’s prototype property points to the object that will become the [[Prototype]] value of any instances that it creates. But, and this is my point, BigInt and Symbol don't create instances because they are incapable of acting like constructors. That's why I'm suggesting that there is no need for BigInt and Symbol to have a prototype property. I diagrammed this distinction in Diagrams of JavaScript constructors.
Yes, were talking about .prototype here. Those objects are still getting used as the [[prototype]] for instances that are getting created temporarily when accessing a property on primitives, such as Symbol("example").description or 1n.toString(). You can also explicitly construct them using Object(Symbol("example")) or Object(1n).
The spec only defines the type of Error that is thrown, the error message is up to the implementation. It just so happens that many implementations have mutually chosen the same message of "is not a constructor".
Thanks bergus, aclaymore, and jmdyck. Very helpful. It's interesting that the Chrome and Node.js implementations (and surely others) interpret the TypeError as "is not a constructor". It suggests to me that (perhaps) the implementation teams do not view Symbol and BigInt as constructors. I would guess this is because Symbol and BigInt are the only standard built-in constructors that do not support the new operator. Just a guess. If so, though, it suggests that many implementation engineers define a constructor as a function object that supports the new operator. I have, too, up to this point. We know, of course, that the spec defines a constructor as a function object that supports [[Construct]]. But, I don't know an empirical way to test this. What do you think of the following (testable) definition: A constructor is a function object that possesses a prototype property. I'm thinking this is true in all cases. Thx much.
Partially based on this discussion, I modified the definition of constructor in Diagrams of JavaScript constructors. I included the isConstructor function though I modified it slightly to make it easier to undertand for readers. I'd be glad to credit you, aclaymore, for this function in my article if you agree. The purpose of the article is to present a clean way to diagram constructors and their instances. Thanks again.