美文网首页
十分钟快速了解《你不知道的 JavaScript》(上卷)

十分钟快速了解《你不知道的 JavaScript》(上卷)

作者: AshengTan | 来源:发表于2018-08-20 21:13 被阅读41次

    最近刚刚看完了《你不知道的 JavaScript》上卷,对 JavaScript 有了更进一步的了解。

    《你不知道的 JavaScript》上卷由两部分组成,第一部分是《作用域和闭包》,第二部分是《this 和对象原型》。下面我会按照简单介绍一下每一章的主要内容及阅读感受。

    第一部分《作用域和闭包》

    第 1 章 作用域是什么

    1. 编译原理:简单介绍分词/词法分析、解析/语法分析、代码生成的概念;
    2. 理解作用域:介绍引擎、编译器、作用域之间的关系;
    3. 作用域嵌套。

    在这一章节中,作者通过引擎、编译器、作用域之间的对话,将这三者之间的关系及作用生动形象地展现出来,并引出了 LHS 查询和 RHS 查询的概念。

    第 2 章 词法作用域

    1. 词法作用域及其相关概念;
    2. 欺骗词法的方式:
      • 在代码运行时修改词法作用域,如 eval()
      • 在代码运行时创建新的词法作用域,如 with

    这一章作者介绍了词法作用域以及欺骗词法的方式。说来惭愧,在看这章之前,我完全没听说过「词法作用域」这个概念,一开始我还以为是个很高大上的东西,看完之后你会觉得其实也没什么,就是你平时都在写的东西,只不过你没有留意而已。

    第 3 章 函数作用域和块作用域

    1. 函数作用域:函数声明和函数表达式的区别、具名函数和匿名函数;
    2. 块作用域:withtry/catchletconst

    这一章作者介绍了 JavaScript 中的函数作用域及块作用域,讲了函数声明和函数表达式的区别,其实很简单,就是看 function 这个关键字是否是在声明中的第一个词,如果是,那就是函数声明,否则就是函数表达式。另外,作者还简单地介绍了下 ES6 中具有块作用域作用的 letconst 关键字。

    在这之前,我一直以为 ES6 之前是没有块作用域的,只有全局作用域和函数作用域,看完这章之后,我才知道其实在 ES3 的时候就有块作用域了。比如,with 。再比如,try/catch 中的 catch,一般我们是这样写的:

    try {
        // do something
    } catch (err) {
        console.log(err)
    }
    

    其中这个 err 只存在 catch 分句内部,从别处引用时会抛出错误。这不就是块作用域吗?

    第 4 章 提升

    这一章节作者简单地介绍了一下变量声明提升和函数声明提升。没什么好说的,需要注意的是函数表达式是赋值操作,并不会提升。

    第 5 章 作用域闭包

    1. 闭包;
    2. 作用域和闭包;
    3. 模块机制。

    闭包是 JavaScript 中的一大难点,在这章中作者用了 4 个小节来介绍闭包,还有 1 个小节来介绍模块机制。不要看闭包有四个小节,其实也不过 8 页而已,核心的文字加起来也就 2 页,但就是这短短的 2 页,就把闭包给讲得非常清楚。

    下面是书中给出关于闭包的定义:

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

    看了是不是还是不懂,没关系,让我们来提取关键字:

    • 函数;
    • 记住并访问所在的词法作用域;
    • 当前词法作用域之外执行。

    再来看下书中的一段代码,看完之后再结合书中的定义来理解,我相信你对闭包肯定会有更进一步的理解。

    function foo() {
        var a = 2;
    
        function bar() {
            console.log(a);
        }
    
        return bar;
    }
    
    var baz = foo();
    
    baz(); // 2 —— 朋友,这就是闭包的效果。
    

    下面结合我们刚刚提取的关键字来理解。

    • 函数。这里的函数是 bar()
    • 记住并访问。 bar() 当前所在的词法作用域是 foo() 的函数作用域。bar() 的词法作用域能够访问 foo() 的内部作用域。
    • 当前词法作用域之外执行。在上面的代码中,我们将函数 bar() 当做一个值类型传递给外部,在这句代码中 var baz = foo();,我们将 foo() 的返回值(也就是 bar())赋值给变量 baz 并调用 baz(),实际上就是调用 bar()。上面第 2 点里我们说了,bar() 的作用域是 foo() 的函数作用域,但是,在这里,它却是在自己定义的词法作用域以外的地方执行。

    怎么样,通过上面的分析,是不是对闭包有了进一步的理解了。

    附录 A 动态作用域

    作者在这一章中简单地分析了下动态作用域,并通过一小段代码将它与词法作用域做了对比。词法作用域与动态作用域的主要区别在于:词法作用域是在定义时确定的,而动态作用域是在运行时确定的。

    附录 B 块作用域的替代方案

    这一章简单地介绍了块作用域的替代方案 Traceur,以及因此可能会带来的性能问题。

    附录 C this 词法

    这一章并没有说明 this 机制 ,只是介绍了 ES6 中的箭头函数引入的行为 —— this 词法。关于 this 机制的详细说明是在第二部分《this 和对象原型》中的第 1 章和 第 2 章。

    附录 D 致谢

    这一章作者致谢了一大堆的人,光人名的排版就占了两页多,说真的,我都怀疑是不是在凑字数了(纯调侃,没别的意思)。


    第二部分《this 和对象原型》

    第 1 章 关于 this

    1. this 的指向;
    2. this 的作用域。

    这一章中作者先是提出我们「为什么要使用 this?」这个问题,然后再指出「this 到底是什么?」,为第 2 章做铺垫。

    这一章我个人认为最核心的就是两句话。第一句是「当一个函数被调用时,会创建一个活动记录(有时也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。this 就是这个记录的一个属性,会在函数执行的过程中用到」。也就是说,this 是活动记录里的一个属性,与函数执行的过程有关。

    第二句话是「this 实际上是函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。」。第 2 章实际上就是在讲这个绑定。

    第 2 章 this 全面解析

    1. 调用位置;
    2. 绑定规则:
    • 默认绑定;
      • 隐式绑定;
      • 显式绑定:call()apply()bind()
      • new 绑定;
      • 箭头函数的绑定;
      • 一些例外的绑定。
    1. 绑定规则的优先级。

    作者在这一章中全面介绍了 this 的绑定规则。

    要弄清楚 this 的绑定对象,需要明白以下两点:

    • 调用位置
    • 绑定规则

    什么是调用位置?简单来说,就是函数在代码中被调用的位置。为了找到调用位置,我们需要分析调用栈,也就是为了到达当前执行位置所调用的所有函数,而调用位置就在当前正在执行的函数的前一个调用中。

    而绑定规则就是说 this 绑定的对象是有规则的,并且这些规则是有优先级的,总的来说有下面四点:

    1. new 调用的,绑定到新创建的对象;
    2. callapplybind 调用的,绑定到指定的对象;
    3. 由上下文对象调用的,绑定到该上下文对象;
    4. 默认的,在严格模式下绑定到 undefined,在非严格模式下绑定到全局对象。

    当然了,ES6 中新增的箭头函数并不在这四条规则里面,而是继承外层第一个非箭头函数调用的 this 绑定。

    在看这一章之前,我对 this 一知半解,网上找的答案也是五花八门,根本不知道哪个对哪个错。在看完这一章之后,我算是对 this 的所绑定的对象有了较为清晰的认识,以后再遇到类似的问题时,直接套用上面的规则就可以了。

    第 3 章 对象

    1. JavaScript 中的数据类型;
    2. 内置对象;
    3. 对象属性与方法;
    4. 数组;
    5. 对象复制;
    6. 属性描述符;
    7. [[Get]] 操作与 [[Put]] 操作;
    8. Getter 和 Setter;
    9. 遍历及 ES6 中的 Symbol.iterator。

    这一章讲到了很多平时我并没有注意到的东西,比如,一般来说,我们使用数组的时候都是下标/值对,但是,给数组添加属性居然也是可以,虽然这并不会改变数组的长度。当我看到这一部分的内容时心里在想:我去,这是什么骚操作?这样居然也可以?后面想了想,数组其实也是对象,是一个特殊的对象,从这一方面来说也是行得通的;再比如,属性访问与赋值时发生的 [[Get]] 操作与 [[Put]] 操作,能够更好地了解其工作原理;还有,我们可以利用 ES6 中的 Iterator 接口实现自己的迭代逻辑。

    第 4 章 混合对象 “类”

    1. 类理论;
    2. 类的机制;
    3. 类的继承;
    4. 混入。

    这一章讲到了 “类” 这一设计模式,以及 JavaScript 中各种实现这一模式的方法。

    这一章的核心就是:类的本质是复制,多态和继承也是。这一点很重要,JavaScript 中也有类,但是两者的本质是不同的,这一点在《第 5 章 原型》和《第 6 章 行为委托》里面有详细的说明。

    这一章一开始看的时候我是很模糊的,因为作为一名 JavaScript 开发者,说实话我对于 “类” 这个东西的理解不是很明白,所以我跳过了这一章,等到看完了后面三章之后再回过来看,瞬间感觉清晰很多了。

    第 5 章 原型

    1. [[Prototype]] 属性;
    2. 属性设置和屏蔽;
    3. JavaScript 中的 “类”;
    4. (原型)继承;
    5. 对象关联。

    这一章中,第一小节的 [[Prototype]] 属性可以和第 3 章中的 [[Put]] 操作结合一起看,这样能够完整的了解属性赋值的工作原理;属性设置和屏蔽这一部分可以和第 4 章结合着阅读,以便更好地了解 JavaScript 中的类与其它语言中的类的区别。

    第 6 章 行为委托

    这一章作者主要从类理论与委托理论(其实也就是对象关联)两种不同的设计模式来介绍他们之间在代码上实现的不同,可以看做是第 4 章和第 5 章的对象关联的实践部分。

    附录 A ES6 中的 Class

    这一章作者分析了 ES6 中新增的 Class 语法的优点与缺点。

    全书感悟

    以上就是本书中的一些主要内容介绍,我写得比较简单,其实书中还有很多比较细小的东西,有兴趣的同学可以去买来看看。总的来说,这本书还是挺不错的,能让你学到一些平时没有注意到的东西,作者偏向于用口语化的文字来介绍知识点,不会显得枯燥。

    最后,抛块砖,希望能引块玉。下面是我在阅读本书过程中的做的思维导图。导图的内容比较多,不是很简洁,因为我希望尽量把书中作者提到的概念提取出来,所以可能会显得比较啰嗦。


    思维导图

    最后,欢迎关注我的微信公众号:前端路漫慢。


    微信公众号-前端路漫慢

    相关文章

      网友评论

          本文标题:十分钟快速了解《你不知道的 JavaScript》(上卷)

          本文链接:https://www.haomeiwen.com/subject/aqepbftx.html