JS高程(红宝书)读书笔记---第三章

134 阅读11分钟

语言基础

JS中: 标识符是区分大小写的,并且遵守以下的命名规则:

  • 第一个字符只能是 字母、下划线、或者$符号
  • 其他字符可以是 字母、下划线、$符号、数字

1、变量

JS是中变量是松散的,变量没有类型可言,所有的类型的变量都可以通过var、let、const来定义。三者定义变量方法都是一样的,通过这些关键字,然后跟上标识符作为变量名定义。但是三者定义的变量有一些区别:

var是ES6之前,let与const是ES6之后

var声明

  • 定义的变量具有函数作用域
  • 存在提升现象:JS代码存在编译(时间很短)与执行阶段,在编译阶段会将var定义的变量提升(提升声明)到当前作用域顶部,所以变量的访问可以在变量声明代码之前,不会报错。
  • 同一作用域下可以重复声明同一变量,不会报错。
  • var在全局声明的变量会成为window的属性。

let声明

  • 定义的变量具有块级作用域,是函数作用域的子集,只在当前代码块中可访问。
  • 不存在变量提升,即解析编译代码时,不会将let的变量声明提升至当前作用域顶部,该变量的访问只能在执行完变量声明代码语句后访问到。
  • 不能在同一块级作用域下重复声明同一变量,会报错。
  • let在全局声明的变量不会成为window的属性。

const声明

基本特性都与let类似,但与let的不同之处在于,声明变量时必须初始化变量值,并且声明之后该值不允许修改了。

定义变量的风格的最佳实践

  • 现在已经不使用var
  • 对一些已知值不会变的变量优先使用const,其次使用let。

2、数据类型

ESCMAScript标准中,将数据分为两大类:

  • 简单数据类型(原始类型):有7种:Number、String、Undefined、Null、Boolean、Symbol、BigInt
  • 复杂数据类型(引用类型):笼统来说就是对象Object

Undefined

该类型只有一个值,就是undefined,含义是变量值不存在。如:变量声明但是没有赋值时,该变量的值为undefined。

let a  // a的值为undefined
var b  // b的值为undefined

PS:使用typeof确定数据类型时,对于一个已经声明但未赋值的变量和一个未声明的标识符时得到的结果都是undefined。但是两者是有区别的,前者可以访问,后者会报错。

Null

该类型只有一个值,即null。其标志一个空对象指针,在定义一个将来要保存数据类型为对象类型的变量时,可以使用null初始化。

let obj = null //表明该变量未来是保存对象类型的数据,但是定义时不知道初始化为什么值。
//....未来某个时刻
obj = { a:1 }

Boolean

该类型只有两种值:truefalse,分别表示逻辑真值与逻辑假值,常用在一些流程控制的判断中,但实际任何其他数据类型的值都有对应的布尔值的等价形式,即JS中任何数据类型的值都可以划分为 真性值假性值。JS在需要真性值与假性值的地方(如一些流程控制语句的的条件判断中),都会进行转换(隐式的),就相当于对这些值直接调用Boolean函数

image.png

PS:助记:除undefined、null、0、''(空字符串)、NaN以外,所有其他的值都是真性值。

Number

数值表示

JS中使用 IEEE 754 双精度浮点格式(64位)表示任何数值。即JS内部,所有数字都是以 64 位浮点数形式存储。

数值的精度:IEEE 754 的双精度浮点格式如下:

  • 第一位:符号位,0表示整数,1表示负数。
  • 2-12位(共11位):指数部分。
  • 13-64位(共52位):有效数字部分。

任何一个数字都通过这64位表示: (-1)^符号位的值 * 1(或0).(有效数字部分的值,可以有52位)* 2^指数部分。精度最多只能到53个二进制位,即可以精确表示从 -2^53~2^53内的值。

  • 值的范围:指数长度为11位,则指数部分最大值为2047(2的11次-1)。分出一半表示负数,则JS能够表示的数的范围为2^1024~2^-1023. 如果一个数大于等于2的1024次方,那么就会发生“正向溢出”,即 JavaScript 无法表示这么大的数,这时就会返回Infinity
Math.pow(2, 1024) // Infinity

如果一个数小于等于2的-1075次方(指数部分最小值-1023,再加上小数部分的52位),那么就会发生为“负向溢出”,即 JavaScript 无法表示这么小的数,这时会直接返回0。

Math.pow(2, -1075) // 0

NaN非数

  • 任何涉及NaN的操作都返回NaN。
  • NaN不等于任何值,包括自身。

转换为数值

就是尝试将不是数值类型的值转为数值,JS提供三种方法:

  • Number:基于以下基本规则
    • 布尔值:true转为1,false转为0
    • null:转为0
    • undefined:转为NaN
    • 字符串:根据以下规则:
      • 空字符串转为0
      • 能够视为表示数字的字符串则转为对应的数字
      • 其他字符串转为NaN
    • 对象:先将对象转为原始值(对其调用valueOf方法,一般情况下对象没有重写valueOf方法时,返回对象本身,如果valueOf返回不是原始值,则调用toString方法),对结果按上述规则进行转换。
    • 值得一提的是:对于数组,valueOf()方法返回数组本身,然后调用toString方法,数组的toString方法其实就是将数组元素根据,进行连接成字符串,等价于 arr.join(',')。所以对于空数组而言,Number转换的结果是0;只有一个元素的数组而言,Number转换后的结果为该元素调用Number的结果。
let o = {
    valueOf(){
        return '11'
    }
}
Number(o)
Number([]) //0
Number([2]) //2
Number(['a']) //NaN
  • ParseInt:从字符串中第一个非空格开始,如果第一个非空格不是数值字符、或者加减符号,则立即返回NaN,如果第一个非空格是数值字符、加减号,继续检测其他字符,直到字符串末尾或者遇到非空格字符。 如:' 1123aa' --> 1123,22.5 --> 22
  • ParseFloat:与parseInt类似,但是可以解析第一个遇到的小数点。

String

  • 字符串类型的值使用 单引号、双引号、反引号包裹。
  • 字符串不可变,一旦创建,值就不能改变,要修改变量中存放的字符串的值,只能销毁原来的值,然后将新的字符串保存到该变量。

转换为字符串

  • toString方法:大部分的值(数值、布尔值、对象、字符串值)都有一个toString方法,返回当前值的字符串形式的值。

PS:null与undfeined没有该方法。

let age = 11
age.toString() // '11'

let flag = true
flag.toString() //'true'
  • String函数:按照以下规则转换:
    • 值有toString方法的话,调用toString方法返回结果
    • null --> 'null'
    • undefined --> 'undefined'

3、操作符

一元操作符

自增与自减操作符

分为前缀与后缀版:前缀版的话,变量的值在表达式求值之前就改变;后缀版的话,变量的值在表达式求值之后改变。 举例:

   //前缀版
   let age = 20
   let sumAge = ++age + 2  // 变量值先变化,然后对表达式求值: 21 + 2
   
   //后缀版
   let age = 20
   let sumAge = age++ + 2 // 表达式先求值为20,求值后变量值才发生变化: 20 + 2 

在使用自增或者自减时,操作数不一定要求是数值,其他类型的值也可以使用,按照Number()会发生类型转换:将操作数转为数值后再运算

  • 操作数为字符串,如果是有效的数值形式,转换为数值再运算;不是有效的数值形式,直接得到NaN。
  • 操作数为布尔值,true转为1后运算,false转为0后运算。
  • 对象,转换为原始值后再尝试转为数值。

一元加减操作符

放在变量前,决定数值的正负性。如果变量为非数值类型,会使用Number()进行转换后再操作。

let a = '001'
+a --> 1
-a --> -1

布尔操作符

  1. 逻辑非 !: 可以应用于JS中的任何值,始终返回布尔值。首先将操作数转为布尔值,然后再取反。转为布尔值的规则如前面Boolean中所说,除了那五个值,任何其他值转为布尔值都为true,然后对应取反即可。

  2. 逻辑与 &&:接受两个操作数,这两个操作数可以是任意类型的值。逻辑与的表达式的返回值取决于第一个操作数的布尔性质。如果第一个操作数为假性值,则返回第一个操作数;如果第一个数为真性值,则返回第二个操作数。

    console.log(false && [])  // false
    console.log(null && [])  // null
    console.log('' && [])  // ''
    console.log(NaN && []) // NaN
    console.log(0 && []) //0
    console.log([] && 1)  // 1
    

    PS:逻辑与操作符是惰性求值的,即如果第一个操作数为假性值,则第二个操作数根本不会进行求值。

    let found = undefined
    let a = 1
    let res = (found && ++a)
    console.log(res)  // undefined
    console.log(a)  //仍然为1,因为&&是惰性的,在第一个操作数为求值为false后,直接返回第一个操作数的值,第二个操作数(表达式)就不再进行求值的。
    
  3. 逻辑或||:接受两个操作数,可以是任意类型的值。逻辑或表达式的返回值取决于第一个操作数的布尔性质。第一个操作数为真性值,则直接返回第一个操作数,第二个操作数不会进行求值;如果第一个操作数为假性值,则返回第二个操作数

    console.log([] || null) // []: 第一个操作数为真性值,直接返回第一个操作数的值
    console.log([] || 1)  // []: 第一个操作数为真性值,直接返回第一个操作数的值
    console.log(false || null)  //null:第一个操作数为假性值,返回第二个操作数的值
    console.log('' || 2) // 2:第一个操作数为假性值,返回第二个操作数的值
    

    PS:逻辑或操作也惰性求值的,即如果第一个操作数为真性值,则第二个操作数根本不会求值。

乘性操作符

包括:乘*、除/、取余%。对应数学中的乘、除、取余的运算,但是操作数可以是任意类型的值,当操作数为非数值类型值时,会进行类型转换,相当于使用Number进行转换

指数操作符

计算指数操作,其结合性为从右到左。

let a = 2 ** 3 ** 2 // 结果等价于2 ** 9

加性操作符

包括:加法+与减法-,对应数学中加法与减法运算,但是又略有区别。其操作数可以是任意类型的值,但是会进行类型转换

加法

JS中,加法操作符的作用不仅是数值上的加法操作,还有字符串的拼接作用,具体看操作数的类型。其规则如下:

  • 有复杂类型的操作数时,先转为原始值。(调用toString方法,得到返回值)
  • 有操作数为字符串,则将另一个操作数也转为字符串,进行字符串的拼接操作
  • 操作数没有字符串,将剩下的操作数转为数值进行加法运算

可以理解为加法操作符优先字符串拼接(在有操作数满足字符串的条件时)

减法

减法操作符只有数学上减法运算的功能,即其会将操作数使用Number()转为数值类型进行运算,具体规则如下:

  • 有复杂类型的操作数时,先转为原始值。(调用valueOf()方法)
  • 其他基本类型的值,都通过Number()转为数值型进行运算

关系操作符

比较两个操作数之间‘大小’的操作符,包括:大于、小于、相等,其返回布尔值。并且操作符会将操作数进行类型转换。具体规则如下:

  • 操作数都是数值,进行数值比较
  • 操作数都是字符串,逐位比较字符的编码
  • 有任一操作数为对象,调用其valueOf()方法转为原始值,再进行比较
  • 有任一操作数为布尔值,转为数字进行比较