Is there any way to prevent the use of "new" in a Javascript function?

Asked

Viewed 155 times

6

Assuming the function:

function foo() {
  return 'something';
}

It is possible to invoke it (abstract operation [[Call]]):

// Invocando `foo`:
foo();

In the above example, the string would be returned something.

But it is also possible to use it as a constructor (abstract operation [[Construct]]):

// Construindo um novo objeto `foo`:
new foo();

In the above example, how foo was not created for the purpose of act as constructor, an "empty" object (of the "type" foo) is returned.


There are cases like this where a function should not be used as a builder, so I wonder if it is possible to prevent the use of new.

I know that in some cases can be useful (especially when class did not exist), but would like to know if you can "block" (i.e. throw error) when trying to use new in foo? I mean, I just want the invocation to be allowed.

It is possible?

  • from the point of view of javascript prototyping is even interesting but... in which real case would someone want to need it? I confess I’ve never seen anyone try to use a Function (which exists only as a "method") as an instance, and I’ve seen a lot of code in my life :)

  • I also maintain your inquiry, @Ricardopunctual, in fact is a use case unusual. Anyway, I don’t think it’s appropriate to think "where it could be used" (because it’s impossible to list this kind of possibility), but yes show the possibility.

  • I understand, I just wanted to know if there are any use cases for this, but as I said, as knowledge is interesting, but it would be more if I had some practical example :)

  • 2

    @Ricardopunctual, there is some use. See the example of this post on Soen. It talks about the difference in using let d = new Date(); and let d = Date(); in the first case an object is returned Date and in the second case a String with the current date.

2 answers

9


An alternative is to check the this. You can make a if to check whether the this is an instance of the function itself.

Something like:

function foo() {
  if (this instanceof foo) {
    throw new Error('Cannot be used as a constructor.');
  }

  return 'something';
}

console.log(foo()); // "something"
console.log(new foo()); // Error: Cannot be used as a constructor.

I saw a similar example in a functional programming code in Javascript, in which the function could only be invoked, never instantiated.

7

An alternative is to use the syntactic construction new.target, refers to the constructor function when the function is called using the operator new.

Thus, if new.target is defined, it is inferred that the function was called through instantiation with new - if we wish to prohibit:

function foo() {
  if (new.target) {
    throw new Error('Cannot be used as a constructor.');
  }

  return 'something';
}

console.log(foo()); //=> "something"
console.log(new foo()); // Error: Cannot be used as constructor.


About new.target

The syntactic construction new.target includes a reference to the construction function when it is instantiated using the operator new.

Syntactically, target seems to be a property of a supposed object new. However, how new is an operator (and not an object), it is said that new.target is a syntactic construction or pseudo-property.

According to the documentation, when a function is instantiated using the operator new (abstract operation [[Construct]]), to pseudo-property new.target refer to the constructor function. On the other hand, when the function is called normally (abstract operation [[Call]]), new.target will be undefined.

Examples:

function foo(msg) {
  console.log(msg + ':', new.target);
}

class Bar {
  constructor(msg) {
    console.log(msg + ':', new.target);
  }
}

foo('Sem new'); //=> undefined
new foo('Com new'); //=> Refere-se à função `foo` (construtor)
new Bar('Classe com new'); //=> Refere-se à classe `Bar` (construtor)

For more details on new.target, refer to the documentation.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.