前言
ES6 同样是 ECMAScript 的拓展,发布于 2015年,目前多数浏览器对于部分 ES6 更新内容不支持,通常需要借助 bable
编译器编译成 ES5
或者 ECMAScript
早期版本语法去执行。
ES6 的学习推荐阮一峰老师的 ES6 教程。
ES6
ES6 是 ECMAScript 最近一次较大版本的更新,更新内容主要是一些优化,包括开发效率的优化,以及之前版本问题的优化等。
let 和 const
-
let
let
关键字用于定义变量,优化掉了var
关键字。let
关键字定义变量的特点:-
在
{}
内部定义变量,会生成块级作用域。{ var a = 1 let b = 1 } console.log(a) // 1 console.log(b) // ReferenceError
注意:块级作用域内重复声明全局变量,则该变量绑定块级作用域。
-
抛出变量提升引起的问题。
console.log(a) // undefined var a = 1 console.log(b) // ReferenceError let b = 1
-
抛出重复声明的问题。
// 重复声明变量 function fun1() { var a = 1 var a = 2 console.log(a) // 2 } fun1() function fun2() { let a = 1 var a = 2 console.log(a) // SyntaxError:already been declared } fun2() // 重复声明参数 function fun3(x) { var x = 1 console.log(x) // 1 } fun3() function fun4(x) { let x = 1 console.log(x) // SyntaxError:already been declared } fun4()
-
-
const
const
关键字用于定义常量,常量一旦声明,必须立即赋值。const
定义常量的特点:-
在
{}
内部定义常量,会生成块级作用域。{ const A = 1 } console.log(A) // ReferenceError
-
定义常量无法修改。
const A = 1 A = 2 // TypeError: Assignment to constant variable
注意:const 也具备 let 相同的特点。
-
解构赋值
-
数组解构
数组解构分为两种:常规解构和默认值解构
-
常规解构
let [a, b, c] = [1, 2, 3] console.log(a, b, c) // 1, 2, 3 let [x, , y, z] = [1, 2, 3] console.log(x, y, z) // 1, 3, undefined let [i] = [1, 2, 3] console.log(i) // 1 let [u, ...v] = [1, 2, 3] console.log(u, v) // 1, [2, 3]
-
默认值解构
let [a = 4, b, c, d = 3] = [1, 2] console.log(a, b, c, d) // 1, 2, undefined, 3
注意:解构有值,则默认值无效,解构无值,则默认值有效。
-
-
对象解构
对象解构分为三种:常规解构,换名解构,默认值解构。
-
常规解构
let obj = { name: 'jim', age: 20 } let { name, age, gender } = obj console.log(name, age, gender) // jim, 20, undefined
-
换名解构
let obj = { name: 'jim', age: 20 } let { name: call } = obj console.log(call) // jim
注意:换名解构相当于定义一个变量,通过解构出来的值进行赋值操作。
-
默认值解构
let obj = { name: 'jim', age: 20 } let { name: call = 'jack', gender: sex = 'male' } = obj console.log(call, sex) // jim, male
注意:解构有值,则默认值无效,解构无值,则默认值有效。
-
-
字符串解构
字符串解构分为两种:字符解构和长度解构
-
字符解构
let [a, b, , c, d] = 'hello' console.log(a, b, c, d) // h, e, l, o
-
长度解构
let { length: len } = 'hello' console.log(len) // 5
-
-
函数参数解构
函数参数解构分为两种:数组参数解构和对象参数解构
-
数组参数解构
function sum(x, y) { return x + y } sum(...[1,2]) // 3
-
对象参数解构
function sum({ x = 0, y = 1 } = {}) { return x + y } sum() // 1 sum({ x: 1 }) // 2 sum({ x: 2, y: 2 }) // 4
-
模板字符串
模板字符串是一种支持内置表达式的字符串,简化字符串拼接操作。
let price = 12.5
let count = 5
console.log(`
单价:¥${price.toFixed(2)}
数量:${count}
=====================
总价:¥${(price * count).toFixed(2)}
`)
模板字符串的特点:
-
模板字符串使用 `` 反引号包裹。
-
模板字符串支持使用 js 表达式,使用
${}
插值,插值内为 js 环境。 -
模板字符串支持换行。
BigInt
JavaScript 在数值表示存在两个问题:
-
大于或等于 2^53 的数值无法保证精度。
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
-
大于或等于 2^1024 的数值无法表示,会返回
Infinity
。Math.pow(2, 1024) // Infinity
注意:可以使用
isInfinity()
判断是否为Infinity
。
BigInt 类型用于解决上述问题,没有位数限制,任何位数的整数都可以精确表示。
注意:
a. BigInt 类型与普通整数数值相等,类型不同。如
2n == 2
返回 true,2n === 2
返回false。b. BigInt 类型不能使用
+
表示正数,会编译成隐式转换,而 BigInt 类型无法转换 Number 类型。c. BigInt 类型可以正常进行相关运算。
定义 BigInt 类型的方式:
-
字面量定义
let x = 2n // 整数后缀 n,表示 bigint 类型 typeof x // bigint
-
转换函数定义
let x = BigInt(2) typeof x // bigint
注意:
BigInt()
只能转换整型、整型字符串以及 boolean 值,其他无法转换,抛出错误。
运算符拓展
-
指数运算符
let a = 2 ** 3 // 2^3 a **= 2 // 64
-
链判断运算符
链判断运算符用于判断对象的属性和方法是否存在。
let obj = { a: '1', b: { c: 2 }, run() { console.log('running') } } // ES6 之前 let x = obj && obj.b && obj.b.c || undefined // ES6 之后 let x = obj?.b?.c // 属性存在则赋属性值,不存在则赋值 undefined obj.run?.() // 方法存在则执行方法,不存在则不执行
-
Null 判断运算符
变量赋值默认值的方法
let x = y || 300 // y 为变量
上例中当 y 存在则赋值给 x,当 y 不存在则赋值 x 为 300。y 不存在具体含义:y 为 undefined 或者 null 时,当上例中 y 为 0 或 false 也满足不存在的条件。
// ES6 let x = y ?? 300
只有当 y 为 undefined 或者 null 时,才会给 x 赋值 300。
-
逻辑赋值运算符
逻辑赋值运算符有:
&&=
,||=
,??=
。x &&= y
等同于x = x && y
。
函数拓展
-
参数拓展
-
函数参数默认值
// ES6 之前 function sum(x, y) { y = y ?? 0 return x + y } // ES6 function sum(x, y = 0) { return x + y }
-
rest 参数
// ES6 之前 function sum() { console.log(arguments) // Arguments[1,2] 类数组对象 } // ES6 function sum(...params) { console.log(params) // [1,2] 数组 } sum(1,2)
注意:
...params
参数之后不能再出现任何形参,否则报错。之前出现形参,则为形参解构实参。
-
-
箭头函数
箭头函数是对匿名函数的简写方式。
let log = function (info) { console.log(info) } let log = info => { console.log(info) } // 函数只有一个参数,则可以不使用 `()` 包括。 let sum = function (x, y) { return x + y } let sum = (x, y) => x + y // 函数只有返回值一句,则可以省略 `{}`。 let co = function (a, b) { return { name: a, age: b } } let co = (a, b) => ({ name: a, age: b }) // 返回值是一个对象,则需要使用 `()` 包括。
注意:
a. 箭头函数内部的
this
始终指向其上层作用域的this
,不可改变,即call
,apply
,bind
无效。let obj = { name: 'json', age: 20 } function outer() { let inner = () => { console.log('inner',this) } inner() console.log('outer', this) } outer() // inner window outer window outer.call(obj) // inner {name: 'json', age: 20}, outer {name: 'json', age: 20}
b. 箭头函数内部不可以使用
arguments
获取参数,会抛出错误。c. 不能使用
new
和yield
命令,即不能作为构造函数和 Generator 函数。
数组拓展
-
扩展运算符
扩展运算符指
...
运算符,作用:-
数组打散
let arr = [1, 2, 3] console.log(...arr) // 1 2 3
-
数组解构
let arr = [1, 2, 3] let [ a, ...b ] = arr console.log(a, b) // 1, [2, 3]
-
对象解构
let obj = { a: 1, b: 2, c: 3 } let { a, ...rest } = obj console.log(a, rest) // 1, { b: 2, c: 3 }
-
-
Array.from()
可将类数组对象和可迭代对象(如 Set 和 Map)转换为真正的数组。
let arr = Array.from(arr-like)
-
Array.of()
将一组参数转换为数组。
let arr = Array.of(1,2,3) // [1, 2, 3]
对象拓展
-
属性和方法简化
let name = 'jim' let age = 21 // ES6 之前 let obj = { name: name, age: age, run: function() { return `${this.name} is running` } } // ES6 let obj = { name, // 同名属性可以简写 age, run() { return `${this.name} is running` // 方法可以简写 } }
-
遍历对象属性
对象属性中的
enumerable
特性设置为 true,表示当前对象属性可以遍历。遍历对象属性的方法有:-
for...in
let obj = { name: 'jason', age: 18 } for(let key in obj) { console.log(key, obj[key]) // name jason, age 18 }
-
Object.keys(obj)
let obj = { name: 'jason', age: 18 } let res = Object.keys(obj) console.log(res) // ['name', 'age']
-
Object.getOwnPropertyNames(obj)
let obj = { name: 'jason', age: 18 } let res = Object.getOwnPropertyNames(obj) console.log(res) // ['name', 'age']
-
Reflect.ownKeys(obj)
let obj = { name: 'jason', age: 18 } let res = Reflect.ownKeys(obj) console.log(res) // ['name', 'age']
-
-
新增对象方法
-
Object.is()
0 === -0 // true NaN === NaN // false Object.is(0, -0) // false Object.is(NaN, NaN) // true
-
Object.assign()
Object.assign()
接收多个参数对象,第一个参数是目标对象,后面参数都是源对象,该方法是将源对象的所有属性复制到目标对象,返回一个合并后的对象。如果存在同名属性,则后面的属性会覆盖前面的属性。let obj1 = { a: 1, b: 2, c: 3 } let obj2 = { d: 4, e: 5, a: 6 } Object.assign({}, obj1, obj2) // {a: 6, b: 2, c: 3, d: 4, e: 5}
目标对象参数不为对象,则报错,源对象参数不为对象,会进行对象转化,转化后处理,只有 String 和 Array 类型可以转换。
Object.assign({}, 1.5) // {} Object.assign({}, true) // {} Object.assign({}, null) // {} Object.assign({}, undefined) // {} Object.assign({}, 'hello') // {0: 'h', 1: 'e', 2: 'l', 3: 'l', 4: 'o'} Object.assign({}, [1,2,3]) // {0: 1, 1: 2, 2: 3}
Object.assign()
拷贝对象属于浅拷贝,参数对象的属性为引用类型,实际拷贝的是属性的引用。let obj = { a: '1', b: { c: 2 } } let cobj = Object.assign({}, obj) obj.b.c = 20 console.log(cobj) // { a: '1', b: { c: 20 } }
实现深拷贝的两种方式:
// 方式一 let cobj = JSON.parse(JSON.stringify(obj))
注意:方式一中,对象属性值如果是 undefined,Symbol 类型,Function 类型会被过滤,因此只适合符合 JSON 风格的对象。
// 方式二 function deepCopy(arg) { if(typeof arg === "object") { var result = arg.constructor === Array ? [] : {}; for(let i in arg) { result[i] = typeof arg[i] === 'object' ? deepCopy(arg[i]) : arg[i]; } } else { var result = arg; } return result }
-
Object.hasOwn()
Object.hasOwn(obj, 'attr')
接收两个参数,分别为对象和属性,用于判断attr
是否为obj
自有属性。同obj.hasOwnProperty()
一致。
-
正则拓展
正则拓展是指拓展 JavaScript 的正则语法,未增加新的 API 方法。
-
先行断言
/x(?=y)/
表示匹配在y
之前的x
。 -
先行否定断言
/x(?!y)/
表示匹配在不是y
之前的x
。 -
后行断言
/(?<=y)x/
表示匹配在y
之后的x
。 -
后行否定断言
/(?<!y)x/
表四匹配不是y
之后的x
。/(?<=(o)d\1)r/.exec('hodor') // null /(?<=\1d(o))r/.exec('hodor') // ["r", "o"]
注意:后行断言的分组引用需要将分组引用前置。
-
分组命名
通过
(?<name>)
可以为分组命名,使用reg.exec()
返回的类数组对象可以获取对应分组匹配结果。let reg = /(?<year>\d{4})-(?<month>\d{1,2})-(?<day>\d{1,2})/ let res = reg.exec('2023-3-31') console.log(res.groups.year) // 2023 console.log(res.groups.month) // 3 console.log(res.groups.day) // 31
分组命名以后,可以获取分组名字,则可以通过解构获取对应分组匹配结果。
let reg = /(?<year>\d{4})-(?<month>\d{1,2})-(?<day>\d{1,2})/ let { groups: { year, month, day } } = reg.exec('2023-3-31') console.log(year) // 2023 console.log(month) // 3 console.log(day) // 31
注意:分组命名,可以通过
\k<name>
引用分组。
Set 和 Map
-
Set
-
WeakSet
-
Map
-
WeakMap