构造函数使用 ES6 速记符号表现不同 [英] Constructor behaving differently using ES6 shorthand notation
问题描述
ES6 引入了速记符号 用函数和属性初始化对象.
//ES6 速记符号常量对象 1 = {一(二){console.log("ES6: obj1");}};//ES5var obj2 = {a: 函数 a(b) {console.log("ES5: obj2");}};obj2.a();obj1.a();新的 obj2.a();new obj1.a();
但是,如您所见,这些不同的符号表现不同.如果我在浏览器中执行 new obj1.a()
(经过测试的 Chrome 和 Firefox),我会得到一个 TypeError: obj1.a is not a constructor
.new obj2.a()
完全正常.
这里发生了什么?有没有人有解释和/或文档/规范的链接?
规范 不是很直接地解释这一点,但我们可以遵循一个短链..
我们将从 EvaluateNew 开始,因为那是我们想知道的行为.第 7 步显然是我们在这里寻找的:
<块引用>- 如果 IsConstructor(constructor) 为 false,则抛出 TypeError 异常.
所以 IsConstructor 是我们接下来需要关注的地方.
摘要和步骤都描述了这一点:
<块引用>抽象操作 IsConstructor 确定 argument(必须是 ECMAScript 语言值)是否是具有 [[Construct]] 内部方法的函数对象.
<小时>- 如果 Type(argument) 不是 Object,则返回 false.
- 如果argument有一个[[Construct]]内部方法,则返回true.
- 返回 false.
所以,从它的外观来看,我们的 obj1.a
没有 [[Construct]] 内部方法.让我们看看它说它不应该有的地方..
这是我们正在寻找的内容,PropertyDefinitionEvaluation.第一步在这里很有用:
<块引用>让methodDef 成为MethodDefinition 的DefineMethod 参数object.
这仅调用 DefineMethod一个参数,对象.让我们看看DefineMethod - 这是我们需要的:
<块引用>带参数对象和可选参数函数Prototype.
<小时>- 如果 functionPrototype 作为参数传递,让 kind 为
Normal
;否则让 kind 成为Method
. - 让 closure 成为 FunctionCreate(kind, [截断更多参数]).
由于functionPrototype没有作为参数传递,所以类型是Method
.让我们看看 FunctionCreate 对此做了什么:
- 如果kind不是
Normal
,让allocKind成为非构造函数"
. - 否则,让 allocKind 成为
normal"
. - 设 F 为 FunctionAllocate([截取的其他参数], allocKind).
现在我们越来越近了!我们只需要看看 FunctionAllocate 对 的作用allocKind(按照上述步骤是 non-constructor"
),这就是为函数提供其所有内部方法等的原因.
- 如果 functionKind 是
"normal"
,让 needsConstruct 为 true. - 否则,让 needsConstruct 为 false.
<小时>
- 让 F 成为一个新创建的 ECMAScript 函数对象,其内部插槽列在 表 27.所有这些内部插槽都初始化为未定义.
<小时>
如果 needsConstruct 是 true,则
一个.将 F 的 [[Construct]] 内部方法设置为 9.2.2 中指定的定义.
B.将F的[[ConstructorKind]]内部槽设置为
"base"
.
终于!如果我们通过相关步骤,我们可以看到由于 functionKind 不是 "normal"
,needsConstruct 变为 false,因此永远不会分配 [[Construct]] 内部方法!然后 IsConstructor 看到并返回 false,因此 EvaluateNew 失败.
MDN 非常简单地描述了这种行为:
<块引用>所有方法定义都不是构造函数,如果您尝试实例化它们,将抛出 TypeError.
..但现在你知道如何他们不是构造函数,正式的.
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 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.
这篇关于构造函数使用 ES6 速记符号表现不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!