《你不知道的JavaScript-上卷》第一部分-笔记-3-函数作用域和块作用域

51 阅读3分钟

3.1 函数中的作用域

一般情况下,每声明一个函数都会为其自身创建一个作用域

函数作用域的含义是指,属于这个函数的全部变量都可以在整个函数的范围内使用及复用(事实上在嵌套的作用域中也可以使用)。


3.2 隐藏内部实现

把变量和函数包裹在一个函数的作用域中,然后用这个作用域来“隐藏”它们

最小授权或最小暴露原则

在软件设计中,应该最小限度地暴露必要内容,而将其他内容都“隐藏”起来,比如某个模块或对象的 API 设计。

规避冲突

避免同名标识符之间的冲突,两个标识符可能具有相同的名字但用途却不一样,无意间可能造成命名冲突。冲突会导致变量的值被意外覆盖。

避免方式

全局命名空间

第三方库通常会在全局作用域中声明一个名字足够独特的变量,通常是一个对象。这个对象被用作库的命名空间,所有需要暴露给外界的功能都会成为这个对象(命名空间)的属性,而不是将自己的标识符暴露在顶级的词法作用域中。

模块管理

通过依赖管理器的机制将库的标识符显式地导入到另外一个特定的作用域中。


3.3 函数作用域

匿名函数表达式

setTimeout(
    function () {
        console.log("I waited 1 second!");
    },
    1000);

行内函数表达式

setTimeout(
    function timeoutHandler() { // <-- 快看,我有名字了!
        console.log("I waited 1 second!");
    }, 
    1000);

立即执行函数表达式

IIFE: Immediately Invoked Function Expression

具名函数的 IIFE

var a = 2;

(function foo() {
    var a = 3;
    console.log(a); // 3
})();

console.log(a); // 2

另一种写法

var a = 2;
(function () {
    var a = 3;
    console.log(a); // 3
}());
console.log(a); // 2

传参的IIFE

将 window 对象的引用传递进去,但将参数命名为 global

var a = 2;

(function IIFE(global) {
    var a = 3;
    console.log(a); // 3
    console.log(global.a); // 2
})(window);

console.log(a); // 2

解决 undefined 标识符的默认值被错误覆盖导致的异常

undefined = true; // 给其他代码挖了一个大坑!绝对不要这样做!
(function IIFE(undefined) {
    var a;
    if (a === undefined) {
        console.log("Undefined is safe here!");
    }
})();

倒置代码的运行顺序

(function IIFE(def) {
    console.log("before def")
    def(window);
    console.log("after def")
})(function def(global) {
    var a = 3;
    console.log(a); // 3
    console.log(global.a); // 2
});

3.4 块作用域

for、if与var

js中for、if中var声明的变量的作用域是和for、if语句所在的作用域一致的,for、if并不会创建独立的作用域

        for (var i = 0; i < 10; i++) {
        }
        console.log(i);//10

        var foo = true;
        if (foo) {
            var bar = foo * 2;
        }
        console.log(bar);//2

try/catch 作用域

ES3 规范中规定 try/catch 的 catch 分句会创建一个块作用域,其中声明的变量仅在 catch 内部有效

        try {
            undefined(); // 执行一个非法操作来强制制造一个异常
        }
        catch (err) {
            console.log(err); // 能够正常执行!
        }
        console.log(err); // ReferenceError: err not found

let

let 关键字可以将变量绑定到所在的任意作用域中(通常是 { .. } 内部)。换句话说,let为其声明的变量隐式地了所在的块作用域。

  • 使用 let 进行的声明不会在块作用域中进行提升

const

同样可以用来创建块作用域变量,但其值是固定的(常量)。之后任何试图修改值的操作都会引起错误。