威尼斯www.9778.com-威尼斯正版官方网站

JavaScript 函数表达式(一)递归

日期:2020-01-04编辑作者:Web前端技术

定义:

递归函数是在一个函数通过名字调用自身的情况下构成的。

1、什么是递归函数?

递归函数就是在函数体内调用本函数;

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

递归函数就是在一个函数通过名字调用自身的情况下构成的,

递归函数的使用要注意函数终止条件避免死循环;

上例是一个经典的递归阶乘函数。虽然这个函数表面看来没什么问题,但下面的代码却可能导致它出错。

如下所示:我们用递归实现阶乘

递归实现形式:

var anotherFactorial = factorial;
factorial = null
alert(anotherFactorial(4)); // error
    var factorial = function (num) {
        if (num <= 1) {
            return 1;
        } else {
            return num * factorial(num - 1);
        }
    }

1.声明一个具名函数,通过函数名调用

上例代码先把 factorial() 函数保存在变量 anotherFactorial 中,然后将 factorial 变量设置为 null,结果指向原始函数的引用只剩下一个。但在接下来调用 anotherFactorial() 时,由于必须执行 factorial(),而 factorial 已经不再是函数,所以就会导致错误。

2、如何实现递归函数?

function f(a){ if(a=1){ return 1 }else{ return a*f(a-1) }}

在这种情况下可以使用 arguments.callee 可以解决这个问题。

(1)先写一层的情况。上面所示的递归阶乘我们就可以先思考num参数乘num-1的情况。

但是这样使用会因为 函数名 f 的变化而报错,

arguments.callee 是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用。

(2)抽象递归参数。在递归函数中,如何将下一层关联起来就需要抽象参数来进行解决,参数的个数根据情况而定。

f = nullf () // Uncaught TypeError: f is not a function
function factorial(num) {
    if (num <= 1) {
        return 1;
    } else {
        return num * arguments.callee(num - 1);
    }
}

(3)找到突破点。为什么要找一个突破点?在js中你会发现很少有人写递归,因为不注意就会造成死循环。必须记住递归函数是自己调用自己,某种情况(这种情况肯定存在)不调用自己。在上面的递归函数中num<=1就是一个突破点。

2. 使用arguments.callee代替函数名

通过使用 arguments.callee 代替函数名,可以确保无论怎样调用函数都不会出问题。在编写递归函数时,使用 argument.callee 总比使用函数名保险

3、上述代码存在的问题

在严格模式下不支持使用arguments.callee

注意:在严格模式下,不能通过脚本访问 arguments.callee 访问这个属性会导致错误。不过可以使用命名函数表达式来达成相同的结果。

    var anotherfactorial = factorial;
    factorial = null;
    console.log(anotherfactorial(3)); // factorial is not a function(出错)

h3.使用函数表达式

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

为什么会出错呢?

var fun = (function f(a){ if(a=1){ return 1 }else{ return a*f(a-1) }})// 或:var f = function (a){ if(a=1){ return 1 }else{ return a*f(a-1) }}var fun = f;

以上代码先把factorial函数保存在变量anotherfactorial中,然后将factorial赋值为null。导致的结果指向原始函数的引用只剩下一个。但是接下来调用anotherfactorial时,由于必须执行factorial函数,而factorial这时候已经赋值为null了,所以就会导致错误。

那么如何解决这个问题呢?

在ECMAScript提供了arguments.callee可以解决这个问题。arguments.callee是一个指向正在执行的函数(anotherfactorial)的指针,因此可以用它来实现对函数的递归调用。例如上述代码我们就可以这样写:

    var factorial = function (num) {
        if (num <= 1) {
            return 1;
        } else {
            return num * arguments.callee(num - 1);
        }
    }
    var anotherfactorial = factorial;
    factorial = null;
    console.log(anotherfactorial(3)); // 6

通过使用arguments.callee代替函数名,可以确保无论怎样调用函数都不会出问题。因此在使用递归函数时,使用arguments.callee总比使用函数名更保险。

但是在严格模式下,不能直接访问arguments.callee,并且会导致错误。所以产生了终极解决方案,使用命名函数表达式来达成相同的效果。例如:

    var factorial = (function f(num) {
        if (num <= 1) {
            return 1;
        } else {
            return num * f(num - 1);
        }
    });
    var anotherfactorial = factorial;
    factorial = null;
    console.log(anotherfactorial(3)); // 6

在以上代码中创建了一个名为f的命名函数表达式,避免了出现arguments.callee。然后将它赋值给变量factorial。即使把函数赋值给另一个变量,函数的名字f仍然有效,所以递归调用照样能正确完成,而且这种方法在严格模式和非严格模式下都能运行。

本文由威尼斯www.9778.com发布于Web前端技术,转载请注明出处:JavaScript 函数表达式(一)递归

关键词:

如果他们在未来的几年内技术水平没有突破性的提升,或者缺乏

友情提示:本人经历有限,没进过大厂,没参与过开源社区维护,就是个写了几年业务代码的底层码农。一切观点来...

详细>>

CFA协会如何解读区块链——下篇

事实:尽管许多行业可以将数据传输和存储到安全的区块链中,并从中受益,并提高许多企业的运营效率,并实现问...

详细>>

H5 游戏开发:决胜三分球

H5游戏开发:套圈圈 2018/01/25 · HTML5 ·游戏 原文出处: 凹凸实验室      H5 游戏开发:推金币 2017/11/10 · HTML5 · 1评论...

详细>>

H5 游戏开采:制胜三分球

H5 游戏开发:推金币 2017/11/10 · HTML5 · 1评论 ·游戏 原文出处: 凹凸实验室    近期参与开发的一款「京东11.11推金...

详细>>