语言基础
小结
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 的值''
、0
、NaN
、null
、undefined
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() 返回值相同
操作符
一元操作符
递增
++
、递减--
操作符后缀版与前缀版的主要区别在于,后缀版递增和递减语在语句被求值后再发生
作用于非数值时,会先使用 Number() 函数将其转换为数值,再进行操作
一元加和减
- 一元加操作符会尝试将操作数转换为数值
- 一元减操作符会尝试将操作数转换为数值,并对其取负
位操作符
ECMAScript 中的所有数值都以 IEEE754 64 位格式存储,但是位操作符先把数值转换为 32 位整数,再进行操作,最后再将结果转换回 64 位
有符号整数使用 32 位的前 31 位表示整数值,第 32 位(第一位 表示 20)表示符号,0 表示正数,1 表示负数。这一位称为 符号位,它的值决定了数值其余部分的格式
正值以真正的二进制格式存储,负值则以二进制补码形式存储
位操作应用到非数值,首先会使用 Number() 函数将该值转换为数值,然后再应用位操作
ECMAScript 中的所有整数都表示为有符号数。特殊值 NaN 和 Infinity 在位操作中都会被当成 0
按位非(~)
- 求操作数的反码
- 最终效果是对数值取反并减 1‘
按位与(&)
- 两个操作数的对应位都是 1 时,结果为 1,否则为 0
按位或(|)
- 两个操作数的对应位只要有一个是 1,结果为 1,否则为 0
按位异或(^)
- 两个操作数的对应位不相同时,结果为 1,否则为 0
左移(<<)
- 将数值的所有位向左移动指定的位数,空位补 0
- 左移会保留操作数值的符号,比如 -2 左移 5 位,结果为 -64
有符号的右移(>>)
- 将数值的所有位向右移动指定的位数,空位补 0
- 有符号右移会保留操作数值的符号,比如 -64 右移 5 位,结果为 -2
无符号右移(>>>)
- 将数值的所有 32 位都向右移动指定的位数,空位补 0
布尔操作符
逻辑非(!)
- 对操作数求反
- 对非布尔值求反,会先使用 Boolean() 函数将其转换为布尔值,再求反
逻辑与(&&)
- 短路操作符
逻辑或(||)
- 短路操作符
乘性操作符
如果操作数不是数值,会先使用 Number() 函数将其转换为数值,再进行操作
乘法(*)
- 如果操作数都是数值,则执行常规的乘法运算。如果 ECMAScript 不能表示乘积,则返回 Infinity 或 -Infinity
- 如果有任一操作数是 NaN,则返回 NaN
- 如果是 Infinity 与 0 相乘,则返回 NaN
除法(/)
- 如果操作数都是数值,则执行常规的除法运算。如果 ECMAScript 不能表示商,则返回 Infinity 或 -Infinity
- 如果有任一操作数是 NaN,则返回 NaN
- 如果是 Infinity 除以 Infinity,则返回 NaN
- 如果是 0 除以 0,则返回 NaN
- 如果是非 0 的有限数除以 0,则返回 Infinity 或 -Infinity
- 如果是 Infinity 除以任何数值,则返回 Infinity 或 -Infinity
求模(%)
- 如果操作数都是数值,则执行常规的除法运算,返回余数
- 如果被除数是无限值,或者除数是 0,则返回 NaN
- 如果被除数是有限值,除数是无限值,则返回被除数
- 如果被除数是 0 ,除数不是 0,则返回 0
指数操作符
指数操作符(**)
- ES7 新增的操作符,与 Math.pow() 方法相同
console.log(3 ** 2) // 9
squared **= 2 // 指数赋值操作符
加性操作符
加法操作符(+)
- 如果两个操作数都是数值
- 有任一操作数是 NaN,则返回 NaN
- 如果 Infinity 加 -Infinity,则返回 NaN
- 如果 -0 加 +0,则返回 +0
- 如果有一个操作数是字符串
- 将另一个操作数转换为字符串,然后返回连接后的字符串
- 如果有任一操作数是对象、数值或布尔值
- 调用它们的 toString() 方法,对于 null 和 undefined 则调用 String() 函数,然后再应用前面的规则
- 如果两个操作数都是数值
减法操作符(-)
- 如果两个操作数都是数值
- 有任一操作数是 NaN,则返回 NaN
- 如果是同符号的无限制相减,则返回 NaN
- 如果是同符号的 0 相减,则返回 +0
- 如果有任一操作数是字符串、布尔值、null 或 undefined
- 首先调用 Number() 函数将其转换为数值,然后再应用前面的规则
- 如果有任一操作数是对象
- 调用它们的 valueOf() 方法,如果结果是 NaN,则减法的计算结果就是 NaN。如果对象没有 valueOf() 方法,则调用 toString() 方法,然后再将得到的字符串转为数字
- 如果两个操作数都是数值
关系操作符
<
、>
、<=
、>=
- 如果有任一操作数是数值或布尔值,则执行数值比较
- 任何关系操作符在涉及比较 NaN 时,都返回 false
- 如果两个操作数都是字符串,则逐个比较字符串中对应字符的编码
- 如果有任一操作符是对象,则调用 valueOf() 方法,用得到的结果按照前面的规则进行比较。如果没有 valueOf() 方法,则调用 toString() 方法,再用得到的结果按照前面的规则进行比较
相等操作符
相等和不相等(== 和 !=)
- 如果有任一操作数是布尔值,则在比较相等性之前先将其转换为数值
- 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值
- 如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf() 方法,用得到的基本类型值按照前面的规则进行比较
- 如果两个操作数都是对象,则比较它们是不是同一个对象
- null 和 undefined 是相等的
- 如果有任一操作数是 NaN,则相等操作符返回 false,不相等操作符返回 true
全等和不全等(=== 和 !==)
- 与相等和不相等操作符的区别是,全等和不全等操作符不会在进行比较之前转换操作数
条件操作符
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
- 两个命名参数不能同名