JS超出最大调用堆栈

时间:2017-10-20 13:59:19

标签: javascript

我有这个:

var Utilities = (function (array, maxN) {

  function generateRandomNumber(array, maxN) {
        let randomN = Math.floor(Math.random() * maxN) + 0;

        if(!array.includes(randomN)) {
            console.log(maxN);
            if(array.length == maxN) {
                console.log('max reached');
                array.length = 0;
                return;
            }
            else {
                array.push(randomN);
            }

        }
        else {
            randomN = generateRandomNumber(array, maxN);
        }
        return randomN;
    }

    return {
        generateRandomNumber: generateRandomNumber
    };

})();

export default Utilities;

并且在点击时使用它:

function getRandomNumber(arr) {
    let randomN = Utilities.generateRandomNumber(arr, 5);
    return randomN;
}

然而,当达到5的长度时,我得到:(虽然我正在清除阵列) 我试图生成一个随机数,但我不想重复它,所以我将它存储在一个数组中,以检查它是否已经生成。但是我遇到的问题是(即使长度为=到最大数时我正在清除数组)我得到了“错误”附加

enter image description here

2 个答案:

答案 0 :(得分:2)

  

然而,当达到5的长度时,我得到一个堆栈溢出,虽然我正在清除数组

不,你没有清除阵列 - 只有当你找到一个新的随机数(不包括在数组中)时才会发生这种情况,当阵列已满时,这当然不会发生。您需要在开头进行长度检查:

function generateRandomNumber(array, maxN) {
    if (array.length == maxN) {
        console.log('max reached');
        array.length = 0;
        return;
    }
    let randomN = Math.floor(Math.random() * maxN) + 0;
    if (!array.includes(randomN)) {
        array.push(randomN);
        console.log(maxN);
        return randomN;
    } else {
        return generateRandomNumber(array, maxN);
    }
}

当然,这种通过尝试和错误生成新随机数的方法通常是低效的,并且一旦maxN非常大并且array几乎已满,将采取太多步骤。如果迭代实现为递归,则最终会出现堆栈溢出。

答案 1 :(得分:0)

这是因为你应该在看到它不在array时将项目推入数组中,这样使数组达到最大长度的项目也会清除array。 1}}。

现在的方式,使数组达到最大值的.push()不会清除数组,因此在下次调用时,无论生成什么数字都将包含在数组中,递归总是发生。

function generateRandomNumber(array, maxN) {
    let randomN = Math.floor(Math.random() * maxN);

    if(!array.includes(randomN)) {
        if(array.push(randomN) == maxN) {
            console.log('max reached');
            array.length = 0;
        }
        return randomN;
    } else {
        return generateRandomNumber(array, maxN);
    }
}

我还通过立即返回递归调用来进行正确的尾调用,而不依赖于当前作用域中的任何变量。我还利用.push()返回新.length这一事实,因此其返回值可用于比较。

我可能也会重新安排它以使用积极的条件。

function generateRandomNumber(array, maxN) {
    let randomN = Math.floor(Math.random() * maxN);

    if(array.includes(randomN)) {
        return generateRandomNumber(array, maxN);
    }

    if(array.push(randomN) == maxN) {
        console.log('max reached');
        array.length = 0;
    }
    return randomN;
}

就个人而言,我认为该函数应该使用自己的数组。我将摆脱array参数,并将var array = [];放在最外面的IIFE函数中。

由于需要重复使用,因此您可以创建一个工厂函数,该函数生成具有自己的数组和最大长度的唯一函数。

function makeNumberGenerator(maxN) {
    var array = [];

    return function generateRandomNumber() {
        let randomN = Math.floor(Math.random() * maxN);

        if(array.includes(randomN)) {
            return generateRandomNumber(array, maxN);
        }

        if(array.push(randomN) == maxN) {
            console.log('max reached');
            array.length = 0;
        }
        return randomN;
    }
}

然后要使用其中一个,首先要为所需大小创建一个数字生成器:

var randomGen5 = makeNumberGenerator(5);

然后在没有参数的情况下使用它,因为它已经有最大值和数组。

var num = randomGen5(); // 2 (or whatever)

这是一种更有效的算法,用于从范围产生随机数:

function makeNumberGenerator(maxN) {
    // Add some code to ensure `maxN` is a positive integer

    var array = [];

    return function generateRandomNumber() {
        if(!array.length) {
          array = Array.from(Array(maxN), (_, i) => i);
        }
        return array.splice(Math.floor(Math.random() * array.length), 1)[0];
    }
}