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()
表现得像 new
ed。作为函数调用的能力可以允许设置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 类语法扩展内置类型,如 Array
和 Error
和 Map
以及任何其他内置构造函数类型。在标准 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 类构造函数不能作为普通函数调用的原因是啥?的主要内容,如果未能解决你的问题,请参考以下文章