1. let和const命令
1.1 语法
var a = 6
let a = 6
const a = 6
1.2 实例
如上语法;
for(let i = 0; i < 6; i++) {
// 操作
}
1.3 拓展
1.3.1 变量提升
即变量在声明之前使用,值为 undefined。
var 有,let 和 const 没有。
推荐使用const和let定义变量,尽量不要使用var
// var
console.log(aa) // 输出 undefined
// let || const
console.log(aa) // 报错 ReferenceError , 表示当一个不存在(或尚未初始化)的变量被引用时发生的错误
let aa = 6
1.3.2 暂时性死区
在代码块内,使用let命令声明变量之前,该变量都是不可用的。
var aa = 123
if(true) {
aa = 666 // 会报错 ReferenceError
let aa
}
// 上述代码中,存在全局变量 aa , 但是在块级作用域内 let 又声明了一个局部变量 aa,导致 后者绑定了这个块级作用域,所以在当前作用域内,aa属于未声明就使用,会报错 ReferenceError
1.3.3 不允许重复声明
let不允许在相同作用域下,重复声明同一个变量。
function Fun() {
let aa = 6
let aa = 66 // 会报错
}
function Fun(age) {
let age = 6 // 会报错
}
function Fun(age) {
{
let age = 6 // 不报错
}
}
2. 变量的解构赋值
2.1 语法
2.1.1 数组的解构赋值
以前,为变量赋值,只能直接指定值。
let a = 1
let b = 2
ES6允许写成下面这样:
let [a, b] = [1, 2]
// 上面代码表示:可以从数组中提取值,按照对应位置,对变量赋值。
// 本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋值对应的值。
// 如果等号的右边不是数组,那么将会报错。
2.1.2 对象的解构赋值
// 对象的属性没有次序,变量必须与属性同名,才能取到正确的值。
let { name, age, sex, hobby } = { name: '张三', sex: '女', age: 18 }
name // '张三'
age // 18
sex // '女'
hobby // undefined
2.1.3 字符串的解构赋值
字符串也可以解构赋值,这是因为 字符串被转换成了一个类似数组的对象
const [ a, b, c, d, e ] = 'hello'
a // 'h'
b // 'e'
c // 'l'
d // 'l'
e // 'o'
2.1.4 数字 和 布尔值的解构赋值
虽然,但是 这个东西没有什么学习的必要,就不提了
2.1.5 函数参数的解构赋值
function add([x, y]) {
return x + y
}
add([1, 2]) // 3
2.2 实例
2.2.1 数组
let [a, [[b], c]] = [1, [[2], 3]]
a // 1
b // 2
c // 3
let [ , , c] = [1, 2, 3]
c // 3
let [a, ...c] = [1, 2, 3, 4]
a // 1
c // [2, 3, 4]
let [a, b, ...c] = [1]
a // 1
b // undefined 如果解构不成功,变量的值就为 undefined
c // []
// 不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。
let [a, b] = [1, 2, 3]
a // 1
b // 2
// 解构赋值允许指定默认值
let [a = 1] = []
a // 1
let [a, b = 2] = [1]
a // 1
b // 2
// ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员 === undefined, 默认值才会生效。
let [a = 1] = [undefined]
a // 1
let [a = 1] = [null]
a // null
2.2.2 对象
const { log } = console
log('hello') // hello
如:变量名和属性名不一致,必须写成下面这样
let { name: personName } = { name: '张三', age: 18 }
personName // '张三'
name // 报错 error: name is not defined
上面代码中, name 是匹配的模式,personName 才是变量。
// 嵌套模式
let obj = {
person: [
'Hello',
{
name: '张三',
}
]
}
let { person, p: [say, { name }] } = obj
say // 'Hello'
name // '张三'
person // [ 'Hello', { name: '张三' } ]
2.3 拓展
2.3.1 交换变量的值
let x = 1
let y = 2
[x, y] = [y, x]
2.3.2 从函数返回多个值
// 返回一个数组
function example(){
return [1, 2, 3]
}
let [a, b, c] = example()
// 返回一个对象
function example() {
return {
name: '张三',
age: 18
}
}
let { name, age } = example()
2.3.3 函数参数的定义
解构赋值可以很方便的将一组参数与变量名对应起来。
// 参数是一组有次序的值
function Fun([x, y, z]) { ... }
Fun([1, 2, 3])
// 参数是一组无次序的值
function Fun({x, y, z}) { ... }
Fun({ z: 3, x: 1, y: 2 })
2.3.4 输入模块的指定方法
const { SourceMapConsumer, SpurceNode } = require("source-map")
3. 模板字符串
const age = 18
// 原
const person = '张三' + age + '岁'
// 新
const person = `张三${age}岁`
4. 数值相关
4.1 数值分隔符
较长的数值允许使用下划线(_)作为分隔符,增加数值的可读性。
let num = 1_000_000_000
这个数值分隔符没有指定间隔的位数
123_00 === 12_300 // true
12345_00 === 1_234_500 // true
注意点:
- 不能放在数值的最前面或最后面。
- 不能两个或两个以上的分隔符连在一起。
- 小数点的前后不能有分隔符。
// 以下写法全都会报错
3_.141
3._141
123__456
_123
123_
4.2 一些方法
- Number.isNaN() 检查一个值是否为NaN
Number.isNaN(NaN) // true
Number.isNaN(15) // false
Number.isNaN('15') // false
Number.isNaN(true) // false
Number.isNaN(9/NaN) // true
Number.isNaN('true' / 0) // true
与传统全局方法isNaN()的区别:
isNaN()会先调用Number()将非数值的值转为数值,再进行判断,而Number.isNaN()只对数值有效
- Number.parseInt(), Number.parseFloat()
ES6 将全局方法 parseInt() 和 parseFloat(),移植到 Number 对象上,行为完全保持不变。
// ES5 的写法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45
// ES6 的写法
Number.parseInt('12.34') // 12
Number.parseFloat('123.45#') // 123.45
这样做的目的:逐步减少全局性方法,使得语言逐步模块化。
4.3 Math 对象的扩展
--- Math.trunc()
用于去处一个数的小数部分,返回整数部分。
Math.trunc(4.1) // 4
Math.trunc(4.9) // 4
Math.trunc(-4.1) // 4
Math.trunc(-0.222) // 0
对于非数值,Math.trunc内部使用Number方法将其先转为数值后返回。
Math.trunc('123.456') // 123
Math.trunc(true) // 1
Math.trunc(false) // 0
Math.trunc(null) // 0
对于空值和无法截取整数的值,返回NaN
Math.trunc(NaN) // NaN
5. 函数的扩展
5.1 函数参数的默认值
ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。
function person(name, age = 18) {
console.log(name, age)
}
person('张三') // 张三 18
person('张三', 20) // 张三 20
person('张三', '') // 张三
5.2 rest参数 — 剩余参数
ES6 引入 rest参数(形式为 ...变量名),用于获取函数的多余参数,这样就不需要使用 arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
// 求和函数案例
function add(...values) {
let sum = 0
for(let val of values) {
sum += val
}
return sum
}
add(2, 5, 3) // 10
// 注意点:
--- rest参数 之后不能再有其他参数(即只能是最后一个参数),否则会报错
// 会报错
function Fun(a, ...b, c) {
}
5.3 箭头函数
--- 基本用法
ES6 允许使用箭头( => )定义函数
let Fun = a => a
// 等同于
let Fun = function(a) {
return a
}
let Fun = () => 5
// 等同于
let Fun = function () {
return 5
}
let Sum = (num1, num2) => num1 + num2
// 等同于
let Sum = function(num1, num2) {
return num1 + num2
}
注意点:
--- 箭头函数没有自己的this对象。
箭头函数你内部的this就是定义时上层作用域中的this。
--- 不可以当做构造函数,也就是说,不可以对箭头函数使用new命令,否则会报错。
--- 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以使用 rest参数代替。
6.数组的扩展
6.1 扩展运算符 (…)
console.log(...[1, 2, 3]) // 1 2 3
应用:
--- 1.复制数组
ES5
const a1 = [1, 2]
const a2 = a1.concat()
ES6
const a1 = [1, 2]
const a2 = [...a1]
或者
const [...a2] = a1
--- 2.合并数组
const arr1 = ['a', 'b']
const arr2 = ['c']
const arr3 = ['d', 'e']
// ES5 的数组合并
arr1.concat(arr2, arr3) // [ 'a', 'b', 'c', 'd', 'e' ]
// ES6 的数组合并
[...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ]
--- 3.字符串
扩展运算符还可以将字符串转为真正的数组
[...'hello'] // [ 'h', 'e', 'l', 'l', 'o' ]
6.2 Array.from()
Array.from() 方法用于将两类对象转为真正的数组:类似数组的对象 和 可遍历的对象。
下面是一个类似数组的对象
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
}
// ES5 的写法
let arr1 = [].slice.call(arrayLike) // ['a', 'b', 'c']
// ES6 的写法
let arr2 = Array.from(arrayLike) // ['a', 'b', 'c']
6.3 Array.of()
Array.of() 方法用于将一组值,转换为数组。
Array.of(3, 11, 8) // [3, 11, 8]
Array.of(3) // [3]
Array.of(3).length // 1
6.4 实例方法:find() findIndex() findLast() findLastIndex()
--- 1. find()
用于找出第一个符合条件的数组成员。
返回值:符合条件 -> 第一个符合条件的成员; 不符合 -> undefined。
[1, 4, -5, 10].find((x, i, arr) => x < 0) // -5
上面代码中, find() 方法的回调函数可以接收三个参数,依次为 当前的值、当前的下标、原数组。
--- 2. findIndex()
返回值:符合条件 -> 第一个符合条件的成员的下标; 不符合 -> 返回 -1。
[1, 5, 10, 15].findIndex((x, i, arr) => x > 9) // 2
--- 3.findLast() 和 findLastIndex()
从数组的最后一个成员开始,依次向前检查,其他与上面两个方法保持不变。
6.5 实例方法:includes()
该方法的第一个参数标识要搜索的值,第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为 -4, 但数组长度为3),则会重置为从0开始。
[1, 2, 3].includes(3, 3) // false
[1, 2, 3].includes(3, -1) // true
6.6 实例方法:flat() flatMap()
--- 1.flat()
[1, 2, [3, 4]].flat() // [1, 2, 3, 4]
默认只会拉平一层。
可以将falt() 方法的参数写成一个整数,表示想要拉平的层数。
[1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5]
当然,如果我们不知道有多少层嵌套,都想转成一维数组,可以用 Infinity 关键字作为参数。
[1, [2, [3]]].flat(Infinity) // [1, 2, 3]
--- 2.flatMap()
flatMap()方法会对原数组的每个成员执行一个函数(类似于map()方法),然后对返回值组成的数组执行 flat()方法。
该方法返回一个新数组,不改变原数组。
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]
6.7 实例方法:at()
接收一个整数作为参数,返回对应位置的成员,支持 负索引。
const arr = [5, 12, 8, 130, 44]
arr.at(2) // 8
arr.at(-2) // 130
// 如果参数位置超过了数组范围,返回 undefined
arr.at(-100) // undefined
arr.at(100) // undefined
7. 对象的扩展
7.1 属性的简洁表示法
ES6允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。
const name = '张三'
const person = {name}
person // {name: '张三'}
// 等同于
const person = {name: '张三'}
7.2 Object.assign()
用于对象的合并,将源对象的所有可枚举属性,复制到目标对象。
第一个参数为目标对象,后面的参数都为源对象。
注意:如果目标对象与源对象有同名属性,则后面的属性会覆盖前面的属性。
例:
const obj1 = { a: 1, b: 1 }
const obj2 = { b: 2, c: 2 }
const obj3 = { c: 3 }
Object.assign(obj1, obj2, obj3)
obj1 // { a: 1, b: 2, c: 3 }
8. 运算符的扩展
8.1 指数运算符(**)
2 ** 2 // 4 2*2
2 ** 3 // 8 2*2*2
特点:右结合,即多个指数运算符连用时,是从最右边开始计算的。
例:
2 ** 3 ** 2 // 512 相当于 2 ** (3 ** 2)
指数运算符可以与等号结合,形成一个新的赋值运算符(**=)
例:
let a = 1.5
a **= 2 // 等同于 a = a*a
let b = 4
b **= 3 // 等同于 b = b *b * b
8.2 链判断运算符 可选链(?.)
let person = {
name: '张三'
}
person?.age // undefined 不加? 就会报错
8.3 Null 判断运算符(??)
只有运算符左侧的值为 null 或 undefined 时,才会返回右侧的值。
let person = {
name: '张三'
}
let personAge = person?.age ?? 0
8.4 逻辑赋值运算符
// 或赋值运算符
x ||= y 等同于 x || (x = y)
// 与赋值运算符
x &&= y 等同于 x && (x = y)
// Null 赋值运算符
x ??= y 等同于 x ?? (x = y)
这三个运算符 相当于先进行逻辑运算,然后根据运算结果,再根据情况进行赋值运算。
let person = {
name: '张三'
}
// 老写法
person.age = person.age || 18
// 新写法
person.age ||= 18
9. Set 和 Map 数据结构
9.1 Set
ES6提供了一个新的数据结构 Set。类似于数组,但是成员的值都是唯一的,没有重复的值。
Set 本身是一个构造函数,用来生成Set数据结构。
例:
去重
const set = new Set([1, 2, 3, 4, 4])
[...set] // [1, 2, 3, 4]
9.2 Map
类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当做键。是一种更完善的 Hash 结构实现。
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"
10. Promise
11. async/await
12. Class
12.1 Class的基本语法
12.1.1 类的由来
JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);
ES6引入了 Class(类)这个概念,我们可以通过Class关键字,来定义类。
例:上面的传统方法用ES6的Class改写
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
// 上面代码定义了一个“类”,里面有一个 constructor()方法,就是构造方法,而this关键字则代表实例对象。
Point类除了构造方法,还定义了一个 toString()方法。
--- 使用时,也是直接对类使用 new 命令,跟构造函数的用法完全一致。
class Bar {
doStuff() {
console.log('stuff')
}
}
const b = new Bar()
b.doStuff() // 'stuff'
构造函数的 prototype 属性,在ES6的“类”上继续存在。事实上,类的所有方法都定义在类的 prototype属性上面。
12.1.2 constructor()
constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。
一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加。
class Point {
}
// 等同于
class Point {
constructor() {}
}
constructor()方法默认返回实例对象(即this),也可以指定返回另外一个对象。
class Foo {
constructor() {
return Object.create(null);
}
}
new Foo() instanceof Foo
// false
上面代码中,constructor()函数返回一个全新的对象,结果导致实例对象不是Foo类的实例。
12.1.3 类的实例
生成类的实例的写法,使用new命令,如果不加new调用class,将会报错。
12.1.x 类的详细信息参考
12.2 Class的继承
13. Module
export
import
咱们现在写的 Vue代码都是采取的模块化处理。
END
参考链接:
ECMAScript 6 入门(阮一峰)
ECMAScript 当前的所有提案
EXPLORING ES6
你会用ES6,那倒是用啊!
20分钟上手ES6,不会ES6好意思说自己会JS ?