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) //undefinedvar 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 ifor (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 = 10console.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 || 10b = b || 20return 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 写法
//剩余参数:由三个点...和一个紧跟着的具名参数指定 ...keyslet 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. 扩展运算符,将一个数组风格,并将各个项作为分离的参数传给函数
//处理数组中的最大值// es5const arr = [10, 20, 30, 40]const result = Math.max.apply(Math, arr)console.log(result)
es6 写法
//es6const arr = [10, 20, 30, 40]const result = Math.max(...arr)console.log(result)
箭头函数
// 使用=>来定义 function(){}等价于()=>{}//es5let add = function (a, b) {return a + b}console.log(add(10, 20))
es6 的写法
//es6let add = (a, b) => {return a + b}let add = (a, b) => a + bconsole.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) //pagethis.doSomeThings(event.type)})},doSomeThings: function (type) {console.log(`事件类型是:${type}`)}}page.init()
使用箭头函数注意事项
- 使用箭头函数,函数内部没有
arguments - 箭头函数不能使用
new来实例化对象
解构赋值
解构赋值是对赋值运算符的一种扩展。它针对数组和对象进行操作。
优点:代码书写上简洁易读
es5
let node = {type: 'iden',name: 'foo'}// es5写法let type = node.typelet name = node.name
es6 写法
//es6写法let node = {type: 'iden',name: 'foo'}let { type, name } = nodeconsole.log(type, name)
解构命名
//把a解构出来然后命令为reslet { a: res } = objconsole.log(res)
剩余运算符
// 用剩余运算符 把数组解构出来放在一个对象中,let { a, ...res } = objconsole.log(res)
对数组解构
let arr = [1, 2, 3]let [a, b] = arrconsole.log(a, b) //1,2
可嵌套
let arr = [1, 2, 3]// let [a, b] = arrlet [a, b, c] = [1, 2, 3]console.log(a, b, c) //1,2
扩展的对象功能
1. 对象返回
es5 写法
const name = '董欢',age = 12const person = {name: name,age: age,sayName: function () {}}
es6 写法,当变量和值相等时,可以省略,函数也可以简写
const name = '董欢',age = 12const 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 = trueconst name = 'a'obj[name + 'bc'] = 123obj['f' + 'bc'] = function () {console.log(this)}console.log(obj) //{ isShow: true, abc: 123, fbc: [Function] }
4. 对象的方法
is()和===
比较两个值 是否严格相等。
console.log(NaN === NaN) //falseconsole.log(Object.is(NaN, NaN)) //trueconsole.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) //falselet 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) {//挂载2yield 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:undefinedlet y = yield '3'console.log('two' + y) //此时是two:undefinedreturn x + y //{value: 50, done: true}}const fn = add()console.log(fn.next())console.log(fn.next(20)) //此时是one:20console.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] = objectEntriesconsole.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 = handlerxhr.responseType = 'json'xhr.setRequestHeader('Accept', 'application/json')//发送xhr.send()function handler() {// console.log(this.readyState) //2,3,4if (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.HeWeather6return arr[0].now}getNowWeather('url').then((now) => console.log(now))
3 async 的作用
- 解决回调地狱,使得异步操作更加方便。
Class 类在 JS 高级章节已经介绍过了。
ES6 的模块化
es6 模块功能主要由两个命令构成export和import
ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。
