title: ES6
categories: Javascript
tag: JS
date: 2021-11-10 15:45:34
ES6
ES6 新特性
- let 和 const,命令
- es6 的模板字符串
- 增强的函数
- 扩展的字符串,对象。数组功能
- 解构赋值
- Symblo
- Map 和 Set
- 迭代器和生成器
- Promise 对象
- Proxy 对象
- async 的用法
- 类 class
- 模块化实现
强大的 babel
- 被称为下一代的 JS 编译器,可以将 es6 的代码转换成 es5 的代码,从而让浏览器获得支持
- 只需要知道前端工具这个 babel 这个工具的作用。
var,let,const
var 会有变量提升
console.log(a) //undefined
var a
let
- let 没有变量提升
- 是一个块级作用域
- 不能重复声明
const
const 与 let 类似,但是 const 声明常量,一旦被声明,无法修改。
const person = {
name: 'andy'
}
person.name = 'alex' //可以
person = {
age: 18
} //不可以
console.log(person)
let 作用
作用 1:
var arr = []
for (var i = 0; i < 10; i++) {
arr[i] = function () {
return i
}
}
console.log(arr[5]()) //10
//相当于以下
var arr = []
var i
for (i = 0; i < 10; i++) {
//当函数调用的时候。i已经为10了
arr[i] = function () {
return i
}
}
console.log(arr[5]()) //10
当用 let 的时候
var arr = []
for (let i = 0; i < 10; i++) {
//当函数调用的时候。i已经为10了
arr[i] = function () {
return i
}
}
console.log(arr[5]()) //5
作用 2:
// 不会污染全局变量
let RegExp = 10
console.log(RegExp)
console.log(window.RegExp)
模板字符串
模板字符串使用 tab 键上面的反引号``,插入变量时使用${变量名}
<div class="box"></div>
<script>
const box = document.querySelector('.box')
const id = 1,
name = '董欢'
box.innerHTML = `<ul>
<li>
<p id=${id}>${name}</p>
</li>
</ul>`
</script>
强大的函数
函数默认参数
// es5写法
function add(a, b) {
a = a || 10
b = b || 20
return a + b
}
console.log(add())
// ES6写法
function add(a = 10, b = 20) {
return a + b
}
console.log(add())
默认参数也可以是表达式
function add(a = 10, b = getValue(5)) {
return a + b
}
function getValue(val) {
return val + 10
}
console.log(add())
…运算符
1. 函数剩余参数,将多个独立的参数合并到一个数组中
// es5写法
// arguments表示实参
function pick(obj) {
let result = Object.create(null)
for (let i = 1; i < arguments.length; i++) {
result[arguments[i]] = obj[arguments[i]]
// console.log(arguments[i])
}
return result
}
let bookData = pick(book, 'title', 'year')
console.log(bookData)
es6 写法
//剩余参数:由三个点...和一个紧跟着的具名参数指定 ...keys
let book = {
title: 'es6教程',
author: '董欢',
year: 2021
}
// ...keys解决了arguments的问题
function pick(obj, ...keys) {
console.log(keys)
let result = {}
for (let i = 0; i < keys.length; i++) {
result[keys[i]] = obj[keys[i]]
}
return result
}
let bookData = pick(book, 'title', 'year')
console.log(bookData)
2. 扩展运算符,将一个数组风格,并将各个项作为分离的参数传给函数
//处理数组中的最大值
// es5
const arr = [10, 20, 30, 40]
const result = Math.max.apply(Math, arr)
console.log(result)
es6 写法
//es6
const arr = [10, 20, 30, 40]
const result = Math.max(...arr)
console.log(result)
箭头函数
// 使用=>来定义 function(){}等价于()=>{}
//es5
let add = function (a, b) {
return a + b
}
console.log(add(10, 20))
es6 的写法
//es6
let add = (a, b) => {
return a + b
}
let add = (a, b) => a + b
console.log(add(10, 20))
箭头函数的 this 指向
//es5中的this指向:取决于调用该函数的上下文对象
let page = {
id: 123,
init: function () {
document.addEventListener(
'click',
function (event) {
console.log(this)
this.doSomeThings(event.type)
}.bind(this)
)
},
doSomeThings: function (type) {
console.log(`事件类型是:${type}`)
}
}
page.init()
在 es6 中
//es6,箭头函数没有this指向,箭头函数的内部this只能通过查找作用域链
let page = {
id: 123,
//这里不可以改为箭头函数,因为如果改为了箭头函数,this就指向了window
//一旦使用箭头函数,当前就不存在作用域,就没有作用域链了
init: function () {
document.addEventListener('click', (event) => {
console.log(this) //page
this.doSomeThings(event.type)
})
},
doSomeThings: function (type) {
console.log(`事件类型是:${type}`)
}
}
page.init()
使用箭头函数注意事项
- 使用箭头函数,函数内部没有
arguments
- 箭头函数不能使用
new
来实例化对象
解构赋值
解构赋值是对赋值运算符的一种扩展。它针对数组和对象进行操作。
优点:代码书写上简洁易读
es5
let node = {
type: 'iden',
name: 'foo'
}
// es5写法
let type = node.type
let name = node.name
es6 写法
//es6写法
let node = {
type: 'iden',
name: 'foo'
}
let { type, name } = node
console.log(type, name)
解构命名
//把a解构出来然后命令为res
let { a: res } = obj
console.log(res)
剩余运算符
// 用剩余运算符 把数组解构出来放在一个对象中,
let { a, ...res } = obj
console.log(res)
对数组解构
let arr = [1, 2, 3]
let [a, b] = arr
console.log(a, b) //1,2
可嵌套
let arr = [1, 2, 3]
// let [a, b] = arr
let [a, b, c] = [1, 2, 3]
console.log(a, b, c) //1,2
扩展的对象功能
1. 对象返回
es5 写法
const name = '董欢',
age = 12
const person = {
name: name,
age: age,
sayName: function () {}
}
es6 写法,当变量和值相等时,可以省略,函数也可以简写
const name = '董欢',
age = 12
const person = {
name,
age,
sayName() {}
}
function fn(x, y) {
return { x, y }
}
2. set 和 get
let cart = {
wheel: 4,
set(val) {
if (val < this.wheel) {
throw new Error('轮子数太少了')
}
this.wheel = val
},
get() {
return this.wheel
}
}
cart.set(3)
console.log(cart.get()) //6
3. 属性和方法可以组合
const obj = {}
obj.isShow = true
const name = 'a'
obj[name + 'bc'] = 123
obj['f' + 'bc'] = function () {
console.log(this)
}
console.log(obj) //{ isShow: true, abc: 123, fbc: [Function] }
4. 对象的方法
is()
和===
比较两个值 是否严格相等。
console.log(NaN === NaN) //false
console.log(Object.is(NaN, NaN)) //true
console.log(Object.is(+0, -0)) //false
Object.assign
//对象的合并 //浅拷贝
// Object.assign(target, obj1, obj2)
const newObject = Object.assign({}, { a: 1 }, { b: 2 })
console.log(newObject) //{ a: 1, b: 2 }
Symbol 一种新的数据类型
ES6 引入了一种新的原始数据类型Symbol
,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined
、null
、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
// 用来定义对象的私有变量
const name = Symbol('name')
const name2 = Symbol('name')
console.log(name === name2) //false
let s1 = Symbol('s1')
console.log(s1) //Symbol(s1)
//取值时用[]
let obj = {}
obj[s1] = '董欢'
console.log(obj) //{ [Symbol(s1)]: '董欢' }
Set 和 Map 数据结构
1. Set 是集合,表示无重复值的有序列表
//添加元素
set.add(2)
set.add('s')
set.add([1, 2, 3])
console.log(set) //Set { 2, 's', [ 1, 2, 3 ] }
//删除元素
set.delete(2) //Set { 's', [ 1, 2, 3 ] }
// 校验是否存在
console.log(set.has('s')) //true
// set的长度
console.log(set.size) //2
1 set 的遍历.由于 set 中键和值是一样的,所以用 forEach 遍历毫无意义。
//set遍历
set.forEach((v, k) => {
console.log(v, k)
})
/**
* 输出:
s s
[ 1, 2, 3 ] [ 1, 2, 3 ]
*/
//会去重
let set = new Set([1, 2, 3, 4, 4, 3])
console.log(set) //Set { 1, 2, 3, 4 }
// set转换为数组
let arr = [...set]
console.log(arr) //[ 1, 2, 3, 4 ]
2 set 中的对象的引用无法被释放
在我们给对象释放的时候,通常是给对象 null。但是在 set 中对象无法释放
如果我们使用 WeakSet()
let set = new WeakSet()
- 不能传入非对象类型的参数
- 不可迭代
- 没有 forEach()
- 没有 size 属性
所以 WeakSet 很少用
2. Map
let map = new Map()
//添加值
map.set('name', '张三')
// 获取值
map.set('age', 20)
console.log(map) //Map { 'name' => '张三', 'age' => 20 }
// 校验是否存在
map.has('name')
// 删除值
map.delete('name')
console.log(map) //Map { 'age' => 20 }
//直接清除
map.clear()
console.log(map) //Map {}
//用set初始化
map.set(['a', [1, 2, 3]], 'hello')
console.log(map) //Map { [ 'a', [ 1, 2, 3 ] ] => 'hello' }
// 直接初始化
let map = new Map([
['a', 2],
['b', 3]
])
console.log(map) //Map { 'a' => 2, 'b' => 3 }
与 Set 一样,也是无法被释放,也有 WeakMap
数组扩展用法
1 from().转换为数组
//数组的方法from()
function add() {
console.log(arguments)
//es5做法
let arr1 = [].slice.call(arguments)
console.log(arr1) //[1,2,3]
//es6
// 通过from()
let arr2 = Array.from(arguments)
console.log(arr2) ////[1,2,3]
// 通过扩展运算符转换为数组
console.log([...arguments]) //[1, 2, 3]
}
add(1, 2, 3)
// from还可以对每个元素进行处理
let liContent = Array.from(lis, (ele) => ele.textContent)
2 of()转换为数组
// of()将任意的数据类型,转换为数组
let res = Array.of(3, 11, 20, '30', {
id: 1
})
console.log(res) // [3, 11, 20, '30', {…}]
3 copyWithin()
//将从索引为3开始后面的数赋值给以0开始的数字
let res = [1, 2, 3, 8, 9, 10].copyWithin(0, 3) //[8, 9, 10, 8, 9, 10]
console.log(res)
4 find()和 findIndex()
//找出第一个符合条件的数组成员
let res = [1, 2, 3, 4, -2, 6, 8].find((n) => {
return n < 0
})
//找出第一个符合条件的数组成员索引
let resIndex = [1, 2, 3, 4, -2, 6, 8].findIndex((n) => {
return n < 0
})
console.log(res, resIndex) //-2 4
5 entries() keys() values()
let resIndex = ['a', 'b'].keys()
let resValue = ['a', 'b'].values()
let resEntries = ['a', 'b'].entries()
console.log(resIndex) //Array Iterator {}
// 对键名的遍历
for (let index of resIndex) {
console.log(index) //0,1
}
// 对值的遍历
for (let value of resValue) {
console.log(value) //a,b
}
// 对键值对的遍历
for (let [index, ele] of resEntries) {
console.log(index, ele) //a,b
/*
0 'a'
1 'b'
*/
}
entries 的另外用法
let letter = ['a', 'b', 'c']
let it = letter.entries()
console.log(it.next().value) //[0, 'a']
console.log(it.next().value) //[1, 'b']
console.log(it.next().value) //[2, 'c']
6 includes()
Includes()返回一个布尔值,表示某个数组是否包含给定的值
//之前我们判断是否存在这个值,用的是indexOf()
let arr = [1, 2, 3]
console.log(arr.indexOf(2))
// 现在可以用includes()
console.log(arr.includes(2)) //true
ES6 的迭代器 Iterator
Iterator
JavaScript 原有的表示“集合”的数据结构,主要是数组(Array
)和对象(Object
),ES6 又添加了Map
和Set
。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map
,Map
的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of
循环,Iterator 接口主要供for...of
消费。
Iterator 的遍历过程是这样的。
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next
方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next
方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next
方法,直到它指向数据结构的结束位置。
每一次调用next
方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value
和done
两个属性的对象。其中,value
属性是当前成员的值,done
属性是一个布尔值,表示遍历是否结束。
//使用迭代
const items = ['one', 'two', 'three']
//创建新的迭代器
const it = items[Symbol.iterator]()
//第一次遍历{value: 'one', done: false}
// false表示还没有遍历完成
console.log(it.next())
ES6 的生成器 Generator
1. 生成器的作用
generator 函数可以通过 yield 关键字,将函数挂起,为了改变执行流提供可能。同时为了做异步编程也提供了方案。
2. 与普通函数的区别
- function 后面,函数名有个
*
- 只能在函数内部使用
yield
,让函数挂起
function* func(a) {
//挂载2
yield 2
}
let res = func()
console.log(res.next()) //{value: 2, done: false}
console.log(res.next()) //{value: undefined, done: true}
3. 总结
generator 函数是分段执行的,yield 语句是暂停执行的,而 next()恢复执行。一个 next 对应一个 yield。
function* add() {
console.log('start')
// 这个x不是yield 2的返回值,他是next()调用,恢复当前yield()执行传入的实参
let x = yield '2'
console.log('one:' + x) //此时是one:undefined
let y = yield '3'
console.log('two' + y) //此时是two:undefined
return x + y //{value: 50, done: true}
}
const fn = add()
console.log(fn.next())
console.log(fn.next(20)) //此时是one:20
console.log(fn.next(30)) //此时two:30
4. 使用场景
为不具备 Interator 接口的对象提供了遍历操作。
function* objectEntries(obj) {
const propKeys = Object.keys(obj)
for (const propKey of propKeys) {
yield [propKey, obj[propKey]]
}
}
const obj = {
name: '董欢',
age: 18
}
obj[Symbol.iterator] = objectEntries
console.log(obj)
for (let [key, value] of objectEntries(obj)) {
console.log(`${key},${value}`)
}
/*
输出:
name,董欢
age,18
*/
5 Generator 的应用
- 部署 ajax,异步代码同步化
解决回调地狱,处理异步操作
function* main() {
//这个res不是request请求的结果
let res = yield request('请求的url')
console.log(res) //这个res就是请求返回的res
//执行后面的操作
console.log('数据请求完成,可以继续操作')
}
const ite = main()
ite.next()
function request(url) {
$.ajax({
url,
method: 'get',
success(res) {}
})
}
加载 loading…页面=>数据加载完成=>loading 关闭
function loadUI() {
console.log('加载loading页面')
}
function showData() {
// 模拟异步操作
setTimeout(() => {
console.log('数据加载完成')
}, 1000)
}
function hideUI() {
console.log('隐藏loading页面')
}
loadUI()
showData()
hideUI()
/*
加载loading页面
生成器.html:97 隐藏loading页面
生成器.html:92 数据加载完成
*/
所以通过生成器
function* load() {
loadUI()
yield showData()
hideUI()
}
let itLoad = load()
itLoad.next()
function loadUI() {
console.log('加载loading页面')
}
function showData() {
// 模拟异步操作
setTimeout(() => {
console.log('数据加载完成')
itLoad.next()
}, 1000)
}
function hideUI() {
console.log('隐藏loading页面')
}
/*
加载loading页面
生成器.html:92 数据加载完成
生成器.html:98 隐藏loading页面
*/
Promise 的基本使用
1 Promise 介绍
相当于一个容器,保存着未来才会结束的事件(异步操作)的一个结果
各种异步操作都可以用同样的方法进行处理 例如 axios。
2 Promise 状态
Promise 对象的状态不受外界影响。处理异步操作有 3 个状态。
Pending(进行)、fulfilled(成功)、Reject(失败)
这三个状态一旦改变之后就不会再改变,任何时候得到这个结果
3 Promise 处理异步
let pro = new Promise(function (resolve, reject) {
//执行异步操作
let res = {
code: 201,
data: {
name: '董欢'
},
error: '失败'
}
setTimeout(() => {
if (res.code === 200) {
resolve(res.data)
} else {
reject(res.error)
}
}, 1000)
})
console.log(pro)
pro
.then((success) => {
console.log(success)
})
.catch((err) => {
console.log(err)
})
Promise 的应用
1 Promise 封装 ajax
then()
方法第一个参数是resolved
回调函数,第二个参数是可选的
const getJSON = function (url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.onreadystatechange = handler
xhr.responseType = 'json'
xhr.setRequestHeader('Accept', 'application/json')
//发送
xhr.send()
function handler() {
// console.log(this.readyState) //2,3,4
if (this.readyState == 4) {
if (tis.status === 200) {
resolve(this.response)
} else {
reject(new Error(this.status))
}
}
}
})
}
getJSON('url')
.then((val) => {
console.log(val)
})
.catch((err) => {
console.log(err)
})
4 Promise 其他方法
1 Promise.resolve()
能将现有的任何对象转换为 promise 对象
let p = Promise.resolve('foo')
p.then((data) => {
console.log(data)
})
let q = new Promise((resolve) => resolve('foo'))
q.then((data) => {
console.log(data)
})
2 Promise.all()
**Promise.all()**
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
let promise1 = new Promise((resolve, reject) => {})
let promise2 = new Promise((resolve, reject) => {})
let promise3 = new Promise((resolve, reject) => {})
let p4 = Promise.all([promise1, promise2, promise3])
p4.then(() => {
// 三个都成功才成功
}).catch((err) => {
// 有一个失败就都失败
})
应用:一些游戏类的素材比较多,等待图片,flash,静态资源都加载完成,然后才进行页面的初始化
3 Promise.race()
Promise.race()
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
给某个异步请求设置超时时间,并且在超时后执行相应的操作。
//请求图片资源
function requestImg(imgSrc) {
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = function () {
resolve(img)
}
img.src = imgSrc
})
}
function timeout() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('图片请求超时')
}, 3000)
})
}
Promise.race([
requestImg(
'https://img0.baidu.com/it/u=2510891666,2454567058&fm=26&fmt=auto',
timeout()
)
])
.then((res) => {
console.log(res)
document.body.appendChild = res
})
.catch((err) => {
console.log(err)
})
async 用法
1 async 介绍
- async:使得异步操作更加方便
- 会返回 Promise 对象 then catch
- async 是 generator 的语法糖
async function f() {
//如果async函数里面有多个await,那么then函数会等待所有的await指令运行完的结果才去执行
let s = await 'hello async'
let data = await s.split('')
return data
}
f()
.then((v) => console.log(v))
.catch((e) => console.log(e))
只要有一个 reject,就不会向下执行
async function f2() {
//只要有一个reject,就不会向下执行
await Promise.reject('出错了')
await Promise.resolve('hello')
}
f2()
.then((v) => console.log(v))
.catch((e) => console.log(e))
为了解决这个问题。我们希望如果出错了,继续往下执行。我们使用try{} catch
async function f2() {
//只要有一个reject,就不会向下执行
// 使用try{}catch,如果出错了在这里处理错误。
try {
await Promise.reject('出错了')
} catch (error) {
console.log(error)
}
return await Promise.resolve('hello')
}
f2()
.then((v) => console.log(v))
.catch((e) => console.log(e))
2 async 使用
async function getNowWeather(url) {
// 发送ajax,获取实况天气
let res = await getJSON(url)
console.log(res)
//获取数据 未来3-7天
let arr = await res.HeWeather6
return arr[0].now
}
getNowWeather('url').then((now) => console.log(now))
3 async 的作用
- 解决回调地狱,使得异步操作更加方便。
Class 类在 JS 高级章节已经介绍过了。
ES6 的模块化
es6 模块功能主要由两个命令构成export
和import
ES6 模块不是对象,而是通过export
命令显式指定输出的代码,再通过import
命令输入。