ES6 类构造函数不能作为普通函数调用的原因是啥?

Posted

技术标签:

【中文标题】ES6 类构造函数不能作为普通函数调用的原因是啥?【英文标题】:What is the reason ES6 class constructors can't be called as normal functions?ES6 类构造函数不能作为普通函数调用的原因是什么? 【发布时间】:2017-11-10 19:32:03 【问题描述】:

ES6 类的构造函数不能作为普通函数调用。根据 ES6,完成此操作后应提高 TypeError。我曾经认为类只是原型中构造函数 + 函数的语法糖,但事实并非如此。

我想知道,这背后的基本原理是什么?除非我错过了什么,否则它会阻止使用自定义 this 调用该函数,这对于某些模式来说可能是可取的。

【问题讨论】:

可能有帮助***.com/questions/30689817/… 这个问题可能是基于意见的,但很可能你不要忘记用 new 来调用它。最好在某个地方的 TC39 讨论论坛上提问。 与几乎所有其他 OOP 语言中它们不能被称为函数的原因相同吗?因为那不是构造函数的用途? 【参考方案1】:

这背后的原因是什么?

这是一种保障。当你在没有new 的情况下调用 ES5 function 构造函数时,它做了非常不受欢迎的事情,默默地失败了。抛出异常有助于您发现错误。

当然,他们本可以选择调用语法与构造函数一样工作,但强制使用 new 关键字是一件好事,有助于我们轻松识别实例化。

它可以防止使用自定义 this 调用函数,这对于某些模式来说可能是可取的。

是的,这就是 ES6 的根本变化。 this 值由超类初始化,它允许子类内置具有内部插槽 - 有关详细信息,请参阅 here。这与传递自定义 this 参数相冲突,为了保持一致性,绝不能允许这样做。

【讨论】:

顺便说一句,使用Reflect.construct 仍然可以实现一些理想的模式。您特别在寻找什么?【参考方案2】:

回顾一下,你的两个要点是

    ES6 类的构造函数不能作为普通函数调用。

    它可以防止使用自定义 this 调用函数

首先要注意的是,从类的运行时行为的角度来看,这些点在功能上并没有联系在一起。例如,您可以在没有 new 的情况下允许 Foo(),但仍然让 Foo.call() 表现得像 newed。作为函数调用的能力可以允许设置this,但不是必须的,就像Foo.bind()()绑定this然后调用函数一样,但是绑定this 将被忽略。

关于这个决定背后的理由,我不能给你一个主要的来源,但我可以告诉你有一个充分的理由。 ES6 类语法是“语法糖”,但不适用于您脑海中可能存在的简化代码。以这个 sn-p 为例,给定你的目标。

class Parent 

class Child extends Parent 
  constructor() 
    // What is "this" here?
    super();
  


Child.call();

这应该怎么做?在 ES6 中,super() 实际设置了this。如果您在调用super() 之前尝试访问this,它将引发异常。您的示例代码可以Base.call() 一起使用,因为它没有父构造函数,因此 this 会预先初始化,但只要您调用子类,this 就不会甚至有一个价值。如果您使用.call,则无法放置该值。

那么接下来的问题是,为什么子类在super() 之前没有得到this?这是因为它允许 ES6 类语法扩展内置类型,如 ArrayErrorMap 以及任何其他内置构造函数类型。在标准 ES5 中这是不可能的,尽管在 ES5 中使用非标准的__proto__ 可以大致模拟。即使使用__proto__,扩展内置类型通常也是一个性能问题。通过在 ES6 类中包含这种行为,JS 引擎可以优化代码,以便扩展内置类型不会影响性能。

所以对于您的问题,是的,它们可以允许Foo.call()Foo(),但它必须忽略this 以允许扩展内置类型。

【讨论】:

【参考方案3】:

对 ES6 规范的重新审视显示如何通过结合第 9.2.9 和 9.2.1 节来禁用调用没有 new 的 Class 函数对象:

9.2.9 MakClassConstructor (F) ... 3. 将 F 的 [[FunctionKind]] 内部槽设置为“classConstructor”。

当指定 [[call]] 方法而不是函数的 [[contruct]] 方法时:

(9.2.1) 2.如果F的[[FunctionKind]]内部槽是“classConstructor”,抛出TypeError异常。

ES5.1 的“11.2.3 函数调用”部分对调用函数没有任何限制。

所以你没有遗漏任何东西:你不能在类构造函数上使用apply

主要的理由可能是使类扩展成为一项相当严格的练习,并检测一些早期形式的错误。例如,您不能调用 Promise 除非作为构造函数 - 并且在调用 Promise 之前省略 new 编程错误。关于扩展类,请注意类实例的 constructor 属性设置正确(可能是多个扩展后的最后一个类),并且类构造函数的 .prototype 属性是只读的 - 您不能动态更改原型用于构造类实例的对象,即使您可以更改构造函数的原型属性。

我曾经认为类是语法糖,但已经脱离了这个概念。

【讨论】:

以上是关于ES6 类构造函数不能作为普通函数调用的原因是啥?的主要内容,如果未能解决你的问题,请参考以下文章

java中的super()是啥

ES6:调用类构造函数而不使用 new 关键字

c++,类的对象作为形参时一定会调用复制构造函数吗?

在ES6类构造函数中工作

如何确保你的构造函数只能被new调用,而不能被普通函数调用?

super staticfinal关键字加深记忆哦!还有父子类构造函数调用问题