2

Is there a way to prevent a new defined function to be called as constructor in JavaScript? Like throw out error when placing new before calling it.

Thomson
  • 20,586
  • 28
  • 90
  • 134
  • 1
    Just so you know, the solutions given below (except for the arrow function) are fragile in that they rely on the variable scope or prototype chains being unaltered. –  Oct 16 '16 at 19:56
  • 3
    ECMAScript 6 has a specific tool for this. You can use [`new.target`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target) in a function to determine if `new` was used to invoke it. `console.log(new.target)` will show `undefined` if `new` was not used, otherwise you get a reference to the function itself. –  Oct 16 '16 at 19:58

3 Answers3

3

You can check if a function was called with new by seeing if it is an instance of itself

function test() {
    if ( this instanceof test ) {
        throw "Oh God no, not a new one !"
    }
}

var a = test(); // success
var b = new test(); // fail

In ES2015 the new.target meta property lets you check directly if new was used to call the method

function test() {
    if (new.target) {
        throw "Oh God no, not a new one !"
    }
}
adeneo
  • 312,895
  • 29
  • 395
  • 388
2

You could check this.constructor inside the function:

function f() {
  if (this && this.constructor === f) {
    throw new Error("Don't call f as a constructor")
  }
}

f();     // ok
new f(); // throws

As @squint points out, it is possible to break this check if f.prototype.constructor is set explicitly:

f.prototype = {
  constructor: "not f!"
}

new f(); // doesn't throw 
joews
  • 29,767
  • 10
  • 79
  • 91
  • `f === g` in the second example, so why not use `f`? That also protects it from outside mutation when `var` or `let` are used. –  Oct 16 '16 at 19:49
  • You're right, I forgot that named function expressions bind the function to its name inside the function body. – joews Oct 16 '16 at 19:51
1

If your environment supports them, you could use an arrow function:

const f = () => "test"
f()       // returns "test"
new f()   // throws TypeError: f is not a constructor(…)

Note that arrow functions inherit this from the scope that they are defined in. That's different from function (where this usually depends on how the function is called), so you can't always use them as a drop-in replacement.

Babel currently does not throw an Exception if an arrow function is called as a constructor: Babel REPL example.

Arrow function compatibility table

joews
  • 29,767
  • 10
  • 79
  • 91