A long walk on recursive functions in JavaScript

You recently saw arguments.callee in JavaScript Advanced Programming to learn that there are so many pits in JavaScript recursion.I didn't know it before. I'll sort it out today. Let's start from the beginning.

Recursively implement a factorial function

Needless to say definitions here, let's go directly to the code, in normal mode.

function factorial(num){
    if(num<=1){
        return 1;
    }else{
        return num * factorial(num-1);
    }
}

The above is a typical recursive call, but it has a problem.

var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4));//Report errors

Recursive functions are situations in which a function calls itself by name (or rather, by name).Normally, it's okay to do this, but the key is that the JavaScript language is different from other languages, the function name is just a pointer, it's not an entity object of the function.So if we change this pointer, the pointer call inside the function is not itself. In the example above, we just killed it.Let it point to the empty, and it will run away.

Here comes an important attribute:

arguments.callee

Callee is a property of the arguments object, arguments.callee value is a pointer to the function being executed.So it can make recursive calls to functions.

For example:

function factorial(num){
    if(num<=1){
        return 1;
    }else{
        return num * arguments.callee(num-1);
    }
}

By using arguments.callee proxy function name, you can ensure that you can call the function without any problems.Therefore, in normal mode, using this method will be more insurance.

However, in strict mode, calling arguments.callee above results in an error because strict mode does not support arguments.What about that?

What if arguments.callee cannot be used in strict mode?

The same effect can be achieved by using named expressions.Look at the code:

var factorial = (function f(num){
     if(num<=1){
        return 1;
    }else{
        return num * f(num-1);
    }
});

The code above creates a named expression named f() and assigns it to the variable factorial.That way, even if I make a variable into another variable, it won't affect my own internal calls.The F function name is still valid.So recursive function calls can still be performed correctly.

Now let's upgrade the ECMA version to 6.

What about ES6?

In the case of ES6, we can do this in another way.For example:

const factorial = (function f(num){
     if(num<=1){
        return 1;
    }else{
        return num * f(num-1);
    }
});

Let's upgrade var to const and make this variable a constant so that it doesn't change.We use the arrow function instead of the named function above.

const factorial =num=>{
     if(num<=1){
        return 1;
    }else{
        return num * factorial(num-1);
    }
}

Constants cannot be changed, so we can write the code above.But do you think the code is okay???

Let's call it this way:

factorial(100000);

RangeError: Exceeded maximum call stack size

RangeError: Maximum call stack size exceeded
    at factorial (H:\company_work_space\Demos\Demo1.js:40:18)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)

The next step is how to solve this problem.The answer is to use tail call optimization.
What is a tail call?You can look at this first What is a tail call?

Tags: Javascript Programming Attribute

Posted on Tue, 03 Sep 2019 15:20:58 -0700 by Rodney H.