One of my favorite pieces of code to use is:
if (!(eval("this instanceof "+arguments.callee.name)))
return eval("new "+arguments.callee.name+"(...arguments)");
This piece of code allows function prototypes to create themselves when set like getting the result of themselves as a regular function. (Removes the requirement to type 'new' for function prototypes)
However, that it uses eval. Is there any way to remove eval from it?
Also, is there a way to shorten it further?
Yes, there's no need for eval
there. Here's how you'd do it in modern environments (ES2015+):
function Example(...args) {
if (!new.target) {
return new Example(...args);
}
// ...use `args` here, possibly destructuring to local meaningful names e.g.:
const [first, second] = args;
// ...
}
That avoids using eval
, the arguments
pseudo-array, and also arguments.callee
, which should never be used and which is disallowed in strict mode. (Strict mode is the default in modules and classes and will be the default in any further new scopes added to the language.)
You could keep using the arguments
pseudo array if you like:
function Example(first, second) {
if (!new.target) {
return new Example(...arguments);
}
// ...
}
FWIW, I strongly recommend not making a function dual-use like that. Instead, consider using class
syntax and a create
function for when you don't want to use new
:
class Example {
constructor(biz, baz) {
// ...
}
static create(...args) {
return new Example(...args); // Or `return new this(...args);` or a couple of other choices.
}
}
If you try to do Example(1, 2)
, an error is automatically thrown because class
constructors can't be called as though they were normal functions. You can avoid new
by using Example.create(1, 2)
instead.
The version of create
that uses this
avoids explicitly naming the constructor:
static create(...args) {
return new this(...args);
}
That works because when you do Example.create(1, 2)
, during the call to create
this
refers to Example.
But it won't work if you do pass Example.create
around without ensuring it's bound. For instance, const create = Example.create; create(1, 2);
would fail with new this(...)
but would work with new Example(..)
.