Is a constructor always a function object? - javascript

I'm reading the latest ECMA-262 reference, edition 5.1 June 2011.
In section 8.6.2 table 9 we have in regard to the [[Construct]] internal property:
Creates an object. Invoked via the new operator. The
arguments to the SpecOp are the arguments passed to the
new operator. Objects that implement this internal method
are called constructors.
The standard doesn't say that a constructor has to be a Function object. So can we have a constructor object that is not a function object?
Link to the standard as requested

The answer is extremely simple. ES5 § 4.3.4 says:
Constructor
Function object that creates and initialises objects.
So there you have it, by definition only a Function can be a constructor. However, likely there are host objects that behave like constructors that do not have any of the other attributes of native Function objects (e.g. the original XMLHttpRequest object in IE that was implemented in ActiveX).

While the term "Constructor" is defined (as #RobG pointed out), there is nothing that prevents a non-"Constructor" object from having a [[Construct]] method.
This is a bit confusing. It means you can use the new operator on an object that is not a Function (thus not a "constructor" as per 4.3.4
), but does indeed provide a [[Construct]] method.
Note that none of the standard objects qualify for that, but host objects may indeed. A browser plugin such as Java may expose some object like so:
new java.lang.String(); // it works, so java.lang.String has a [[Construct]] method
java.lang.String instanceof Function // false
Object.prototype.toString.call(java.lang.String).indexOf('Function') // -1
Note that typeof java.lang.String returns "function" even though java.lang.String is not a function. This is correct according to 11.4.3 (it is a host object with a [[Call]] method)

To add to Pumbaa80's answer (this would be too long for a comment).
The confusion is increased by 13.2.2 according to which when a function's construct is executed its call operation has to be executed(but it doesn't say what has to be done when the construct of an object that isn't a function is executed). Now, objects who implement call are callable function objects according to 9.11.
Also according to 4.2 "a function is a callable object". But of course this doesn't imply that every callable object is a function.
So if I got this right non Function objects can have a Construct method and also a Call method. java.lang.String would be one such example.

Related

Are Array, Object, Map built-in constructor functions or classes?

In some places, they say that Array, Date and Object are built-in classes. However, I find that they can all be called without new which isn't supposed to be possible with classes.
And even though Map, Set, etc... can't be called without new. All of them will show something like function ItsName() { [native code] } while trying to get their definition while I would think a class is supposed to show something like class ItsName { [native code] }.
The most specific description for objects like Object, Array, etc. is "built-in constructor".
In short, section 4.4.12 built-in object annotates:
NOTE
Standard built-in objects are defined in this specification.
An ECMAScript implementation may specify and supply additional kinds of
built-in objects. A built-in constructor is a built-in object that is
also a constructor.
Section 4 is non-normative, but the above note can be derived from the following:
Section 6.1.7.2 Object Internal Methods and Internal Slots: (Boldness applied by me)
... A function object is an object that supports
the [[Call]] internal method. A constructor is an object that
supports the [[Construct]] internal method. Every object that
supports [[Construct]] must support [[Call]];
that is, every constructor must be a function object.
Therefore, a constructor may also be referred to as a
constructor function or constructor function object.
This means, constructors are functions, and functions are objects.
Section 6.1.7.4 Well-Known Intrinsic Objects:
Well-known intrinsics are built-in objects (...).
And:
The well-known intrinsics are listed in Table 6.
With Table 6:
Global Name
ECMAScript Language Association
Array
The Array constructor (23.1.1)
Object
The Object constructor (20.1.1)
...
...
Point 2 lists several built-in objects like Array and Object. The table and the definitions in the corresponding links call these "constructors".
Point 1 explains that constructors are objects, so it is valid to say "built-in constructor" instead of "built-in objects", where applicable.
Here is a short summary of other interesting terms and definitions in the specification:
An object is a member of the type Object. (See 4.4.6)
A function (or function object) is an object that supports the internal function [[Call]] (which may or may not be exposed). (See 6.1.7.2 Table 5)
A constructor (or constructor function or constructor function object) is a function that supports the internal function [[Construct]] (commonly invoked with the new operator). (See 6.1.7.2 Table 5)
All constructors have the property prototype. (See 4.3.1)
Both function definitions and function expressions create constructors. (See 15.2.4 and 15.2.5, respectively)
Arrow function expressions do not create constructors and can therefore not be instantiated. (See 15.3.4)
Class definitions (ES6 class syntax) return constructor functions that are uncallable (meaning they do not expose [[Call]]). (See 15.7.14)

What is the return value of an ES2015 constructor function?

This was a question that originally came up because I was trying to document an ES2015 class with JSDoc, and to document the class's constructor function properly I needed to know the return value and its type, and I realized that I had no idea. In researching it I couldn't find the question being asked on Stack Overflow, so I dug around looking up the details of ES2015 classes, the new operator, and the constructor function. I have found what I think might be the answer, but thought it might be helpful to post the question on SO, along with what I found, and see if others can confirm or have better answers.
To demonstrate the question, suppose one has the following code:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
var mySquare = new Rectangle(10, 10);
console.log(mySquare);//output: Object { height: 10, width: 10 }
One doesn't ever directly or explicitly call the constructor function, nor explicitly return anything from it, but it is clearly invoked and it must return something. So, what is the return value of the constructor function? And of what type is the return value?
To make things more difficult, one can't simply call the constructor and check the type of the return value, as I tried to do at first:
console.log(typeof mySquare.constructor(11, 11));//output: TypeError: class constructors must be invoked with |new|
console.log(typeof Rectangle.prototype.constructor(12, 12));//output: TypeError: class constructors must be invoked with |new|
According to MDN's reference on the new operator:
When the code new Foo(...) is executed, the following things happen:
A new object is created, inheriting from Foo.prototype.
The constructor function Foo is called with the specified arguments, and with this bound to the newly created object. new Foo is equivalent to new Foo(), i.e. if no argument list is specified, Foo is called without arguments.
The object returned by the constructor function becomes the result of the whole new expression. If the constructor function doesn't explicitly return an object, the object created in step 1 is used instead. (Normally constructors don't return a value, but they can choose to do so if they want to override the normal object creation process.)
(Emphasis mine.) So it seems that the return value of the constructor function is an object, more specifically an instance of the class.
Further research showed that the return value of the constructor can be overridden by explicitly returning any object from within the constructor function (though non-object return values will be ignored, in which case the default of the newly created object reference is used.) I could not find anything to indicate that ES2015 constructors are any different than constructors prior to ES2015 classes.
This Stack Overflow post was very helpful in researching the above, particularly this article it contained a link to, though since constructor did not appear in the code in either the question nor the article, with my limited understanding of constructors that I had going into researching this they did not seem at first to answer my question. Hopefully this may clarify for others in the same situation.
Edit: The veracity of MDN's information was called into question in comments, so I researched a more definitive source. The ECMA specification says of constructors:
A constructor is an object that supports the [[Construct]] internal method.
The [[Construct]] internal method is then defined as follows:
Signature: (a List of any, Object) → Object
Description: Creates an object. Invoked via the new or super operators. The first argument to the internal method is a list containing the arguments of the operator. The second argument is the object to which the new operator was initially applied. Objects that implement this internal method are called constructors. A function object is not necessarily a constructor and such non-constructor function objects do not have a [[Construct]] internal method.
The spec states that in describing signatures such as the one above, the following convention is used: "If an internal method explicitly returns a value, its parameter list is followed by the symbol “→” and the type name of the returned value."
So, to summarize, based on the ECMA specification as quoted above, yes, constructor functions definitively do in fact return objects.

Constructor behaving differently using ES6 shorthand notation

ES6 introduced a shorthand notation to initialize objects with functions and properties.
// ES6 shorthand notation
const obj1 = {
a(b) {
console.log("ES6: obj1");
}
};
// ES5
var obj2 = {
a: function a(b) {
console.log("ES5: obj2");
}
};
obj2.a();
obj1.a();
new obj2.a();
new obj1.a();
However, these different notations behave differently, as you can see. If I do new obj1.a() in the browser (tested Chrome and Firefox), I get a TypeError: obj1.a is not a constructor. new obj2.a() behaves completely normally.
What happens here? Does anyone have an explanation, and/or links to documentation/specification?
The specification isn't very direct about explaining this, but we can follow a short chain..
We'll start at EvaluateNew, since that's the behaviour we're wondering about. Step 7 is clearly the one we're looking for here:
If IsConstructor(constructor) is false, throw a TypeError exception.
So IsConstructor is where we need to look next.
Both the summary and the steps describe this:
The abstract operation IsConstructor determines if argument, which must be an ECMAScript language value, is a function object with a [[Construct]] internal method.
If Type(argument) is not Object, return false.
If argument has a [[Construct]] internal method, return true.
Return false.
So, judging by the looks of it, our obj1.a doesn't have a [[Construct]] internal method. Let's look for where it says that it shouldn't have one..
Here's what we're looking for, PropertyDefinitionEvaluation. The first step is useful here:
Let methodDef be DefineMethod of MethodDefinition with argument object.
That calls DefineMethod with just one argument, object. Let's look at DefineMethod - here's what we need:
With parameters object and optional parameter functionPrototype.
If functionPrototype was passed as a parameter, let kind be Normal; otherwise let kind be Method.
Let closure be FunctionCreate(kind, [more arguments snipped]).
Since functionPrototype was not passed as a parameter, the kind is Method. Let's look at what FunctionCreate does with that:
If kind is not Normal, let allocKind be "non-constructor".
Else, let allocKind be "normal".
Let F be FunctionAllocate([other arguments snipped], allocKind).
Now we're getting close! We just need to look at FunctionAllocate does with allocKind (which is "non-constructor" as per the above steps), which is what gives a function all of its internal methods and such.
If functionKind is "normal", let needsConstruct be true.
Else, let needsConstruct be false.
Let F be a newly created ECMAScript function object with the internal slots listed in Table 27. All of those internal slots are initialized to undefined.
If needsConstruct is true, then
a. Set F's [[Construct]] internal method to the definition specified in 9.2.2.
b. Set the [[ConstructorKind]] internal slot of F to "base".
Finally! If we go through the relevant steps, we can see since functionKind isn't "normal", needsConstruct becomes false, and so a [[Construct]] internal method is never assigned! Then IsConstructor sees that and returns false, and so EvaluateNew fails.
MDN describes this behaviour very simply:
All method definitions are not constructors and will throw a TypeError if you try to instantiate them.
..but now you know how they aren't constructors, officially.
Seems the original discussion happens here:
https://github.com/rwaldron/tc39-notes/blob/master/es6/2012-07/july-26.md
MM: three reasons for this:
precedent in builtins
using a method as a constructor is generally nonsense
to freeze a class, I have to freeze the .prototype of the methods on the prototype!!
and
AWB: suggestion: concise methods should be the same for both classes and object literals
strictness
enumerability
constructability
attributes
That's how both class methods and object methods became non-constructable
Methods declared using this syntax are not intended to be constructable
Reference here

why typeof(Function.prototype) is function

I am aware of the fact that Prototypes are object literal. So methods and properties can be defined on them. Function.prototype has some method like apply, call, bind, toString etc. So I thought a function's prototype should be a object literal. But I ran following code and encountered that Function.prototype is of type function !
console.log(typeof(Function.prototype)); // function
How come it is not a object literal itself ?
From the specification:
The Function prototype object is the intrinsic object %FunctionPrototype%. The Function prototype object is itself a built-in function object. When invoked, it accepts any arguments and returns undefined. It does not have a [[Construct]] internal method so it is not a constructor.
NOTE
The Function prototype object is specified to be a function object to ensure compatibility with ECMAScript code that was created prior to the ECMAScript 2015 specification.
(my emphasis)
If we go to the ES5 spec, it says:
The Function prototype object is itself a Function object (its [[Class]] is "Function") that, when invoked, accepts any arguments and returns undefined.
...without offering any explanation for why that would be the case. That language is essentially unchanged in ES1, ES2, ES3, and ES5. I think the original idea was basically that that was what gave it its function-ness, although typeof (even in ES1) didn't look at the internal [[Class]], it looked at whether the thing implemented [[Call]] (as it still does). When something goes back all the way to ES1, one frequently has to just invoke the "because Eich did the first JavaScript in 10 days and yeah, weird stuff happens when you do that" argument. :-)
Side note: By "object literal" I take it you mean "plain object." (An "object literal" — what the specifiation calls an object initializer — is just a way to write an object in source code. There are other ways to create plain objects.)
An object literal is some JavaScript syntax for creating objects. It isn't a data type.
Functions are just a specific type of object in JavaScript. Anywhere you can have an object, you can have a function.
Let's say you have declared an array,
let arr = [ 1 , 2 ];
So, internally it will be created as
let arr = new Array ( 1, 2 );
This is the function constructor.
Have you ever thought about how the array got all functions like concate, map, filter, reduce etc.?
Internally when we create an instance from a function constructor, the prototype property of a function will be set to the prototype property of that newly created instance. Hense, this concate, map, filter, reduce get automatically associated with that function constructor. So that's how we can use that array properties by arr.map, arr.concate.
Actually the prototype property of a function is visible but the prototype property of an instance which is created by a function constructor is hidden. If you want to check then you can check it by obj_name.proto. It's a pointer towards that prototype property.
Now, you can see that the array "arr" is not the array internally. It's an instance of a function constructor. That's why if you check the type of the array, you will get the answer as object and also if you check the typeof(Array), you will get the answer as Function.
If you find it useful then please like it on
https://www.linkedin.com/posts/sh-jay_javascript-array-prototype-activity-6951547190049677312-Dqbn?utm_source=linkedin_share&utm_medium=member_desktop_web
Well, I don't think you mean object literal, as alluded to by other answers and comments.
alert(Function.prototype instanceof Object) // true
alert(Function.prototype instanceof Function) // true
alert(typeof Function.prototype) // function
It is an object. It's also a function. Also, all functions are objects. They're all following the rules just fine.
alert((function(){}) instanceof Object) // true
alert((function(){}) instanceof Function) // true
alert(typeof (function(){})) // function
One big happy we-all-derive-from-Object family. Why should the prototype of Function not be a function?
Now if you wanna get weird... let's get weird.
var notAFn = Object.create(Function.prototype);
alert(notAFn instanceof Function); // true
alert(typeof notAFn); // object
And no, you can't call notAFn(). Not until they add a call Symbol for that. :)
Oh hey, feel free to tell me why this isn't a good answer. I'll try to improve it.

Why {}.toString can check data type?

Can anyone explain a bit about why these codes can check data type? They does not make sense to me. I cannot understand what the codes do behind the scene. Thanks in advance!
var toClass = {}.toString // {} is for what? toString is a method?
alert(toClass); // alert it I get a function? = > function toString() {[native code]}
alert( toClass.call( [1,2] ) )
alert( toClass.call( new Date ) )
var toClass = {}.toString;
is equivalent to
var toClass = Object.prototype.toString;
because
{}
as an expression (Object initialiser) is equivalent to
new Object()
where Object is “the standard built-in constructor with that name” (ECMAScript Language Specification, 5.1 Edition, section 11.1.5; and earlier Editions).
So {} stands in for a reference to a new Object instance. Object instances by default inherit properties from the object initially referred to by Object.prototype through the prototype chain (section 15.2.5), including the built-in property Object.prototype.toString. The property value is initially a reference to a Function instance, i.e. the property is a method that you can call (section 15.2.4.2).
alert is actually window.alert (and should be written so). window is a property of the ECMAScript global object; the object referred to by that property is a host object in the scope chain which has the alert method. Neither of those is specified in ECMAScript, but they are provided by some host environments (usually by browsers) as allowed by the Specification (section 15.1).
Because it is designed to display alert messages, the alert host method displays the string representation of its first argument. For objects, that includes calling their toString or valueOf method, whichever is supported first (section 9.8). Function instances, such as that referred to by {}.toString, inherit themselves a toString method that returns the implementation-dependent representation of the function (section 15.3.4.2).
There are no classes, though, and the ECMAScript concept of [[Class]] is somewhat different from that of "data type" (see typeof and instanceof). These are languages using prototype-based inheritance (section 4.2.1).
This is equivalent to:
Object.prototype.toString.call([1, 2]);
Object.prototype.toString.call(new Date);
Here, Object.prototype.toString is the default toString method that all objects inherit by default. When invoked it prints something like this:
[object XXX]
{}.toString is similar here, because {} is short for new Object().
Derived objects are free to override the toString method to suit their needs, for example:
> [1, 2].toString()
"1,2"
> (new Date).toString()
"Wed Feb 27 2013 17:03:42 GMT+0800 (Malay Peninsula Standard Time)"
However, you can still use the "primitive" method by using .call() on either Object.prototype.toString or {}.toString whereby the first parameter to .call() is used to define what this refers to inside the method you're calling.

Categories

Resources