语言基础

小结

1. 语法特点?

  • 区分大小写
  • 标识符(变量、函数、属性或函数参数的名称)可以由数字、字母、下划线、美元符号组成,但是第一个字符不能是数字
  • 单行注释 //,多行注释 /* */
  • 语句以分号结尾,也可以不用,多条语句用代码块包裹 {}

2. let、var 和 const

  • 作用域:let 和 const 声明的变量的作用域是块级的,var 声明的变量的作用域是函数级的
  • windows 对象:let 和 const 在全局作用域中声明的变量不会成为 window 对象的属性
  • 变量提升:var 存在变量提升,let 和 const 存在暂时性死区
  • 重复声明:可以多次使用 var 关键字声明同一个变量,不能在同一个块级作用域中多次使用 let 和 const 关键字声明同一个变量
  • 声明常量:使用 const 声明的变量必须进行初始化,并且不能再次赋值

3. 数据类型

ECMAScript 标准定义了 8 种数据类型:

  • 7 种基本类型:Undefined、Null、Boolean、Number、String、Symbol(ECMAScript 6 新增)和 BigInt(ECMAScript 2020 新增)
  • 1 种引用类型:Object

4. null 和 undefined

  • null 表示一个空对象指针,typeof null 返回 object
  • undefined 表示一个未初始化的变量,typeof undefined 返回 undefined

5. 转布尔值为 false 的值''0NaNnullundefined

6. 转数值

以下三个函数最终得到的都是十进制数或者 NaN

Number()

  • null 转 0,undefined 转 NaN
  • 布尔值:true 转 1,false 转 0
  • 字符串:
    • '' 转为 0
    • 如果字符串中只包含数字(包括前面带正负号和带小数点的情况),则将其转换为十进制数值
    • 否则为 NaN
  • 对象:调用对象的 valueOf() 方法,然后依照前面的规则转换返回的值。如果转换的结果是 NaN,则调用对象的 toString() 方法,然后再次依照前面的规则转换返回的值

parseInt() 区别与 Number()

  • null 转 NaN
  • 字符串:
    • '' 转为 NaN
    • 从第一个非空格字符开始解析,直到遇到一个非数字字符为止
    • 如果字符串以 0x 开头,则按照十六进制整数解析
    • 如果字符串以 0 开头,则按照八进制整数解析
  • 可以传入第二个参数指定进制

parseFloat() 区别与 parseInt()

  • 只解析十进制数,不能指定底数
  • 如果字符串表示整数,则返回整数
    • console.log(parseFloat('0x6')) // 0

7. 转字符串

  • toString():
    • null 和 undefined 没有这个方法
    • 数值调用时可以传入底数,表示转换为对应进制的字符串
  • String():
    • null 和 undefined 会返回 'null' 和 'undefined'
    • 如果值有 toString() 方法,则调用该方法(不传参数)并返回结果

ECMA-262 以一个名为 ECMAScript 的伪语言(pseudo language)的形式,定义了 JavaScript 的所有这些方面

数据类型

Number 类型

Number 类型使用 IEEE754 格式来表示整数和浮点值

超出 Number.MAX_VALUE 的值会被转换为 Infinity

isFinite() 函数可以用来判断一个数值是否有限

const result = Number.MAX_VALUE + Number.MAX_VALUE
console.log(isFinite(result)) // false

NaN 是一个特殊的数值,表示一个本来要返回数值的操作失败了

console.log(0 / 0) // NaN
console.log(Infinity / Infinity) // NaN
console.log(5 / 0) // Infinity

console.log(NaN == NaN) // false

// isNaN() 函数可以用来判断一个数值是否是 NaN
console.log(isNaN(NaN)) // true

String 类型

String 类型表示零或多个 16 位 Unicode 字符序列

模板字面量标签函数:

const a = 6
const b = 9

const zipTag = (strings, ...expressions) => {
	console.log(strings)
	console.log(expressions)
	return expressions.reduce((prev, cur, i) => {
		return prev + cur + strings[i + 1]
	}, strings[0])
}
const taggedResult = zipTag`${a} + ${b} = ${a + b}`

console.log(taggedResult)

String.raw() 函数:获取原始字符串

console.log(String.raw`Hi\n${2 + 3}!`) // Hi\n5!

Symbol 类型

Symbol 的用途是确保对象属性使用唯一标识符,没有字面量语法

  • 使用 Symbol() 函数来初始化

    • 可以传入一个字符串参数作为对符号的描述,但是只是用于调试,与符号的定义和标识没有关系
    • 不能与 new 关键字一起作为构造函数使用,避免创建符号包装对象
  • 在全局符号注册表中创建并重用符号,使用 Symbol.for() 函数

    • 如果传入的字符串参数已经存在,则返回已有的符号
    • 如果不存在,则创建一个新的符号
  • Symbol.keyFor() 函数

    • 接收符号,返回该全局符号对应的字符串键名
    • 如果查询的不是全局符号,则返回 undefined
    • 如果接收到的不是符号,则抛出 TypeError 异常
    const s1 = Symbol.for('foo')
    console.log(Symbol.keyFor(s1)) // foo
    
  • 凡是可以使用字符串或者数值作为属性的地方,都可以使用符号

    const s1 = Symbol('foo')
    const s2 = Symbol('bar')
    const s3 = Symbol('baz')
    
    const o = { [s1]: 'foo val' }
    Object.defineProperty(o, s2, { value: 'bar val' })
    Object.defineProperties(o, {
    	[s3]: { value: 'baz val' },
    })
    console.log(o)
    
    • Object.getOwnPropertyNames() 返回对象实例的常规属性数组;Object.getOwnPropertySymbols() 返回对象实例的符号属性数组,两个方法的返回值彼此互斥;Object.getOwnPropertyDescriptors() 会返回同时包含常规和符号属性描述符的对象;Reflect.ownKeys() 会返回两种类型的键
    const s1 = Symbol('foo')
    const s2 = Symbol('bar')
    
    const o = {
    	[s1]: 'foo val',
    	[s2]: 'bar val',
    	baz: 'baz val',
    	qux: 'qux val',
    }
    
    // ['baz', 'qux']
    console.log(Object.getOwnPropertyNames(o))
    
    // [Symbol(foo), Symbol(bar)]
    console.log(Object.getOwnPropertySymbols(o))
    
    /**
    {
      "baz": {
          "value": "baz val",
          "writable": true,
          "enumerable": true,
          "configurable": true
      },
      "qux": {...},
      Symbol(bar): {...},
      Symbol(foo): {...}
    }
     */
    console.log(Object.getOwnPropertyDescriptors(o))
    
    // ['baz', 'qux', Symbol(foo), Symbol(bar)]
    console.log(Reflect.ownKeys(o))
    

Object 类型

每个 Object 实例都有如下属性和方法:

  • constructor,用于创建当前对象的函数
  • hasOwnProperty(propertyName):用于判断当前对象实例(不是原型)上是否存在给定的属性。要检查的属性名必须是字符串或者符号
  • isPrototypeOf(object):用于判断当前对象是否是另外一个对象的原型
  • propertyIsEnumerable(propertyName):用于判断给定的属性是否可以使用 for-in 语句枚举
  • toLocaleString()
  • toString()
  • valueOf(),返回对象的字符串、数值或者布尔值表示,通常与 toString() 返回值相同

操作符

一元操作符

  1. 递增 ++ 、递减 -- 操作符

    • 后缀版与前缀版的主要区别在于,后缀版递增和递减语在语句被求值后再发生

    • 作用于非数值时,会先使用 Number() 函数将其转换为数值,再进行操作

  2. 一元加和减

    • 一元加操作符会尝试将操作数转换为数值
    • 一元减操作符会尝试将操作数转换为数值,并对其取负

位操作符

ECMAScript 中的所有数值都以 IEEE754 64 位格式存储,但是位操作符先把数值转换为 32 位整数,再进行操作,最后再将结果转换回 64 位

有符号整数使用 32 位的前 31 位表示整数值,第 32 位(第一位 表示 20)表示符号,0 表示正数,1 表示负数。这一位称为 符号位,它的值决定了数值其余部分的格式

正值以真正的二进制格式存储,负值则以二进制补码形式存储

位操作应用到非数值,首先会使用 Number() 函数将该值转换为数值,然后再应用位操作

ECMAScript 中的所有整数都表示为有符号数。特殊值 NaN 和 Infinity 在位操作中都会被当成 0

  1. 按位非(~)

    • 求操作数的反码
    • 最终效果是对数值取反并减 1‘
  2. 按位与(&)

    • 两个操作数的对应位都是 1 时,结果为 1,否则为 0
  3. 按位或(|)

    • 两个操作数的对应位只要有一个是 1,结果为 1,否则为 0
  4. 按位异或(^)

    • 两个操作数的对应位不相同时,结果为 1,否则为 0
  5. 左移(<<)

    • 将数值的所有位向左移动指定的位数,空位补 0
    • 左移会保留操作数值的符号,比如 -2 左移 5 位,结果为 -64
  6. 有符号的右移(>>)

    • 将数值的所有位向右移动指定的位数,空位补 0
    • 有符号右移会保留操作数值的符号,比如 -64 右移 5 位,结果为 -2
  7. 无符号右移(>>>)

    • 将数值的所有 32 位都向右移动指定的位数,空位补 0

布尔操作符

  1. 逻辑非(!)

    • 对操作数求反
    • 对非布尔值求反,会先使用 Boolean() 函数将其转换为布尔值,再求反
  2. 逻辑与(&&)

    • 短路操作符
  3. 逻辑或(||)

    • 短路操作符

乘性操作符

如果操作数不是数值,会先使用 Number() 函数将其转换为数值,再进行操作

  1. 乘法(*)

    • 如果操作数都是数值,则执行常规的乘法运算。如果 ECMAScript 不能表示乘积,则返回 Infinity 或 -Infinity
    • 如果有任一操作数是 NaN,则返回 NaN
    • 如果是 Infinity 与 0 相乘,则返回 NaN
  2. 除法(/)

    • 如果操作数都是数值,则执行常规的除法运算。如果 ECMAScript 不能表示商,则返回 Infinity 或 -Infinity
    • 如果有任一操作数是 NaN,则返回 NaN
    • 如果是 Infinity 除以 Infinity,则返回 NaN
    • 如果是 0 除以 0,则返回 NaN
    • 如果是非 0 的有限数除以 0,则返回 Infinity 或 -Infinity
    • 如果是 Infinity 除以任何数值,则返回 Infinity 或 -Infinity
  3. 求模(%)

    • 如果操作数都是数值,则执行常规的除法运算,返回余数
    • 如果被除数是无限值,或者除数是 0,则返回 NaN
    • 如果被除数是有限值,除数是无限值,则返回被除数
    • 如果被除数是 0 ,除数不是 0,则返回 0

指数操作符

  1. 指数操作符(**)

    • ES7 新增的操作符,与 Math.pow() 方法相同
    • console.log(3 ** 2) // 9
    • squared **= 2 // 指数赋值操作符

加性操作符

  1. 加法操作符(+)

    • 如果两个操作数都是数值
      • 有任一操作数是 NaN,则返回 NaN
      • 如果 Infinity 加 -Infinity,则返回 NaN
      • 如果 -0 加 +0,则返回 +0
    • 如果有一个操作数是字符串
      • 将另一个操作数转换为字符串,然后返回连接后的字符串
    • 如果有任一操作数是对象、数值或布尔值
      • 调用它们的 toString() 方法,对于 null 和 undefined 则调用 String() 函数,然后再应用前面的规则
  2. 减法操作符(-)

    • 如果两个操作数都是数值
      • 有任一操作数是 NaN,则返回 NaN
      • 如果是同符号的无限制相减,则返回 NaN
      • 如果是同符号的 0 相减,则返回 +0
    • 如果有任一操作数是字符串、布尔值、null 或 undefined
      • 首先调用 Number() 函数将其转换为数值,然后再应用前面的规则
    • 如果有任一操作数是对象
      • 调用它们的 valueOf() 方法,如果结果是 NaN,则减法的计算结果就是 NaN。如果对象没有 valueOf() 方法,则调用 toString() 方法,然后再将得到的字符串转为数字

关系操作符

<><=>=

  • 如果有任一操作数是数值或布尔值,则执行数值比较
  • 任何关系操作符在涉及比较 NaN 时,都返回 false
  • 如果两个操作数都是字符串,则逐个比较字符串中对应字符的编码
  • 如果有任一操作符是对象,则调用 valueOf() 方法,用得到的结果按照前面的规则进行比较。如果没有 valueOf() 方法,则调用 toString() 方法,再用得到的结果按照前面的规则进行比较

相等操作符

  1. 相等和不相等(== 和 !=)

    • 如果有任一操作数是布尔值,则在比较相等性之前先将其转换为数值
    • 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值
    • 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf() 方法,用得到的基本类型值按照前面的规则进行比较
    • 如果两个操作数都是对象,则比较它们是不是同一个对象
    • null 和 undefined 是相等的
    • 如果有任一操作数是 NaN,则相等操作符返回 false,不相等操作符返回 true
  2. 全等和不全等(=== 和 !==)

    • 与相等和不相等操作符的区别是,全等和不全等操作符不会在进行比较之前转换操作数

条件操作符

variable = boolean_expression ? true_value : false_value

赋值操作符

简单赋值用 = 表示

每个数学操作符以及其他一些操作符都有对应的复合赋值操作符,如 +=-=*=/=%=**=<<=>>=>>>=

逗号操作符

逗号操作符可以用来在一条语句中执行多个操作,如

let num1 = 1,
	num2 = 2,
	num3 = 3

在赋值语句中,逗号操作符会返回表达式中的最后一项,如

let num = (5, 1, 4, 8, 0) // num 的值为 0

语句

if 语句

if (expression) statement1 else statement2

do-while 语句

do {
	statement
} while (expression)

while 语句

while (expression) statement

for 语句

for (initialization; expression; post - loop - expression) statement

for-in 语句

for (property in expression) statement

ECMAScript 中对象的属性是无序的,因此通过 for-in 循环输出的属性名的顺序是不可预测的。换句话说,所有可枚举的属性都会返回一次,但返回的顺序可能会因浏览器而异

for-of 语句

for (variable of object) statement

for-of 循环会按照可迭代对象的 next() 方法产生值的顺序迭代元素。如果尝试迭代的变量不支持迭代,则会抛出错误

标签语句

用于给语句加标签

label: statement

在下面的例子中, start 是一个标签,可以在后面通过 break 或 continue 语句引用它

start: for (let i = 0; i < count; i++) {
	console.log(i)
}

break 和 continue 语句

break 和 continue 都可以与标签语句一起使用,返回代码中特定的位置。通常是在嵌套循环中,如:

let num = 0
outermost: for (let i = 0; i < 10; i++) {
	for (let j = 0; j < 10; j++) {
		if (i === 5 && j === 5) {
			break outermost
		}
		num++
	}
}
console.log(num) // 55

with 语句

严格模式下不允许使用 with 语句

with 语句影响性能且难于调试其中的代码,因此不建议使用

with 语句的作用是将代码的作用域设置到一个特定的对象中

with (expression) statement

使用 with 语句的主要场景是针对一个对象反复操作,如:

let qs = location.search.substring(1)
let hostName = location.hostname
let url = location.href

上面的每一行都包含 location 对象,如果使用 with 语句,可以简化为:

with (location) {
	let qs = search.substring(1)
	let hostName = hostname
	let url = href
}

switch 语句

switch (expression) {
	case value1:
		statement
		break
	case value2:
		statement
		break
	case value3:
		statement
		break
	default:
		statement
}

函数

最佳实践是函数要么返回值,要么不返回值。只在某个条件下返回值的函数会带来麻烦,尤其是调试时

严格模式对函数有一些限制:

  • 函数不能以 eval 或 arguments 作为名称
  • 函数的参数不能叫 eval 或 arguments
  • 两个命名参数不能同名
Last Updated: