JS Tip of the Day: New Functions With new

New Functions With new
Version: ES2015 (generator), ES2017 (async)
Level: Advanced

Just about any time you create a new function in JavaScript, you’re more than likely going to be using some kind of function declaration or expression to do so. But functions, being objects, also have a constructor. And that constructor can be used to create new functions, just like any other object constructor can be used to create new objects.

The Function constructor is called using new Function() (or just Function()) and takes a variable number of arguments. The last argument is a string representing the code in the body of the function while every other argument before that represents its parameter names.

let logger = new Function('value', 'console.log("[LOG] " + value)');
logger('is wood'); // [LOG] is wood

Functions created with the Function constructor are similar to function expressions created with the function keyword. One noticeable difference is that code within the Function constructor does not have access to any outside scope other than global. While this can be limiting, it can also make it a safer alternative to eval since it doesn’t expose variables in other scopes.

function runSecretTest () {
    let local = 'secret';

    function normal () {
        console.log('Normal: ' + local);
    }
    function evaled () {
        eval('console.log("Eval: " + local)');
    }
    let constructed = new Function(
        'console.log("Constructor: " + local)'
    );

    normal(); // Normal: secret
    evaled(); // Eval: secret
    constructed(); // ReferenceError: local is not defined
}

runSecretTest();

Unique function constructors also exist for generator and async functions (and async generator functions). Unfortunately, these are not exposed globally. To get access to these constructors, you’d need to make a generator or async function first, then access their constructor properties.

let GeneratorFunction = (function * () {}).constructor;
let count3 = new GeneratorFunction(
    'yield 1; yield 2; yield 3;'
);
console.log([...count3()]); // [1, 2, 3]

let AsyncFunction = (async function () {}).constructor;
let delay3 = new AsyncFunction(
    'return 3'
);
delay3()
    .then(value => console.log(value)); // 3

There is no constructor for arrow functions. If you attempt to access constructor from an arrow function, it will refer to the standard Function constructor. The only way to create an arrow function with arrow function-like behavior is using arrow function syntax.

let arrow = () => {};
console.log(arrow.constructor === Function); // true

More info: