Can `new` be optional?

# Michael Lewis (7 years ago)

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 use new. I feel like there should be a way to build this into the language.

I have a View() class that is invoked a lot:

View().append({
    one: View(...),
    two: View(View(...), View(...))
});

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.

# Michał Wadas (7 years ago)

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

# Michael Lewis (7 years ago)

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?

# Oriol _ (7 years ago)

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 }
# Andrea Giammarchi (7 years ago)

oldie but goldie ?

Object.defineProperty(
  Function.prototype,
  'new',
  {
    configurable: true,
    value(...args) {
      return new this(...args);
    }
  }
);
# J Decker (7 years ago)

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 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 }

At the cost of non-zero overhead.

# Logan Smyth (7 years ago)

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.

# Andrea Giammarchi (7 years ago)

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?