• <你不知道的JavaScript>读书笔记


    近几天看了一本不错的 JavaScript 的书,是 Kyle Simpson 写的 <You Don't know JS>。这本书是 Kyle Simpson 在 Github 上的开源项目,截止到现在已经有 27,796 个 star 。本想看英文版本,不料在某东上看到了中文版,于是乎就买下了。结果,翻译得还不错,推荐。

    原著github地址 ,某东链接自行搜索呵呵。

    这本书属于JavaScript开发者的进阶读物,原著至今为止已经出了6本,中文版(上卷)涵盖了其中的两本,介绍了4个知识点,分别为作用域、闭包、this和原型继承。

    看完了作用域和闭包部分,以前对这块有很多不大理解的地方,我觉得现在清晰了很多。虽然有很多概念先前已经有所耳闻,比如说作用域、变量提升等,但始终停留在知其然而不知所以然的程度。但此书用简单的例子从相对比较底层的角度解释这些概念,着实为我打开了新世界的大门,总之是相见恨晚!

    以下是我认为受益良多的部分:


    1. 编译器、引擎和作用域的关系

    我没有软件工程专业学位,在读这本书之前,我并不知道编译器、引擎是什么。当然,这本书也不是一本介绍编译器和引擎的书,但是至少到现在我知道了它们在大概是做什么的,以及在运行你写的代码的时候它们扮演了什么角色。

    简单来说,编译器会事先拿到你的代码,然后把它们拆开,然后重组成特定的结构,再转换成计算机能够读懂的形式交给引擎,然后引擎会运行这些机器指令。由于JavaScript是一门 动态的解释执行 的语言,编译发生在代码块执行前的极短时间内,这个过程可能会和传统语言有所出入,但是大同小异。

    而作用域和编译器与引擎有什么关系?关系很大。 因为引擎执行一段代码之前,需要知道变量相关的情况。因为引擎需要弄清楚变量属于哪个作用域,这样引擎才能够对变量进行赋值等操作。

    另外:变量查询分为两种:LHS查询RHS查询 (分别代表Left和Right)。什么是LHS和RHS?如果查找的目的是对变量进行赋值,就会用到LHS;如果目的是获取变量的值,就是RHS。


    2. 词法作用域和欺骗词法

    好的,假设你已经在别的地方获知了作用域这个概念。那 词法作用域 是啥?

    原来作用域分为 词法作用域动态作用域

    词法作用域 就是在书写的时候就确定了的,而 动态作用域 是在函数被调用的时候确认的。观察一下的例子:

    ```
    function foo() {
      console.log(a);
    }
    
    function bar() {
      var a = 3;
      foo();
    }
    
    var a = 2;
    
    bar();
    ```
    
    • 如果是词法作用域,结果会是 2
      由于词法作用域是书写时确定的,那么作用域就会根据 书写的位置 确定。在这个例子中,console.log() 查询 a 变量的时候,在 foo() 作用域内部查找不到 a 标识符,就会向外部的作用域查询,也就是 window.a 啦。

    • 如果是动态作用域的话,结果会是 3
      动态作用域不关心函数是在何处声明的,只关心它是在何处被调用的。这个例子中,console.log() 查询 a 变量的时候,同样在 foo() 作用域内部查找不到 a 标识符。但是它会根据 foo()调用的位置 来查找外部作用域,也就是 bar() 啦。所以这里会输出3。

    总之,一句话: 词法作用域只关心函数是在何处声明的,而动态作用域只关心函数是在哪里被调用的JavaScript并没有动态作用域,但是可以用 this.apply().call()机制来实现动态作用域的效果。


    3. 块级作用域

    传统编程语言会有块作用域的概念,通常由大括号 {} 包住的诸如forif这些都会创建块作用域。

    JavaScript并不是这样的。

    来,我们回顾一下这个经典的案例:

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

    因为JavaScript没有块作用域,这时候 i 会污染全局作用域。所以通常建议这样写:

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

    起码这样视觉上更加突出 i 污染了全局作用域的事实(笑)。那是不是说明JavaScript并没有快作用域?不是的。

    实际上,withtry...catchcatch 分句会创建一个块作用域。

    除此之外,ES6的 let 关键字会把变量锁定在块里面(真是不错啊)。另外一点需要注意的是使用 let 声明的变量并不会被提升。


    4. 变量提升与函数提升

    还是书中的例子:

    console.log(a);
    
    var a = 2;
    

    请问结果是什么?答案是 undefined。前几天我刚在别的地方看到过这个问题,现在我懂了。

    根据上述编译的原理。JavaScript会把代码中的声明都找出来,以把它们归为相应的作用域。所以说,这些声明是会在执行其他代码之前处理过的。

    实际上,变量包括函数声明都会被提升到 相应作用域 之前运行。在以上例子中,实际上执行起来的顺序看起来是这样的:

    var a;
    
    console.log(a);
    
    a = 2;
    

    所以,结果是 undefined

    另外,我有一点猜测,没有证实过不知道对不对。LHS查询失败都会报ReferenceError,RHS查询不到值都会输出undefined。


    5. 闭包

    闭包是什么? 以下这句话能够很好的阐释闭包:

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

    可见,同时满足以下条件的实体才能够形成闭包:
    1. 引用了外部作用域的变量(数据)
    2. 在定义词法作用域以外被调用

    (未完)

  • 相关阅读:
    Mishka and Interesting sum
    Tree Restoring
    LIB 配置文件读写器
    WCF 采用net.tcp协议实践
    MVC ViewEngineResult实际上是一种设计
    MVC 依赖注入扩展
    功能权限设计
    代理上网荟萃
    MD5算法 简介
    MVC _ViewStart文件的作用
  • 原文地址:https://www.cnblogs.com/lozio/p/5279616.html
Copyright © 2020-2023  润新知