语言基础

语法

  • 区分大小写

  • 标识符

    • 首字符为字母、下划线、美元符号
    • 建议使用驼峰大小写
    • 关键字、保留字、true、false、null不能为标识符
  • 注释

    • 单行注释//
    • 块注释/**/
  • 严格模式

    • 加一行"use strict"
    • 是一个预处理指令
  • 语句

    • 可以不分号结束,由解析器决定结尾,但建议都手动自己加分号

    • 大括号里是代码块

  • 变量

    • var

      • 函数级作用域
      • 作用域提升
      • 重复声明不报错
      • 全局声明的变量成为window对象属性
    • let

      • 块级作用域

      • 没有提升,有暂时死区TDZ

        • 当程序流程进入新的作用域(module、function、block)进行实例化时,在此作用域中,用let、const声明的变量会先在作用域中被创建,但此时还未进行词法绑定,所以还不能被访问,此时访问会抛出错误。在这运行流程一进入作用域创建变量,到变量开始可被访问之间的一段时间,就称之为TDZ(暂时死区)。

        • let x = 'outer value'
          
          function func () {
            // 这里会产生 TDZ for x
            console.log(x) // TDZ期间访问,产生ReferenceError错误
            let x = 'inner value' // 对x的声明语句,这里结束 TDZ for x
          }
          
          func()
          
      • 不能重复声明

      • 混用varlet,重复声明会报错

      • 全局声明不是window对象的属性

    • const

      • 声明同时必须初始化
      • 初始化后不能修改
      • 不能修改仅限于常量的引用
        • 想让整个对象都不能修改,可以使用Object.freeze()
      • 不能用于声明会自增的迭代变量

数据类型

六种简单数据类型、一种复杂数据类型(Object)。可以使用typeof来确定任意变量的数据类型,它返回一个字符串

Undefined类型

当使用varlet声明了变量但没有初始化时,就相当于给变量赋予了undefined值。在对未初始化的变量调用typeof时,返回的结果是"undefined",但对未声明的变量调用它时,返回的结果还是"undefined"

Null类型

null值表示一个空对象指针,这也是给typeof传一个null会返回"object"的原因。任何时候,只要变量要保存对象,而当时又没有那个对象可保存,就要用null来填充该变量

Boolean类型

两个字面量true、false

要将一个其他类型的值转换为布尔值,可以调用特定的Boolean()转型函数,Boolean()转型函数可以在任意类型的数据上调用,而且始终返回一个布尔值。

NaN返回false

Number类型

  • Number类型使用IEEE754格式表示整数和浮点值。
  • 整数也可以用八进制或十六进制字面量表示。如果字面量中包含的数字超出了应有的范围,就会忽略前缀的零,后面的数字序列会被当成十进制数
  • 正零和负零在所有情况下都被认为是等同的
  1. 浮点数

    • 数值中必须包含小数点,而且小数点后面必须至少有一个数字,没有数字或只有0,则会变成整数
    • 科学计数法,数值(整数或浮点数)后跟一个大写或小写的字母e,再加上一个要乘的10的多少次幂
    • 精度最高达17位小数,算术计算有时会出现偏差,典型例子:0.1+0.2==0.3是false
  2. 值的范围

    • 最小值
      • Number.MIN_VALUE
      • -Infinity
      • Number.POSITIVE_INFINITY
    • 最大值
      • Number.MAX_VALUE
      • Infinity
      • Number.NEGATIVE_INFINITY
  3. NaN

    不是数值

    • 0、+0、-0相除会返回NaN,任何涉及NaN的操作均返回NaNNaN不等于包括NaN在内的任何值
    • isNaN()函数。该函数接收一个参数,可以是任意数据类型,然后判断这个参数是否“不是数值”。
      1. 首先会调用对象的valueOf()方法,然后再确定返回的值是否可以转换为数值
      2. 如果不能,再调用toString()方法,并测试其返回值
  4. 数值转换

    1. Number()
      • 布尔:0、1
      • 数值:返回
      • null:0
      • undefinerNaN
      • 字符串
        • 正负号+数字字符,转换回十进制数值
        • 字符串前含有十六进制格式(0xf),返回这个十六进制对应的十进制
        • 空字符串:0
        • 其他字符情况:NaN
      • 对象
        • 调用valueOf()方法,再按上述规则转换,如果转换为NaN,则调用toString()方法,再按字符串规则转换
    2. parseInt()
      • 如果第一个字符不是数值字符、加号或减号,parseInt()立即返回NaN
      • 如果是,则继续依次检测每个字符,直到字符串末尾,或碰到非数值字符
      • 字符串以”0x”开头,就会被解释为十六进制整数
      • 以”0”开头,且紧跟着数值字符,在非严格模式下会被某些实现解释为八进制整数。
      • parseInt()也接收第二个参数,用于指定底数(进制数)。
    3. parseFloat()
      • parseInt()函数相似,从位置0开始检测每个字符。同样,它也是解析到字符串末尾或者解析到一个无效的浮点数值字符为止。
      • 始终忽略字符串开头的零
      • 十六进制数值始终会返回0。因为parseFloat()只解析十进制值,因此不能指定底数

String类型

双引号、单引号、反引号

  • 以某种引号作为字符串开头,必须仍然以该种引号作为字符串结尾。
  • 字符串的长度可以通过其length属性获取
  • 字符串是不可变的(immutable),意思是一旦创建,它们的值就不能变了。要修改某个变量中的字符串值,必须先销毁原始的字符串,然后将包含新值的另一个字符串保存到该变量
  • 字符串值也有toString()方法,该方法只是简单地返回自身的一个副本,nullundefined值没有toString()方法
  • 模板字面量保留反引号内部的空格,可以跨行定义字符串

字符串插值

  • 模板字面量不是字符串,而是一种特殊的JavaScript句法表达式,只不过求值后得到的是字符串
  • 所有插入的值都会使用toString()强制转型为字符串,而且任何JavaScript表达式都可以用于插值。嵌套的模板字符串无须转义

标签函数

  • 会接收被插值记号分隔后的模板和对每个表达式求值的结果。
  • 接收到的参数依次是原始字符串数组和对每个表达式求值的结果。

原始字符串

  • 可以直接获取原始的模板字面量内容(如换行符或Unicode字符),而不是被转换后的字符表示
  • 默认的String.raw标签函数

Symbol类型

符号实例是唯一、不可变的,用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险。符号就是用来创建唯一记号,进而用作非字符串形式的对象属性。

  • 使用Symbol()函数初始化,可以传入一个字符串参数作为对符号的描述,字符串参数与符号定义或标识完全无关

  • Symbol()函数不能与new关键字一起作为构造函数使用。这样做是为了避免创建符号包装对象

    • 确实想使用符号包装对象,可以借用Object()函数
  • 全局符号注册表

    • 如果运行时的不同部分需要共享和重用符号实例,那么可以用一个字符串作为键,在全局符号注册表中创建并重用符号。

    • 方法:Symbol.for()

      • 都执行幂等操作
      • 检查全局运行时注册表,发现不存在对应的符号,于是就会生成一个新符号实例并添加到注册表中。后续使用相同字符串的调用同样会检查注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例。
    • 全局注册表中的符号必须使用字符串键来创建,所以参数都会转换成字符串

    • Symbol.keyFor()来查询全局注册表

      • 接收符号,返回该全局符号对应的字符串键。如果查询的不是全局符号,则返回undefined
      • 如果传给Symbol.keyFor()的不是符号,则该方法抛出TypeError
    • 凡是可以使用字符串或数值作为属性的地方,都可以使用符号

    • Object.getOwnPropertyNames()返回对象实例的常规属性数组

    • Object.getOwnPropertySymbols()返回对象实例的符号属性数组

    • Object.getOwnPropertyDescriptors()会返回同时包含常规和符号属性描述符的对象

    • Reflect.ownKeys()会返回两种类型的键

常用内置符号

  1. Symbol.asyncIterator
    • 返回对象默认的AsyncIterator。由for-await-of语句使用
    • 循环时,它们会调用以Symbol.asyncIterator为键的函数,并期望这个函数会返回一个实现迭代器API的对象
  2. Symbol.hasInstance
    • 该方法决定一个构造器对象是否认可一个对象是它的实例。由instanceof操作符使用
    • instanceof操作符可以用来确定一个对象实例的原型链上是否有原型
    • 这个属性定义在Function的原型上
  3. Symbol.isConcatSpreadable
    1. 数组对象
      • 默认:打平到已有数组
      • true:打平到已有数组
      • false:将整个数组对象追加到数组末尾
    2. 类数组对象
      • 默认:将对象追加到数组末尾
      • true:打平到已有数组
      • false:将对象追加到数组末尾
    3. 不是数组,也不是类数组
      • 默认:将对象追加到数组末尾
      • true:忽略
      • false:将对象追加到数组末尾
  4. Symbol.iterator
    • 该方法返回对象默认的迭代器。由for-of语句使用
  5. Symbol.match
    • 用正则表达式去匹配字符串。由String.prototype.match()方法使用
  6. Symbol.replace
    • 替换一个字符串中匹配的子串。由String.prototype.replace()方法使用
  7. Symbol.search
    • 返回字符串中匹配正则表达式的索引。由String.prototype.search()方法使用
  8. Symbol.species
    • 作为创建派生对象的构造函数
    • 用Symbol.species定义静态的获取器(getter)方法,可以覆盖新创建实例的原型定义
  9. Symbol.split
    • 该方法在匹配正则表达式的索引位置拆分字符串。由String.prototype.split()方法使用
  10. Symbol.toPrimitive
    • 该方法将对象转换为相应的原始值。由ToPrimitive抽象操作使用
  11. Symbol.toStringTag
    • 该字符串用于创建对象的默认字符串描述。由内置方法Object.prototype.toString()使用
    • 通过toString()方法获取对象标识时,会检索由Symbol.toStringTag指定的实例标识符,默认为"Object"。内置类型已经指定了这个值,但自定义类实例还需要明确定义

Object类型

就是一组数据和功能的集合,通过new操作符后跟对象类型的名称来创建

  • constructor
    • 创建当前对象的函数
  • hasOwnProperty(propertyName)
    • 用于判断当前对象实例上是否存在给定的属性
  • isPrototypeOf(Object)
    • 判断当前对象是否为另一个对象的原型
  • propertyIsEnumerable(propertyName)
    • 判断给定的属性是否可以使用for-in语句枚举
  • toLocaleString()
    • 返回对象的字符串表示,该字符串反映对象所在的本地化执行环境
  • toString()
    • 返回对象的字符串表示
  • valueOf()
    • 返回对象对应的字符串、数值或布尔值表示。通常与toString()的返回值相同。

流控制语句

  • if
  • do-while
  • while
  • for
  • for-in
    • 用于枚举对象中的非符号键属性
  • for-of
    • 用于遍历可迭代对象的元素
  • 标签语句
    • 主要用于嵌套循环
    • 和c++的goto差不多
  • break、continue
  • switch

理解函数

  • function关键字声明,后跟一组参数,然后是函数体
  • 通过函数名来调用函数,要传给函数的参数放在括号里
  • 不需要指定是否返回值。任何函数在任何时间都可以使用return语句来返回函数的值,用法是后跟要返回的值