《你不知道的JS》读书笔记——作用域和闭包

Yukino 396 2021-12-29
  1. JS是一门编译语言,但其不像传统编译语言一样,不会提前编译,编译结果也不能在分布式中进行移植。JS的编译大部分情况下发生在代码执行前的几微秒。

  2. 传统编译过程:词法分析->语法分析->代码生成。

  3. LHS和RHS查询:LHS查询是查找变量本身,RHS查询是查找变量的值。

  4. 词法作用域:定义在词法分析阶段的作用域,是由你在写变量时将变量和块作用域写在哪里决定的。

  5. 欺骗词法:

    1. 欺骗词法作用域会导致性能下降
    2. eval和with可以用来欺骗词法
  6. 提升:

    1. 在JS中,函数和变量声明都会被提升,例如:
    a = 2;
    var a;
    console.log(a);
    
    // 实际中,var a这条变量声明会被提升到作用域的顶部
    var a;
    a = 2;
    console.log(a);
    
    1. 每个作用域都会进行提升。
    2. 函数声明会提升,但是函数表达式不会,例如:
    foo(); // TypeError
    bar(); // ReferenceError
    var foo = function bar() { // ...
    };
    
    // 提升之后
    var foo;
    foo(); // TypeError
    bar(); // ReferenceError
    foo = function() {
        var bar = ...self... // ...
    }
    
    1. 函数优先,JS中函数会首先提升,其次是变量(这就是JS中一等公民吗),例如:
    foo(); // 1
    var foo;
    function foo() { 
        console.log( 1 );
    }
    foo = function() { 
        console.log( 2 );
    };
    
    // 提升之后
    function foo() { 
        console.log( 1 );
    }
    foo(); // 1
    foo = function() { 
        console.log( 2 );
    };
    
    1. let/const声明的变量不会被提升,实际上编译器也会注意到后面的let声明,但不能在声明之前以任何方式引用未声明的变量。es6中的解释:
    The variables are created when their containing Lexical
    Environment is instantiated but may not be accessed in any way
    until the variable’s LexicalBinding is evaluated.
    

    变量在其包含的词法作用域被实例化时被创建,但在变量被词法绑定之前不能以任何方式被访问。在let声明之前的这段运行时间被称为“暂时性死区,temporal dead zone,简称TDZ”。

  7. let声明的范围是块作用域,var声明的变量是函数作用域;let在全局作用域下声明的变量不会成为window对象的属性,var声明的会。

  8. 闭包:当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域外执行。


# 前端 # JS