Nonconstructors

# Raul-Sebastian Mihăilă (7 years ago)

I have a dilemma. I like how typically the built-in methods are not constructors (like Array.prototype.forEach). I have cases in which I'm creating a function in which I want to use this but I would like the function to not be constructible. There are ways of doing this:

  • checking new.target - but you have to check and the last time I checked uglifyjs was failing because of it.
  • ({function() { ... }}).function - but this requires creating an object every time, so it's ugly.

It would probably be too much to add a new kind of function definition, but I was wondering if anybody ever cared about this kind of things. For instance, would people usually expect frameworks/libaries or Javascript itself to not make functions constructible if they're not meant to be?

# Tab Atkins Jr. (7 years ago)

On Mon, Apr 24, 2017 at 1:42 PM, Raul-Sebastian Mihăilă <raul.mihaila at gmail.com> wrote:

I have a dilemma. I like how typically the built-in methods are not constructors (like Array.prototype.forEach). I have cases in which I'm creating a function in which I want to use this but I would like the function to not be constructible. There are ways of doing this:

  • checking new.target - but you have to check and the last time I checked uglifyjs was failing because of it.
  • ({function() { ... }}).function - but this requires creating an object every time, so it's ugly.

It would probably be too much to add a new kind of function definition, but I was wondering if anybody ever cared about this kind of things. For instance, would people usually expect frameworks/libaries or Javascript itself to not make functions constructible if they're not meant to be?

The obvious question is, why do you want to use this?

# Michał Wadas (7 years ago)

You can use:

const foo = { bar() { // Not constructible } }; new foo.bar; //TypeError: foo.bar is not a constructor

# J Decker (7 years ago)

function Entity() { if (this instanceof Entity) throw new Error("Please do not call with new"); }

oh the other side; you can bind this to a simple function call...

function f() { } var fWithThis = f.bind( {} /*Something to use as 'this' */ )

call with fWithThis()

and f's this will be the object you passed to bind.

# Jordan Harband (7 years ago)

Arrow functions are not constructible; just use one of those.

# T.J. Crowder (7 years ago)

On Tue, Apr 25, 2017 at 6:19 AM, Jordan Harband <ljharb at gmail.com> wrote:

Arrow functions are not constructible; just use one of those.

He said he wanted to use this within it (I'm assuming in a non-closed-over way)...

-- T.J. Crowder

# Raul-Sebastian Mihăilă (7 years ago)

On Tue, Apr 25, 2017 at 12:15 AM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:

The obvious question is, why do you want to use this?

~TJ

For cases such as a debounce function:

  const debounce = (func, delay) => {
    let timeout;

    return function (...args) {
      clearTimeout(timeout);

      timeout = setTimeout(() => {
        func.apply(this, args);
      }, delay);
    };
  };

If func is using this the resulted function (returned by debounce) should pass its this. Another case is related to form fields. Let's say a form field is an object with a validate method. I provide a createValidator function to the user that accepts multiple functions in order to call them 1 by 1 until one of them returns an error. The result of createValidator is a function that then becomes the value of the field's validate method (which the user will call in order to validate the field). So the function will want to use this because it will forward it to the functions provided by the user. This is expected because the function will be called as a method of the field object.

# Tab Atkins Jr. (7 years ago)

On Mon, Apr 24, 2017 at 10:53 PM, Raul-Sebastian Mihăilă <raul.mihaila at gmail.com> wrote:

On Tue, Apr 25, 2017 at 12:15 AM, Tab Atkins Jr. <jackalmage at gmail.com> wrote:

The obvious question is, why do you want to use this?

~TJ

For cases such as a debounce function:

  const debounce = (func, delay) => {
    let timeout;

    return function (...args) {
      clearTimeout(timeout);

      timeout = setTimeout(() => {
        func.apply(this, args);
      }, delay);
    };
  };

If func is using this the resulted function (returned by debounce) should pass its this. Another case is related to form fields. Let's say a form field is an object with a validate method. I provide a createValidator function to the user that accepts multiple functions in order to call them 1 by 1 until one of them returns an error. The result of createValidator is a function that then becomes the value of the field's validate method (which the user will call in order to validate the field). So the function will want to use this because it will forward it to the functions provided by the user. This is expected because the function will be called as a method of the field object.

Ah, these use-cases are reasonable.

In that case, then, yeah, checking new.target seems to be the way you want to go. That's explicitly what it was defined to do:

if(new.target) throw "Foo() must not be called with new";

There's no shorter way to do this; all the shorter methods rely on the function clearly existing as a method, syntactically.