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
Related
I have been trying to learn about prototypes since I needed quite a bit of clarity on them. I have been using this MDN article and its related articles for reference.
After some reading, I got a bit of clarity on prototypes and tried putting them in my own words and would like to know if it is right and would love to know if and where I am wrong
START OF EXPLANATION
Every object is created using a constructor function of some sort. Say, we create an object as follows.
let obj = new Object();
Here, Object is the constructor function. The thing about functions is that, all of them (including non-constructor functions) have a property called prototype on them. This prototype property defines what will be the prototype of any object that is created using the new keyword and said constructor function. You can check the prototype property as follows:
console.log(Object.prototype);
The above piece of code will return an object with a bunch of methods that any object created using new Object() can use.
In the above example, if the wording is too confusing, you can replace all occurrences of Object with any other constructor function such as Array, Date or even custom constructor functions such as Person or something else that you may have defined.
END OF EXPLANATION
Is my understanding right? If not, can you point me where I went wrong?
Is my understanding right? If not, can you point me where I went wrong?
In big picture terms, yes, your understanding is mostly correct, but the explanation is incomplete, and there are some specifics that are incorrect.
Every object is created using a constructor function of some sort.
That's not quite correct, JavaScript also has literal forms ({} [object], [] [array], and // [regular expression]) that create objects without using a constructor function. Those forms assign Object.prototype, Array.prototype, and RegExp.prototype (respectively) to the objects they create, even though the constructor itself isn't invoked.
There are also other ways of creating objects that don't go through constructor functions at all. For instance, there's Object.create, which creates an object and assigns the prototype you supply to it:
const p = {};
const obj = Object.create(p);
console.log(Object.getPrototypeOf(obj) === p); // true
(There are also more obscure ways of creating objects through implicit conversion.) You can also change the prototype of an existing object by using Object.setPrototypeOf.
The thing about functions is that, all of them (including non-constructor functions) have a property called prototype on them.
Not quite, arrow functions and class methods do not have a prototype property and cannot be used as constructors:
const arrow = () => {};
class X {
method() {
}
static staticMethod() {
}
}
console.log("prototype" in arrow); // false
console.log("prototype" in X.prototype.method); // false
console.log("prototype" in X.staticMethod); // false
This prototype property defines what will be the prototype of any object that is created using the new keyword and said constructor function.
Correct. (Constructor functions can mess with what they return, but that's the usual, standard behavior.)
At this point in an explanation I'd probably point out the distinction between the prototype property on functions and the prototype of an object. Beginners sometimes think setting a prototype property on an object will change its prototype; it doesn't, that name is only significant on functions, and it's not the function's prototype, it's just a property that (as you said) will be used to assign the prototype of an object created using new with that function. The prototype of an object is held in an internal field of the object called [[Prototype]]. That field isn't directly accessible, but you can access it via Object.getPrototypeOf and change it via Object.setPrototypeOf (you can also use the deprecated __proto__ accessor property, which is just a wrapper around those functions — but don't use __proto__, use the functions directly).
But aside from all that, there's a big unanswered question in your explanation: What are prototypes for? What do they do? Why have them?
The answer is that they provide JavaScript's inheritance mechanism. When you get the value of a property on an object and the object doesn't have a property of its own with the given key, the JavaScript engine looks at the object's prototype to see if it has the property (and the prototype of the prototype, and so on through the chain):
const parent = {
a: "a property on base",
};
const child = Object.create(parent);
child.b = "a property on child";
const grandChild = Object.create(child);
grandChild.c = "a property on grandChild";
console.log(grandChild.a); // "a property on base"
console.log(grandChild.b); // "a property on child"
console.log(grandChild.c); // "a property on grandChild"
const hasOwn =
Object.hasOwn || // Fairly new, ES2022
Function.prototype.call.bind(Object.prototype.hasOwnProperty);
console.log(`hasOwn(grandChild, "a")? ${hasOwn(grandChild, "a")}`); // false
console.log(`hasOwn(grandChild, "b")? ${hasOwn(grandChild, "b")}`); // false
console.log(`hasOwn(grandChild, "c")? ${hasOwn(grandChild, "c")}`); // true
Those example property values are strings, but this is widely used where the property values are functions, providing a means of inheriting methods from parent objects.
The property access process is asymmetrical, though; it only works as described above for getting a property's value. If you set a property's value on an object, it always sets it on the object itself, not on its prototype:
const parent = {
prop: "parent",
};
const child = Object.create(parent);
const hasOwn =
Object.hasOwn || // Fairly new, ES2022
Function.prototype.call.bind(Object.prototype.hasOwnProperty);
console.log(`[Before] child.prop: ${child.prop}`);
// => "[Before] child.prop: parent"
console.log(`[Before] hasOwn(child, "prop")? ${hasOwn(child, "prop")}`);
// => "[Before] hasOwn(child, "prop")? false"
child.prop = "child";
console.log(`[After] child.prop: ${child.prop}`);
// => "child.prop: child"
console.log(`[After] hasOwn(child, "prop")? ${hasOwn(child, "prop")}`);
// => "[After] hasOwn(child, "prop")? true"
(This difference between getting and setting the property value applies to data properties [the kind we mostly create]; accessor properties work differently because getting and setting the property result in function calls, and the accessor's setter function can do whatever the author wants it to do.)
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.
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.
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.
Please help me to understand why the following code works:
<script>
var re = RegExp('\\ba\\b') ;
alert(re.test('a')) ;
alert(re.test('ab')) ;
</script>
In the first line there is no new operator.
As far as I know, a contructor in JavaScript is a function that initialize objects created by the operator new and they are not meant to return anything.
In general, if something is documented as being a constructor, use new with it. But in this case, RegExp has a defined "factory" behavior for the situation where you've called it as a function instead. See Section 15.10.3 of the ECMAScript (JavaScript) specification (that links to the outgoing spec; the section number is the same in the new spec, which you can download from the ECMA front page [on the right-hand side]; I don't want to directly link to a ~4MB PDF file):
15.10.3 The RegExp Constructor Called as a Function
15.10.3.1 RegExp(pattern, flags)
If pattern is an object R whose [[Class]] property is "RegExp" and flags is undefined, then return R unchanged. Otherwise call the RegExp constructor (15.10.4.1), passing it the pattern and flags arguments and return the object constructed by that constructor.
You can actually define your own JavaScript constructor functions to allow omitting the new keyword (by detecting they've been called as a function instead, and turning around and calling themselves correctly), but I wouldn't suggest it as it leads to misleading code. (And you can't do it with class syntax, you have to use the older, clunkier function syntax.)
+1 TJ Crowder has it. The ECMAScript standard goes out of its way to define behaviours for the built-in constructor functions when called as plain functions. Often it simply calls itself back as a constructor, but there are some more complicated cases.
constructors in javascript [...] are not meant to return anything
In general, a constructor can ignore this and just return an independent object:
function Thing() {
return {'foo': 1};
}
in this case you can equally use the function as a constructor (with new) or a plain function.
If the constructor doesn't return anything, as is the usual pattern for constructors, the new operator itself ensures that it returns the new object created and passed as this. In this case you must use new.
It's best not to rely on a constructor working as a bare function, and the alternative behaviours of the built-in constructors are rarely of any use, so generally you should stick with new.