使用ES6简写符号的构造函数行为不同

12
ES6引入了一种简写符号来初始化带有函数和属性的对象。

// 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();

然而,这些不同的符号行为不同,如您所见。如果我在浏览器中执行new obj1.a()(经测试适用于Chrome和Firefox),我会得到一个TypeError:obj1.a不是构造函数new obj2.a() 表现完全正常。
发生了什么?有人有解释和/或文档说明的链接吗?

methods(使用类或对象字面量中的方法定义)和箭头函数不能与new一起使用。您必须使用老式的functionclass构造函数。 - Bergi
在ES6之前,函数和构造函数都混在一起。ES6试图将它们区分开来。 - Oriol
3个回答

12

这个规范没有直接解释这个问题,但我们可以遵循一条短链。

我们从EvaluateNew开始,因为这是我们想知道的行为。第七步显然就是我们在这里要寻找的:

  1. 如果IsConstructor(constructor)是false,则抛出一个TypeError异常。

所以下一步我们需要查看的是IsConstructor

摘要和步骤都描述了这个操作:

抽象操作IsConstructor用于确定argument(必须是ECMAScript语言值)是否是具有[[Construct]]内部方法的函数对象。


  1. 如果Type(argument)不是Object,则返回false
  2. 如果argument具有[[Construct]]内部方法,则返回true
  3. 返回false

看起来,根据这个操作,我们的obj1.a没有[[Construct]]内部方法。让我们找到它不应该有一个[[Construct]]内部方法的地方。

这就是我们需要查找的地方PropertyDefinitionEvaluation。第一步在这里很有用:

让methodDef成为带有object参数的MethodDefinition的DefineMethod。

这会调用只有一个参数 objectDefineMethod。我们来看一下 DefineMethod - 这是我们需要的:

具有参数对象和可选参数函数原型。


  1. 如果函数原型被作为参数传递,让 kind 为 Normal;否则让 kind 为 Method。
  2. 让闭包(closure)成为 FunctionCreate(kind, [更多参数省略])。

由于函数原型未作为参数传递,kind 是 Method。我们来看一下 FunctionCreate 对此执行的操作:

  1. 如果 kind 不是 Normal,则让 allocKind 为 "non-constructor"。
  2. 否则,让 allocKind 为 "normal"。
  3. 让 F 成为 FunctionAllocate([其他参数省略], allocKind)。

现在我们接近了!我们只需要看一下 FunctionAllocate 对 allocKind(根据上述步骤为 "non-constructor")执行的操作,这是赋予函数所有内部方法和其他内容的关键。

  1. 如果 functionKind"normal",则将 needsConstruct 设为 true
  2. 否则,将 needsConstruct 设为 false

  3. 使用 表 27 中列出的内部插槽创建新的ECMAScript函数对象F。所有这些内部插槽都初始化为undefined

  4. 如果 needsConstructtrue,则执行以下操作:

    a. 将 F 的 [[Construct]] 内部方法设置为 9.2.2 中指定的定义。

    b. 将 F 的 [[ConstructorKind]] 内部插槽设置为 "base"

终于!如果我们遵循相关步骤,我们可以看到由于 functionKind 不是 "normal"needsConstruct 变为 false,因此永远不会分配[[Construct]]内部方法!然后IsConstructor看到了这一点,并返回false,因此EvaluateNew失败。

MDN非常简单地描述了这种行为:

 

所有方法定义都不是构造函数,如果您尝试实例化它们,则会抛出TypeError。

..但现在你知道了它们如何非正式地成为构造函数。


2
看起来最初的讨论发生在这里:https://github.com/rwaldron/tc39-notes/blob/master/es6/2012-07/july-26.md MM提出了三个原因:
1.内置对象中的先例。 2.将方法用作构造函数通常是无意义的。 3.要冻结一个类,必须冻结原型上的方法的原型!!
AWB建议:简洁的方法应该对类和对象字面量都一样。
1.严格性 2.可枚举性 3.可构造性 4.属性
这就是为什么类方法和对象方法都变成了不可构造的原因。

1

规范在哪里说了这个? - Michał Perłakowski
所有的方法定义都不是构造函数,如果您尝试实例化它们,将会抛出 TypeError 异常。 - williamsandonz
MDN不是规范。 - Michał Perłakowski
你是唯一一个使用“规范”这个词的伙计。 - williamsandonz
1
不,原帖作者也使用了它。 - Michał Perłakowski
@Gothdo,基本上是这样的:如果类型不是Normal,那么让allocKind成为“非构造函数”。如果你从“对象字面量”部分一直追溯到“函数创建”部分,你会发现kind是方法而不是normal - artem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接