ECMAScript 6 _ 2

ECMAScript 6

模块化语法

第一种语法

导出 export

语法:export default {xxx}

作用:导出一个任意类型的变量

在一个模块中,export default 只能导出一次;

导入 import

语法:import xxx from '...'

作用:导入另外一个模块 export default 导出的变量;可以自定义变量名接收;

1
2
3
4
5
6
7
8
9
// a.js 导出一个任意类型的变量,只能使用导出一次,不然会报错
export default {}
export default 123
export default 'abc'
export default fn
export default []

// 导入
import variable from './a.js'

第二种语法

导出 export

语法:export const {xxx}

作用:导出一个声明后的任意类型的变量

在一个模块中,export 可以导出多次,也可以用一次性导出多个变量;

导入 import

语法:import {} from '...'

作用:导入另外一个模块 export 导出的变量;一次性接收,但是变量名必须与导出的模块中的变量名相同;

1
2
3
4
5
6
7
8
9
10
// a.js 导出一个或者多个变量
export const num = 666
export const str = 'abc'
export const b = true
export const arr = ['a']
export const obj = {}
export const fn = () => {}

// 通过对应的变量名接收,num22没有导出,undefined
import {num, b, arr, str, obj, fn, num22} from './a.js'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 一次性导出:
const num = 666
const str = 'abc'
const b = true
const arr = ['a']
const obj = {}
const fn = () => {}

export { num, str, b, arr, obj, fn }

/*
一次性接收
* 表示所有内容
as 表示起别名
a 就表示 模块a 中导出的所有内容了
*/
import * as a from './js/b'
// as 语法:来起别名,避免命名冲突
import { num as num1 } from './js/b'
const num = 666

ES6 变量与声明

变量声明的两种方式

const:声明一个常量,不能修改赋值,复杂类型不能修改引用地址,但是可以修改属性;

let:声明一个变量,可以修改值和应用地址;

constlet 特点:

  • 声明的是局部常量/变量;
  • 在同一作用域不能重复声明,作用向下兼容;
  • 不会被预解析,只能先声明后使用;
  • 支持局部作用域(代码块);

ES6块级作用域

每一对{}内都是局部作用域;也叫代码块;

所以可以使用let来完成for循环闭包函数的功能;

1
2
3
for (let i = 0; i < 10; i++){
console.log(i)
}

字符串

字符串模板

对于一个字符串HTML结构来说,用引号包裹后,不支持换行,如果换行则需要拼接;

ES6 新的字符串语法:字符串模板;支持字符串内换行;

反引号(键盘ESC下面的键)包裹

字符串模板可以写JavaScript表达式

1
`字符串${}字符串`

字符串 String

新增字符串方法

startsWith("data");-判断字符串是否以data参数开头,返回布尔类型;

1
"abcdefg".startsWith("abc");//true

endsWith("data");-判断字符串是否以data参数结尾,返回布尔类型;

1
"abcdefg".endsWith("fg");//true

ES6 对象 Object

对象添加属性简写

ES6中对象属性的简化语法,属性名相同的时候可以简写;

1
2
3
this.list.push({ id: id, name: name, content: content })
/* === ↓ === ↓ === ↓ === ↓ === ↓ === ↓ === ↓ === ↓ === */
this.list.push({ id, name, content })

对象简化语法

  • 对象中的属性和方法,都可以使用简化语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 属性的简化语法: */
var foo = 'bar'
var baz = {foo}

// 等同于
var baz = {foo: foo}

/* 方法的简化语法: */
var o = {
method() {
return "Hello!"
}
}
// 等同于
var o = {
method: function() {
return "Hello!"
}
}

对象扩展运算符

  • 注意:该语法不是真正的ES规范,需要使用stage-2解析
1
2
3
var obj = { name: 'jack', age: 19 }
var o = { ...obj, gender: 'male' }
// o => {name: 'jack', age: 19, gender: 'male'}

属性名表达式

  • ES6 允许字面量定义对象时,用表达式作为对象的属性名,即把表达式放在方括号内。
1
2
3
4
5
6
7
8
9
10
11
12
13
var propKey = 'foo'
var methodKey = 'bar'

var obj = {
[propKey]: true,
['a' + 'bc']: 123,
[methodKey]() {
return 'hi'
}
}

console.log(obj[propKey])
console.log(obj.foo)

ES6 数组 Array

ES6 数组新增方法

[].find 遍历数组,参数是一个回调函数,回调函数如果return了一个真值,则会返回第一个满足这个条件的元素;

findIndex(() => {}) 遍历数组,参数是一个回调函数,回调函数如果return了一个真值,则会返回当前这个元素的下标;

数组扩展运算符

  • 扩展运算符(spread)是三个点(…)。作用:将一个数组转为用逗号分隔的参数序列
1
2
3
4
5
var arr = ['a', 'b', 'c']
console.log(...arr)

// 上面这句代码相当于:
console.log(arr[0], arr[1], arr[2])

ES6 函数

方法函数

对象内函数封装的新方法;this指向和原来一样;

1
2
3
4
5
6
7
{
fn: function () {}
}

{
fn () {}
}

箭头函数

  • ES6箭头函数
  • 注意 1:函数体内的this对象,就是定义时所在的对象(一般是外层函数中的this)
  • 注意 2:无法使用arguments,没有arguments对象
  • 注意 3:不能当作构造函数,不能使用new创建对象
  • 注意:不要在Vue的选项属性或回调上使用箭头函数
    • 比如:created: () => console.log(this.a)vm.$watch('a', newValue => this.myMethod())
  • 箭头函数 => goes to
  • 箭头函数自身没有this,会向函数外部寻找this,外部的this指向谁,箭头函数的this就指向谁;

语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function fn() {}
var fn = function () {}
var fn = () => {}

// 无参数
var fn = () => {}
fn()

// 只有一个参数,小括号可以省略
var fn = (parameter) => {}
var fn = parameter => {}
fn(parameter)

// 有一个以上参数,小括号不能省略
var fn = (a, b) => {}
fn(a, b)

// 只有一句返回值,可以简写
var fn = () => {return 22}
var fn = () => 22
fn()

// 如果函数内部有多个语句不能简写
var fn = () {
console.log("abc");
return 22;
}
fn()

rest参数

  • ES6 引入 rest 参数(形式为…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了
  • 说明:rest 参数的类型是:数组
  • 注意:rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function add(...values) {
var sum = 0
values.forEach(function(val) {
sum += val
})
return sum
}

add(2, 5, 3) // 10

// 报错
function f(a, ...b, c) {
// ...
}

解构赋值

  • ES6解构
  • ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 对象解构
var { foo, bar } = { foo: "aaa", bar: "bbb" }
foo // "aaa"
bar // "bbb"

// 数组解构
var [a, b, c] = [1, 2, 3]
a // 1
b // 2
c // 3

// 函数参数的解构赋值
function foo({x, y}) {
console.log(x, y) // 1 2
}

foo({x: 1, y: 2})

Promise 异步编程

  • Promise 是异步编程的一种解决方案,它允许你以一种同步的方式编写异步代码
  • promise:承诺、保证
  • ES6 - Promise

回调地狱

JavaScript 是单线程的,异步操作执行后不会马上的到结果,而是会执行后面的代码,所以需要在异步操作之后得到结果,在对这个结果处理,就需要使用回调函数来处理这些数据,如果异步操作一层一层嵌套,会产生回调函数嵌套回调函数,这就是回调地狱

例如需要按顺序读取文件,由于异步操作不能结果返回的顺序;所以需要嵌套回调函数来保证顺序;

1
2
3
4
5
6
7
8
9
10
11
12
13
// 按序读取文件:a -> b -> c -> d
readFileAsync('a.txt', (err, data) => {
console.log('文件a: ', data)
readFileAsync('b.txt', (err, data) => {
console.log('文件b: ', data)
readFileAsync('c.txt', (err, data) => {
console.log('文件c: ', data)
readFileAsync('d.txt', (err, data) => {
console.log('文件d: ', data)
})
})
})
})

Promise 的用法

Promise 方式:将异步操作以同步操作的方式表达出来,避免了层层嵌套的回调函数

  • Promise 是一个构造函数;
  • 把 Promise 看成是一个容器,内部封装了异步操作;
  • Promise 的参数是一个回调函数;这个回调函数有两个参数,两个回调函数;
    • resolve 表示异步操作成功调用的函数
    • reject 表示异步操作失败调用的函数
    • 也就是说:如果异步操作成功,应该调用 resolve;如果异步操作失败,应该调用 reject
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const p = new Promise(function (resolve, reject) {
// 异步操作
setTimeout(() => {
// 随机生成一个小于 0.5 的随机数
const num = Math.random()
if (num < 0.5) {
resolve(num)
} else {
reject(num)
}
}, 500)
})

// 使用 Promise 创建的对象
p
// then() 方法用来获取异步操作成功的结果
.then(function (res) {
console.log('成功了', res)
})
// catch 方法用来获取异步操作失败的结果
.catch(function (err) {
console.log('失败了', err)
})

Promise 的三种状态

Promise 有三种状态,Promise 内部执行的结果将决定由 pending 变为 fulfilled(已成功) 或者 rejected(已失败) 状态,这个结果是唯一的,并且不会再改变;

  • Promise 对象代表一个异步操作,有三种状态:pending(初始状态/进行中/未决定的)fulfilled(已成功)rejected(已失败)
    • 状态改变1:pending => fulfilled 表示成功的状态,调用 resolve 回调函数;
    • 状态改变2:pending => rejected 表示失败的状态,调用 reject 回调函数;
    • 状态确定之后,就不会再改变;

then 和 catch

实例通过调用这两方法,可以来获得 Promise 执行异步操作得到结果后的回调函数;

  • 说明:获取异步操作的结果
  • then() :用于获取异步操作成功时的结果 -> resolve
  • catch():用于获取异步操作失败时的结果 -> reject
  • 说明:then()方法可以有多个,按照先后顺序执行,通过回调函数返回值传递数据给下一个 then()

可以通过 return Promise 的实例对象来继续调用下一个 then() 来完成异步编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const p = new Promise((resolve, reject) => {
const num = Math.random()
if (num <= 0.5) {
resolve(num)
} else {
reject(num)
}
})

p
.then((res) => {
console.log('表示成功回调函数1', res)
return p
})
.then((res) => {
console.log('表示成功回调函数2', res)
return p
})
.then((res) => {
console.log('表示成功回调函数3', res)
})

Promise 解决回调地狱的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 通过Promise 来实现读文件的操作:
const fs = require('fs')

function readFileAsync (fileName) {
const p = new Promise((resolve, reject) => {
fs.readFile(`./data/${fileName}`, (err, data) => {
// 失败
if (err) {
return reject(err)
}
// 成功
resolve(data.toString())
})
})
return p
}

readFileAsync('a.txt')
.then(data => {
console.log('a:', data)

// 先读完a,再读b
return readFileAsync('b.txt')
})
.then(data => {
console.log('b:', data)

return readFileAsync('c.txt')
})
.then(data => {
console.log('c:', data)

return readFileAsync('d.txt')
})
.then(data => {
console.log('d:', data)
})

all 和 race

Promise 的两个方法,Promise.all()Promise.race()

Promise.all() 用来处理多个异步请求都完成后,在执行下一步操作;promise.all( 参数是一个['promise对象'])每一个数组的属性都是一个promise对象;
Promise.race() 用来处理多个异步请求,只要有一个先完成,就执行下一步操作;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 需求:等到多个请求都完成后,再执行某个操作
// Promise.all 方法的参数是一个数组,数组中的每一项都是一个 Promise 对象
Promise
.all([
axios.get(' http://localhost:3000/brands'),
axios.get(' http://localhost:3000/colors')
])
.then(res => {
// res 是一个数组,表示上面all方法中异步操作的结果,数组中每项的顺序与 all 方法中对应
// 数组参数项顺序相同
console.log('所有请求都完成了:', res)
})

// 需求:两个请求,获取到先完成的那一个
// race 方法的参数:也是数组,数组中的每一项也是一个 Promise 对象
// 作用:竞速,哪个Promise先完成,then中就能够获取到这个先完成的Promise的结果
Promise
.race([
axios.get(' http://localhost:3000/brands'),
axios.get(' http://localhost:3000/colors')
])
.then(res => {
// res 表示:先完成的Promise的结果
console.log(res)
})

async 和 await

  • 异步编程终极方案
  • 注意:await只能在async函数中使用
  • 注意:await后面是一个Promise实例对象
  • 注意:await关键字用来暂停后面的函数,等到获取到结果后,下面的代码才会执行
    • 这样,就可以按照代码书写的顺序来理解代码执行顺序
  • 注意:async函数外的代码不受影响,继续按照同步方式执行
  • 使用ES2017的Async功能
  • async/await替代Promise的6个理由
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 通过Promise 来实现读文件的操作:
const fs = require('fs')

function readFileAsync (fileName) {
const p = new Promise((resolve, reject) => {
fs.readFile(`./data/${fileName}`, (err, data) => {
// 失败
if (err) {
return reject(err)
}
// 成功
resolve(data.toString())
})
})
return p
}
// 终极形态:
async function fn () {
const fileA = await readFileAsync('a.txt')
console.log(fileA)
const fileB = await readFileAsync('b.txt')
console.log(fileB)
const fileC = await readFileAsync('c.txt')
console.log(fileC)
const fileD = await readFileAsync('d.txt')
console.log(fileD)
}

fn()

Promise 执行顺序问题

1
2
3
4
5
6
7
8
9
10
11
let promise = new Promise(function(resolve, reject) {
console.log('1 Promise')
// 异步操作
setTimeout(resolve, 1000, 'done')
})

promise.then(function() {
console.log('3 resolved.')
})

console.log('2 Hi!')

class关键字

  • ES6以前,JS是没有class概念的,而是通过构造函数+原型的方式来实现的
  • 注意:ES6中的class仅仅是一个语法糖,并不是真正的类,与Java等服务端语言中的类是有区别的
  • ES6 - 文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
constructor() {
// 实例属性
this.name = 'jack'
}

// 实例方法
say() {}

// 静态方法
static coding() {}
}
// 静态属性
Person.age = 0

console.log(Person.age)
  • 类继承:
    • 1 如果子类提供了 constructor,那么,必须要调用super()
    • 2 子类添加属性,必须在 super() 调用后面
1
2
3
4
5
6
7
8
9
10
// 类继承:
class Chinese extends Person {
constructor(name, gender, weight) {
super(name, gender)

this.weight = weight
}
}

const ch = new Chinese('小明', '男', 130)

静态属性和实例属性

  • 静态属性:直接通过类名访问
  • 实例属性:通过实例对象访问