Can `new` be optional?
This was covered by "call constructor" proposal, but unfortunately, it was withdrawn.
tc39/ecma262/blob/master/workingdocs/callconstructor.md
Status: "Withdrawn: can be solved with decorators" - tc39/proposals/blob/master/inactive-proposals.md
Is there a technical limitation, or just an opinion?
If I call an ES6 class MyClass {}
without new: MyClass()
, it throws.
Just change this bit to do whatever new
was supposed to do. Problem
solved?
Why can't
new
be optional?
When you call a function, you are using the internal [[Call]] method. When you use the new
operator, it's the internal [[Construct]] method.
They are different things, so IMO avoiding new
when you are instantiating is bad practice.
But if you really want to avoid new
when using ES6 class
syntax, you can use proxies, e.g.
let MyClass = new Proxy(class MyClass {
constructor(arg) { this.arg = arg; }
}, {
apply: (target, thisArg, args) => Reflect.construct(target, args)
});
MyClass(1); // { arg: 1 }
new MyClass(2); // { arg: 2 }
oldie but goldie ?
Object.defineProperty(
Function.prototype,
'new',
{
configurable: true,
value(...args) {
return new this(...args);
}
}
);
On Sun, Nov 5, 2017 at 7:53 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:
oldie but goldie ?
Object.defineProperty( Function.prototype, 'new', { configurable: true, value(...args) { return new this(...args); } } );
doesn't make new optional, just moves it, and doesn't apply to 'class'es
which the OP is saying.
On Sun, Nov 5, 2017 at 11:28 AM, Oriol _ <oriol-bugzilla at hotmail.com> wrote:
Why can't
new
be optional?When you call a function, you are using the internal [[Call]] method. When you use the
new
operator, it's the internal [[Construct]] method.They are different things, so IMO avoiding
new
when you are instantiating is bad practice.
Of course it is... with out new, classes throw an exception.
This seems like a really short sighted issue. Why can't calling a class just invoke it's constructor?
I also don't see how decorators can solve it.
Seems just as arbitrary as the underscore proposal not accepting underscore trailing a number or before or after a decimal.
Maybe, it would be better to ask 'Why does calling a class throw an exception instead of just creating a new instance.
But if you really want to avoid
new
when using ES6class
syntax, youcan use proxies, e.g.
let MyClass = new Proxy(class MyClass { constructor(arg) { this.arg = arg; } }, { apply: (target, thisArg, args) => Reflect.construct(target, args) }); MyClass(1); // { arg: 1 } new MyClass(2); // { arg: 2 }
At the cost of non-zero overhead.
I also don't see how decorators can solve it.
Making to wrapper for class constructor to allow the constructor to callable would be fairly straightforward, e.g.
function callable(cls) {
function Wrapper(...args) {
return Reflect.construct(cls, args, new.target || cls);
}
Object.defineProperty(Wrapper, "name", { value: cls.name });
Object.setPrototypeOf(Wrapper, cls);
Wrapper.prototype = cls.prototype;
return Wrapper;
}
const Example = callable(class Example {});
Example();
and thus potentially
@callable
class Example {}
I know you said "At the cost of non-zero overhead." but it seems like this would be something engines could optimize more easily than proxies.
This seems like a really short sighted issue. Why can't calling a class
just invoke it's constructor?
I think the big question for me when discussing this with people is, is it more surprising to throw a clear error in these case, or to do something close but not quite the same as if it were an ES5-style constructor?
class Foo {
constructor() {
this.foo = 4;
}
}
var obj = {};
var inst = Foo.call(obj);
// what happens?
// obj.foo => 4 or undefined
// inst => obj, new object, or undefined
// inst.foo => 4 or undefined
To be consistent with ES6 class semantics it has to be that a new instance
is returned and obj
is 100% ignored but that are very much not what you'd
get if you replaced the class there with function Foo(){ this.foo = 4; }
.
Isn't that its own set of confusing changes?
Specifically for "This seems like a really short sighted issue" I'd say this is the opposite of short-sighted. There's nothing preventing support for this in the future, but if it got added with a behavior that ended up confusing people more than not having it at all, then we'd be stuck with it for good.
Why does calling a class throw an exception instead of just creating a
new instance
backward compatibility and early "rushed" behavior is one of the reasons.
String() instanceof String
is false and it's just one example.
Because of primitives and typeof
, new Symbol
throws too, you are not
meant to do that.
Array and Regexp though, have no primitive namespace, but that's old gotcha later specs tried to prevent.
What about new Date
? Now that's an instance, while Date()
is a string.
Using new
makes your intent unambiguous in a PL where functions can be
used as constructors too.
There is also the possibility to create functions that cannot be used as
new
:
const method = {method(){}}.method;
new method; // throws
Accordingly, since you have Reflect for all procedural use cases, what is
the benefit of not using new
where it's an instance of that Class you are
after?
Why can't
new
be optional?I found this thread, esdiscuss.org/topic/obsoleting-the-new-keyword, but it's so long (and 9 years old), I didn't read much of it. Can someone summarize the current status of this decision?
On a side note, it seems the ES Steering Committee needs a wiki - a place to document important decisions. I feel like the MDN web docs would be a good place to put all levels of documentation. I'm going to make a new thread about docs, it's so important...
Anyway, in my ES5 "classes", I use an
if (!(this instanceof Class)) return new Class()
(similar to what jQuery does) to avoid having to usenew
. I feel like there should be a way to build this into the language.I have a
View()
class that is invoked a lot:And, being forced to write
new View()
every time is actually the only reason why I won't switch to native classes, at the moment.