你不知道的JavaScript(下卷)
上QQ阅读APP看书,第一时间看更新

1.11 函数

手机商店的店员应该不会随身携带计算器来计算税费和最后的应付金额。这个任务是她需要定义一次并多次复用的。很有可能的是,公司的收银设备(计算机或平板等)内置了这样的“函数”。

类似地,你的程序也几乎总是需要将代码的任务分割成可复用的片段,而不是一直重复编码。实现这一点的方法就是定义一个函数

通常来说,函数是可以通过名字被“调用”的已命名代码段,每次调用,其中的代码就会运行。考虑:

        function printAmount() {
            console.log( amount.toFixed( 2 ) );
        }


        var amount = 99.99;
        printAmount(); // "99.99"




        amount = amount * 2;


        printAmount(); // "199.98"

函数可以接受参数,即你传入的值,也可以返回一个值:

        function printAmount(amt) {
            console.log( amt.toFixed( 2 ) );
        }


        function formatAmount() {
            return "$" + amount.toFixed( 2 );
        }


        var amount = 99.99;


        printAmount( amount * 2 );   // "199.98"


        amount = formatAmount();
        console.log( amount );    // "$99.99"

函数printAmount(..)接受一个名为amt的参数。函数formatAmount()返回一个值。你也可以在同一个函数中同时使用这两种技术。

通常来说,你会在计划多次调用的代码上使用函数,但只是将相关的代码组织到一起成为命名集合也是很有用的,即使只准备调用一次。

考虑:

        const TAX RATE = 0.08;


        function calculateFinalPurchaseAmount(amt) {
            // 根据税费来计算新的数值
            amt = amt + (amt * TAX RATE);
            // 返回新的数值
            return amt;
        }


        var amount = 99.99;
        amount = calculateFinalPurchaseAmount( amount );


        console.log( amount.toFixed( 2 ) );      // "107.99"

尽管calculateFinalPurchaseAmount(..)只被调用了一次,但将它的行为组织到一个独立的命名函数中使得使用其逻辑(amount = calculateFinal...语句)的代码更为清晰。函数中的语句越多,其效果就会越明显。

作用域

如果你向手机商店的店员询问的手机型号是该商店里没有的,那么她也就无法将你想要的手机卖给你。她只能接触到商店里现有的手机。你也就不得不到另外一家商店尝试看看能否找到你想要的手机型号了。

编程中的一个术语可以表示这个概念:作用域(严格说是词法作用域)。在JavaScript中,每个函数都有自己的作用域。作用域基本上是变量的一个集合以及如何通过名称访问这些变量的规则。只有函数内部的代码才能访问这个函数作用域中的变量。

同一个作用域内的变量名是唯一的,所以不能有两个变量a一个接一个地放在一起。但是,同一个变量名a可以出现在不同的作用域中:

        function one() {
            // 这个a只属于one()函数
            var a = 1;
            console.log( a );
        }


        function two() {
            // 这个a只属于two()函数
            var a=2;
            console.log( a );
        }


        one();      // 1
        two();     // 2

此外,作用域是可以彼此嵌套的,就好像生日聚会上的小丑可以在一个气球内部吹起另一个气球那样。如果一个作用域嵌套在另外一个作用域内,那么内层作用域中的代码可以访问这两个作用域中的变量。

考虑:

        function outer() {
            var a = 1;


            function inner() {
              var b = 2;
                // 这里我们既可以访问a,也可以访问b
                console.log( a + b );    // 3
            }


            inner();
            // 这里我们只能访问a
            console.log( a );         // 1
          }


          outer();

词法作用域的规则表明,一个作用域内的代码可以访问这个作用域内以及任何包围在它之外的作用域中的变量。

因此,inner()函数内部的代码可以访问变量a和b,但是outer()中的代码只能访问a,不能访问b,因为这个变量b只在inner()函数内部。

我们来回顾一下前面的代码片段:

        const TAX RATE = 0.08;


        function calculateFinalPurchaseAmount(amt) {
            // 根据税费来计算新的数值
            amt = amt + (amt * TAX RATE);


            // 返回新的数值
            return amt;
        }

因为词法作用域的缘故,我们可以从函数calculateFinalPurchaseAmount(..)的内部访问常量(变量)TAX RATE,尽管我们并没有将它传递进去。

有关词法作用域的更多信息,参见本系列《你不知道的JavaScript(上卷)》第一部分中的前三章。