精读《JavaScript权威指南》


第2章词法结构

所谓词法结构指的是: 如何使用这门语言编写程序 的方式。

JavaScript 的词法结构主要包含了 5 个部分:

  1. 注释
  2. 字面量
  3. 标识符和保留字
  4. Unicode
  5. 分号

注释

JavaScript 的注释分为两大类,一共有三种写法:

image-20230612191101753

上图的第一种为 单行注释,剩下的两种为 多行注释

这里有一个小点大家注意,第三种注释方式配合 VS Code 可以让我们在使用 js 函数获得对应的注释提示:

image-20230612191150938

可选的分号

我们知道在 JS 中分号并不是一个强制必须要有的东西,并且根据现在企业开发的标准,很多时候是 不允许 写结尾分号的。

这种不允许在某些情况下,可能会导致一些错误。比如:

image-20230612172032215

这样的代码,一旦我们通过格式化处理,那么就会被合并被一行:

image-20230612172044225

所以,此时我们就增加一些 “防御性” 的分号,避免这种情况出现:

image-20230612172058260

另外有一个小的细节点需要注意,那就是 一定不能在return、break或continue等关键字后加入《换行符》,这里作者给我们列举了实例:

image-20230612172119386

第3章 类型、值和变量

JS 中类型一般被分为两大类:

  1. 简单数据类型:作者称其为 原始类型
  2. 复杂数据类型:作者称其为 特殊类型

整个第三章中,所讲解的主要就是这两大类型的内容。

原始类型

首先咱们先来看原始类型,整个原始类型包含了 7 种类型的值: number、string、boolean、null、undefined、symbol、bigint

这 7 种数据类型我们不会一个一个都去讲,我们主要来看其中 5 个。

  1. 首先是 number:在 JS 中,无论是整数还是小数都被称为 number 类型。但是大家要注意的是:JS 中无法精确表示浮点数。比如:0.3 - 0.2 是不等于 0.1 的。所以说 JS 无法进行精确地浮点数表示。
  2. 接下来是 nullundefined:这两个值在 JS 中都被称为是 假性值。但是它们也有不太一样的地方。null 是一个特殊值,通常被用来表示 某个值不存在,它是一个 object 类型,我们可以使用 typeof null === 'object' 来进行测试。
  3. undefined 它表示一个 “更深层次的不存在”,当我们定义一个变量但不进行赋值时,那么这个变量默认是 undefinedundefined 是一个单独的类型,我们可以使用 typeof undefined === 'undefined' 来进行测试。
  4. 最后是 symbolbigint:这两个都是 ES6 之后新增的数据类型。其中 symbol 表示 唯一的值。我们可以通过 symbol 得到一个永远都不会重复的结果 Symbol('a') === Symbol('a') // false
  5. bigint 表示的是 大数字。默认情况下 JS 中的 number 类型只能表示 -2^53 -- 2^53 之间,超过这个范围就无法精准表示了。所以 ES 推出了 bigint 来表示大数字。使用 bigint 非常简单,只需要在 **大数字后面加上 n ** 就可以了,同时需要注意 bigint 只能是 整数,另外 bigint 不能与 number 进行算术运算。

特殊类型

接下来我们来看特殊类型。 JS 中的特殊类型只有一个,那就是 object。或者说:在 JS 所有不属于原始类型的都是特殊类型。

比如:ArrayObjectFunctionSet 等等

这些特殊类型我们会在 第六章 - 第九章 再去进行详细讲解。

第4章 表达式与操作符

表达式

在 JS 中你的每一行语句,都可以被称为是一个表达式。

操作符

条件式调用

首先是 条件式调用:在我们的日常开发中,如果一个对象为 假性值,那么当我们获取这个对象的属性时,会得到一个错误:

image-20230612172916349

为了解决这样的问题,在 ES2020 中提供了一个新的语法 条件式调用,可以让我们避免这种错误:

image-20230612172928143

obj 后面加上一个 ?,如果 obj 为假性值,则会得到一个 undefined。而不会抛出异常。

in 操作符

in 操作符用来判断 左侧值是否是右侧对象的属性名,返回一个 boolean 类型的值。

instanceof 操作符

instanceof 操作符用来判断 左侧值是否是右侧类的实例,返回一个 boolean 类型的值。

typeof 操作符

typeof 操作符 我们在前面使用过,它可以用来 判断一个值的类型。它可以判断的所有类型如下表所示:

image-20230612173011302

delete 操作符

delete 操作符可以用来 删除一个对象或数组的指定属性或元素

image-20230612173034357

eval() 函数(操作符)

eval() 是一个函数,但是在这里作者把它作为操作符来进行讲解。

eval() 可以接收一个 字符串,这个字符串应该是一段表达式。然后 JS引擎 会把这段字符串当做 JavaScript 代码来进行执行。

?? 先定义操作符

?? 先定义操作符 有点类似于 || 操作符。但是不同的地方在于 || 操作符数字 0 会被作为假性值判断为假,但是在 ?? 先定义操作符数字 0 会被作为真来进行处理。

第5章 语句

循环语句

循环语句指的是 包含循环的语句。在 JS 中可以产生循环功能的语法非常多,比如:while、do/while、for 这三个都是基本的循环语法,这里咱们不去详细说。

除此之外,还有一些 ES6 之后新增的循环方式:

首先是 for/of 循环: for/of 循环,不可以循环对象,只能循环非对象,比如 Array、Map 都可以利用 for/of 进行循环操作。当数组使用 for/of 进行循环时,每个循环对象表示数组的 item。当 Map 进行循环时,每个循环对象是一个 数组,我们可以通过 [] 解构 的方式,拿到对应 itemkey 和 value

其次是 for/in 循环: for/in 循环比较简单,它既可以循环对象,也可以循环数组

最后是 for/await与异步迭代 循环:这一块涉及到了迭代器和异步的部分,咱们把它放到 第12章 迭代器与生成器 和 第13章 异步JavaScript 中在进行讲解。

跳转语句

所谓跳转语句指的是:会修改代码执行顺序的语句

JS 中的跳转语句一共分为 7 类:break、continue、return、yield、throw、try/catch/finally,咱们一个一个来说。

首先是 break:它通常配合循环或者 switch 进行使用,表示退出当前循环或 switch 条件判断。

continue:通常配合循环使用,它表示 跳出当前循环,继续进行下一次循环

returnreturn 语句只能出现在函数体内,表示返回值,并终止函数执行。

yield: 主要配合生成器函数来进行使用,这个咱们等到 第12章 迭代器与生成器 时再进行说明。

throw:表示抛出一个异常,通常情况下,这会终止程序的执行。

try/catch/finally:可以捕获异常。当 try 中的代码块出现异常时,代码会进入 catch 执行,最终无论结果如何都会执行 finally

第6章 对象

JS 中,对象是一个属性的 无序集合,所有用 {} 包裹,并且包含 key:value 结构的值都是一个对象。

想要创建一个对象的话,一共有三种方式:

  • 对象字面量:这是最常用的一种方式 const obj = { name: '张三' }
  • new 创建:利用 Object 构造函数,配合 new 关键字,创建对象实例 const obj = new Object
  • 原型创建:通过 Object.create 方法创建对象实例

而想要创建对象属性的话,那么一共有两种方式:

  • . 操作符:obj.key
  • [key]obj[key]

想要删除对象中的某个属性,可以利用咱们前面提到的 delete 关键字。

JS 中,如果想要测试某个属性是否属于指定对象,那么一共有三种方式:

  • in 操作符:咱们之前有提到过这个操作符,这里就不再多说了。
  • hasOwnProperty 方法:当前方法可以 测试指定属性是否为对象的自有属性
  • propertyIsEnumerable 方法:当前方法可以 测试指定属性是否为对象的可枚举属性

而如果想要 依赖现有对象,扩展出其他对象的话,那么有两种方式:

  • Object.assign():该方法可以将所有可枚举Object.propertyIsEnumerable() 返回 true)的自有Object.hasOwnProperty() 返回 true)属性从一个或多个源对象复制到目标对象,返回修改后的对象。
  • 扩展操作符...:利用 ES6 提供的 扩展操作符,可以把多个对象合并成一个新的对象。

除此之外,JS 还提供了序列化的方法,所谓的序列化指的是 把对象的状态转换为字符串的过程,之后可以从中恢复对象的状态 。在 JS 中,可以通过 JSON.parseJSON.stringify 来完成对象的序列化过程。

最后就是 对象字面量扩展语法,这些扩展方法有些可能比较冷僻,但是有的确实在日常开发中存在一些价值,所以我们这里需要拿出来一些时间来说一下。

  • 首先是 简写属性:这种语法是开发中非常常见的语法,key 和 value 为相同的名称时,可以简写

    image-20230612173658238
  • 其次是 计算的属性名:有些时候我们可能期望使用变量来作为对象的key,此时就需要用到这种计算的属性名。

    image-20230612173711768
  • 然后是 简写方法:这个也是非常常见的一种写法。常见到很多同学都不认为这是一种简写的写法。

    image-20230612173721850
  • 最后是 属性的获取方法与设置方法:我们可以通过 setget 表示来标记一个对象的方法,这样这个方法就可以像调用属性一样进行调用了。vue3 中的 ref 就是通过这种方式实现的响应性。

    image-20230612173734002

第7章 数组

JS 提供了两个 工厂方法 来创建数组:

  • 第一个是 Array.of():可以通过 可变数量的参数 创建一个新的 Array 实例,而不需要考虑参数的数量或类型。

    image-20230612174333706
  • 第二个是 Array.from():这个方法的主要价值是可以把 把类数组转化为真实数组。关于类数组我们会在下面讲解到

    image-20230612174345691

想要添加或者删除数组元素的话,这里咱们各列举三种方式:

  • 添加
    • Array.prototype.push():把指定元素添加对数组的最后
    • Array.prototype.unshift():把指定元素添加到数组的最前
    • arr[1] = 'xx':把指定元素添加到数组的指定位置
  • 删除
    • Array.prototype.pop():从数组的最后删除元素
    • Array.prototype.shift():从数组的最前删除元素
    • delete 操作符:删除数组指定位置元素

对于数组迭代的话,我们日常开发中常用的其实只有两种:

  • 传统 for 循环:
    image-20230612174446241
  • forEach 循环:
    image-20230612174455484

JS 中存在一个特殊的数组概念,叫做 类数组对象。在 JS 中,所有的数组都拥有如下四个特性:

  • ① 数组的length属性会在新元素加入时自动更新
  • ② 设置length为更小的值会截断数组
  • ③ 数组从Array.prototype继承有用的方法
  • Array.isArray()对数组返回true

而类数组,虽然也具备 length 属性,但是却没有以上这四个特性。比如:arguments。如果想要把类数组对象转化为数组对象,可以使用我们之前提到的 Array.from() 方法

第8章函数

函数包含参数的概念,参数分为 形参实参 。其中 形参 表示 定义函数时指定的参数实参 表示 调用函数时传递的参数。在 JS 中实参和形参并 不需要 是一一对应的,同时函数调用也不对传入的实参进行任何类型检查。那么这样的一个动态的形式,在 JS 中就被分为了 5 个概念:

  • 可选形参与默认值:这样的代码表示,如果没有传入 a 形参,那么 a 默认为 []

    image-20230612175022888
  • 剩余形参与可变长度实参列表:可以通过 ... 的形式来获取所有的剩余实参。在这里 rest = [10, 100, 2, 3, 1000, 4, 5, 6]

    image-20230612175044195
  • Arguments 对象:表示或许所有的实参,它是一个伪数组,可以通过 Array.from 方法转化为真实数组

    image-20230612175102465

  • 在函数调用中使用扩展操作符:在实参中可以使用扩展运算符,这表示把数组所有的元素作为一个一个的实参单独传入

    image-20230612175122564
  • 把函数实参解构为形参:函数的形参可以直接进行解构

    image-20230612175136561

函数内部可以嵌套函数,如果 一个函数访问了其它函数作用域中才有的变量,那么这个函数可以叫做 闭包函数。闭包是在面试时的一个常见概念,需要大家能够掌握。

image-20230612175235324

对于函数而言,它除了可以被调用之外,还拥有自己的 属性方法

  • 属性:函数的属性有很多,咱们这里主要说三个

    • length 属性:形参的个数
    • name 属性:定义函数时使用的名字
    • prototype 属性:获取原型对象
  • 方法

    • call()、apply():这两个是函数的典型方法。call()apply()允许间接调用一个函数,就像这个函数是某个其他 对象 的方法一样,同时会把该函数中的 this 修改为当前对象。

      image-20230612190335748
    • bind():在函数 f 上调用 bind() 方法并传入对象 o,则这个方法会返回一个新函数。同时它也会修改函数 f 中的 this 指向

      image-20230612190346634
    • toString() 方法:返回一个符合函数声明语句的字符串

    • Function() 构造函数:使用 Function() 构造函数可以用来 创建新函数

      image-20230612190355559

    最后如果一个函数 接收一个函数参数,并返回一个新函数 的话,那么这个函数可以被叫做 高阶函数

    image-20230612190407357

第9章 类

首先 JS 中把 可以让一组对象在同一个原型对象中继承属性 的东西称之为 。比如,我们可以简单的通过 Object.create() 创建实例 ,那此时 Object 其实就可以被称为一个类。

ES6 之前,类以构造函数的形式进行呈现,所谓构造函数其实就是一个 首字母大写的普通函数

image-20230612190420015

因为它是一个普通函数,所以它既可以通过 构造函数 的形式,配合 new 进行使用。也可以直接调用使用。如果我们想要判断当前的函数是否是通过 new 关键字使用的,那么可以通过 new.target 来进行判定“:

image-20230612190431804

通过构造函数得到的实例,可以利用 instanceof 关键字进行判定

image-20230612190441808

ES6 之后,ES 推出了 class 的语法,利用 class 语法可以直接创建类,而不需要在像之前通过构造函数进行创建了。我们可以使用 class 重写 Range 类:

image-20230612190450512

有了 class 之后,如果想要实现继承,那么就非常简单了,可以直接通过 extends 实现继承

image-20230612190459918

利用 static 关键字,可以为类设置静态方法:静态方法是作为构造函数而非原型对象的属性定义的。

image-20230612190507402

以上代码通过:Range.parse() 调用

因为 class 本质上依然是 原型继承 的机制,所以我们可以通过 prototype 属性来为已有的类添加方法:

image-20230612180421423

视频出处:【(旧)一小时读完《JavaScript权威指南(第7版)》上】 https://www.bilibili.com/video/BV1Ts4y1N7S2/?share_source=copy_web&vd_source=a9f0fd4630ebe41da19ca2c83eb295e6

视频文档出处:https://juejin.cn/post/7211422882252947515

作者:LGD_Sunday


文章作者: QT-7274
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 QT-7274 !
评论
  目录