每日读《你不知道的JavaScript(上)》| 深入理解JavaScript函数作用域

62 阅读2分钟

函数作用域

属于这个函数的全部变量都可以在整个函数的范围内使用及复用。

从两个角度来理解 函数作用域 的意义和用途:外部和内部。

“外部”指的是,声明一个函数,往里面添加代码。

可以理解为添加一个工具函数。

“内部”指的是,将需要私有化的变量和逻辑封装进一个函数中,相当于用一个函数作用域去隐藏内部。

制作一个工具函数

比如我们需要一个能将 name-age 转换为 nameAge 的工具函数。

那这时候我们会声明一个函数,命名为 transUpper。

function transUpper(str) {
    // ... 往这里添加 name-age 转 nameAge 的逻辑
}

此时我们以 transUpper 创建了一个函数作用域,内部声明的一切变量将为内部逻辑所使用。

外部无法访问这个函数内部的变量。

最小特权原则

来看下面这段代码,找找看有什么很膈应的地方?😏

function fn(a) {
    b = a + foo(a * 2);
    console.log(b * 3);
}

function foo(a) {
    return a - 1;
}

var b;

fn(2);

可以看到,foo 和 b 都挂到了全局作用域下了。

但使用的地点却在 fn 中。

可见如此设计代码结构并不合理。

改成如下方式:

function fn(a) {
    function foo(a) {
        return a - 1;
    }
    var b = a + foo(a * 2);
    console.log(b * 3);
}

fn(2);

规范许多。

在这个 case 中,其实我们利用了函数作用域的特性,将变量 b 和函数 foo 隐藏到 fn 的作用域内部,避免了污染全局作用域。

规避冲突

接上,我们知道如果把变量都定义在全局作用域中的话,会很容易造成命名冲突。

在规避冲突的方面,函数作用域也能发挥很好的作用。

有一个很经典的case。

来看下面这段代码:

function foo(){
    function bar(a) {
        i = 3; // 这里有问题
        console.log(a + i);
    }
    
    for(var i=0; i<10; i++) {
        bar(i * 2);
    }
}

foo();

可以先思考一下这段代码会有什么样的执行结果,和预期的效果是否一致。

输出结果是无限循环打印11。

这里为啥会无限循环打印呢?

原因在于i = 3这一行上。

每一次在循环体中调用 bar 函数的时候,bar 函数内部的变量 i 会覆盖循环体中的 i,导致 i 的值永远为 3,也永远满足不了i>=10的条件,便造成了无限循环的后果。

想要达到预期效果,需要改成如下:

function foo(){
    function bar(a) {
        var i = 3; // 修改这里
        console.log(a + i);
    }
    
    for(var i=0; i<10; i++) {
        bar(i * 2);
    }
}

foo();

小结

今天主要从两个不同的角度来介绍函数作用域,主要在于如何深入理解函数作用域、利用函数作用域的特性能做什么等等。

明天继续!GoodBye~