问题
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?
回答1:
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 beMethod
.- 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.
回答2:
Methods declared using this syntax are not intended to be constructable
Reference here
回答3:
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
来源:https://stackoverflow.com/questions/41193117/constructor-behaving-differently-using-es6-shorthand-notation