Search code examples
javascriptevalprototype-programming

Remove the use of eval function from code


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?


Solution

  • 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(..).