To recap, your two main points are
ES6 class constructors can't be called as normal functions.
It prevents calling the function with a custom this
The first thing to note is that from the standpoint of the runtime behavior of a class, those are to points are not functionally tied together. You could for instance allow Foo()
without new
but still have Foo.call({})
behave like it had been new
ed. The ability to call as a function can allow setting this
, but it doesn't have to, the same way Foo.bind({})()
would bind a this
and then call the function, but the bound this
would be ignored.
For the rationale behind the decision, I can't give you a primary source, but I can tell you there is one solid reason. ES6 class syntax is "syntax sugar", but not for the simplified code you likely have in your head. Take for example this snippet, given your goal.
class Parent {}
class Child extends Parent {
constructor() {
// What is "this" here?
super();
}
}
Child.call({});
What should this do? In ES6, super()
is what actually sets this
. If you try to access this
before having called super()
, it will throw an exception. Your example code could work with Base.call({})
since it has no parent constructor, so this
is initialized up front, but as soon as you're calling a child class, this
doesn't even have a value up front. If you use .call
there is no where to put that value.
So then the next question is, why do child classes not get this
before super()
? This is because it allows ES6 class syntax to extend builtin types like Array
and Error
and Map
and any other builtin constructor type. In standard ES5 this was impossible, though with the non-standard __proto__
in ES5 it could be simulated roughly. Even with __proto__
it is generally a performance issue to extend builtin types. By including this behavior in ES6 classes, JS engines can optimize the code so that extending builtin types works without a performance hit.
So for your questions, yes, they could allow Foo.call()
, or Foo()
, but it would have to ignore this
either way in order to allow for extending builtin types.