The beauty of non-nameless functions in javascript

    image


    Anonymous switch functions in JavaScript , according to some polls, are the most popular feature of ES-2015, which is also emphasized by the exhaustive number of tutorials on the Internet. They are undoubtedly very useful, but in this short article we will look at examples of the use of neglected, yet remarkable, expressions with named functions — NFE.


    Short help


    Named Function Expression is an extension of functional expressions in JavaScript that allows you to name a function created as part of an expression ( FunctionExpression ):


    let fe = functionnamed(...) { /* function body */ };

    The whole point is that from the inside of the function referenced by the variable fe , there is access to the function itself through the name named . Only from inside the function and the name cannot be overwritten!


    How can this be used


    For example, we need to write a decoration function that will count the number of calls of a function. This can be done very elegantly with the help of NFE:


    const count = f =>functiondf() {
        df.calls = (df.calls || 0) + 1;
        return f(...arguments);
    };

    Here we have access to the returned function df , in order to call it, store the counter in the calls property , which will be readable if necessary:


    const csum = count((x, y) => x + y);
    csum(5, 10); // 15
    csum(50, 1); // 51
    csum.calls; // 2

    Or we save all the results of the function call:


    const accum = f =>functiondf() {
        let res = f(...arguments);
        (df.results = (df.results || [])).push(res);
        return res;
    };

    In any case, we use the fact that the function in JavaScript is an object, and we can supplement it with the necessary properties in our local tasks.


    Obviously, the function we can call. Using NFE for recursion is especially nice. Suppose we want to find the n-th member of the Fibonacci sequence (for some reason everyone wants it sooner or later):


    const rf = functiongetfn(n) {
        return n > 2 ? getfn(n - 2) + getfn(n - 1) : 1;
    };
    rf(1); // 1
    rf(10); // 55

    You should not pay attention to the "lack of tail". But the opportunity to do the same through FunctionDeclaration - yes. But the expression function has one advantage: if you want to transfer a function from rf to another variable, you do not need to edit the recursive call inside.


    A neat example of a timer implementation:


    const ping = (callback, t) => setTimeout(functionpf() {
        callback();
        setTimeout(pf, t);
    }, t);

    Here, we use NFE as a literal as an argument instead of declaring an unnecessary variable. Why not setInterval ? Instead of a callback, there may be an awaiting rezolv promise before the next timer tick.


    An interesting example would be the combination of NFE and IIFE ( Immediately-Invoked Function Expression ), when in place only the result of a recursive function is needed. Only once:


    let data = {
        f10: functionfact(n) {
            return n > 1 ? n * fact(n - 1) : 1;
        }(10)
    };
    data.f10; // 3628800

    What about yourself? well, here's the real problem: There is some strictly increasing function f that acts in the set of natural numbers. Find the extreme point x at which the value of the function does not exceed the given y . An example of such a function can be f(x) = 3 * x + 5or f(x) = 2^x + 11.


    const find = (f, y) =>functionbs(a, b) {
        if (a + 1 === b) return a;
        let m = Math.ceil((a + b) / 2);
        return f(m) <= y ? bs(m, b) : bs(a, m);
    }(-1, y + 1);
    find(x =>3 * x + 5, 200); // 65
    find(x =>Math.pow(2, x) + 11, 1000); // 9

    We will not go into the mathematical details. Consider the implementation:


    1. The required find function has two of our parameters, f, y, and returns the result IIFE.
    2. Immediately-called function implements a binary search for a solution, the first call receives the initial range.
    3. The search itself is implemented through recursion, which uses the name NFE for subsequent calls. We do not declare the search function and do not create a new variable.

    Of course, a similar problem can be solved through a simple cycle with a precondition, but this is too imperative for console coding.


    Finally a couple of words about stack tracing. We have some NFE-function, in the body of which an exception was thrown:


    const fe = functionnamed() {
        thrownewError('Something went wrong');
    };

    Since we have a named function in the expression, we will see it in the stack trace:


    UncaughtError: Somethingwentwrongatnamed (<anonymous>:2:11)

    However, starting with ES-2015, many anonymous function expressions actually create a function with a name, taking it out of context:


    const unnamed = function() {
        thrownewError('Something went wrong');
    };

    The function on the right side of the expression is associated with the variable on the left:


    UncaughtError: Somethingwentwrongatunnamed (<anonymous>:2:7)

    But it is not always possible. A classic example is the initialization of an external library script via IIFE, as an option:


    /**
     * @license
     * @description
     */
    ;(function() {
        // some awesome code
    }.call(this));

    Or a frequent example of a function that returns another function:


    const bind = (f, ctx) =>function() {
        return f.apply(ctx, arguments);
    };

    There is no variable for output, so in case of an exception we will see anonymous . NFE can help a little in proceedings.


    Short conclusion


    NFE well helps to write concise and unsupported code for rapid prototyping of a wide range of tasks. Use their beauty in the final code should be a couple of times thinking: JavaScript is already overloaded with interesting specifics.


    Also popular now: