原文连接: github.com/qiudongwei/…javascript
本文是一篇应用型文章,根据实际的项目场景总结ES6的各类使用姿式。文章不会对ES6语法中的特性或原理作过多的说明,重点从实际的应用场景去理解ES6的新语法、新特性。html
为解决(或规范)JS中块级做用域的问题,ES6新增了let
和const
两种声明变量的方式,与var
的区别在于:java
var
声明的变量,做用域会提高,变量可被重赋值;let
和const
声明的变量存在块级做用域和暂时性死区,但不会变量提高;let
声明的变量可被重赋值,const
不行。平常咱们项目开发中,会在IDE保存文件或提交Git时会借助Eslint
之类的工具对代码规范进行校验,其中一条常见的校验提示是:声明的变量xxx没有被再赋值,建议使用const
。 所以,通常咱们用ES6声明变量时,能够遵循下面的原则:webpack
let
命令;const
命令;var
命令。function submit(){
let loading = true
const postData = this.getParamData()
// ...
loading = false
}
复制代码
解构赋值是从字符串、数组或对象中提取值,对变量进行赋值的过程。能够应用在变量定义、函数参数等场景中。ios
// 解构数组
let [name, age, sex='male'] = ['安歌','18']
// output -> name: '安歌', age: 18, sex='male'
// 对象解构
const ange = {name: '安歌', sex: 'male'}
let {
name,
age=18,
sex
} = ange
// output -> name: '安歌', age: 18, sex: 'male'
// 字符串解构
const hi = 'hello'
let [a, b, c, d, e] = hi
let { length } = hi
// output -> a: 'h', b: 'e' ... length: 5
复制代码
Set
数据结构要求其元素不可重复,这种惟一性特性在项目开发中能带来很大便利;Map
数据结构是一种键值对集合,它对键的数据类型提供了更普遍的支持,同时有一系列极好用的API。git
这里咱们以数据列表为场景:对销售订单进行对帐,要求限制只能对同一产品进行对帐。 github
// 根据Set内元素不重复的特性,判断申请对帐按钮是否可用
function canSubmit() {
const checkedListData = getCheckedList() // 获取选中的数据项列表
const productNos = new Set( // 取出商品ID列表
checkedListData.map(each => each.product_no)
)
return productNos.size === 1
}
复制代码
/* * 使用Map数据结构存储勾选数据列表 * 假设列表数据项 item={ id, order_no, product_no, product_name, isChecked } */
const checkedData = new Map()
// 选中操做
checkedData.set(item.id, item)
// 取消选中
checkedData.delete(item.id)
// 清空
checkedData.clear()
// 取出选中项ID集合
const recordIds = checkedData.keys()
// 若是须要对选中的数据进行二次筛选,能够取出数据集合
const data = checkedData.values()
// 从其余页码(好比第2页)返回到已访问过的页面(好比第一页),通常须要还原用户的选中状态
checkedData.has(item.id) && (item.isChecked = true)
复制代码
ES6的模板字符串容许咱们在字符串中嵌入变量,或者定义一个多行字符串,有更好的可读性。web
const orderNo = 'ON90509000001'
const msg = `订单${orderNo}对帐失败!`
// output -> 订单ON90509000001对帐失败!
复制代码
箭头函数,让你的表达更简洁。它有如下几个特色:canvas
this
,是箭头函数定义时所在上下文的this
,这点不可变。但能够经过bind
、call
、apply
方法改变上下文的this
指向;new
命令;arguments
对象,但可使用rest
参数代替;yield
命令,所以箭头函数不能用做Generator
函数。const obj = {
name: '安歌',
hello: function () {
setTimeout(() => console.log(this.name), 1000)
}
}
obj.hello() // output -> '安歌'
obj.hello.call({name: 'Ange'}) // output -> 'Ange'
复制代码
function add(x, y, fixed=2) {
const result = (+x) + (+y)
return result.toFixed(fixed)
}
add(1,2) // output -> '3.00'
复制代码
function add(...args) {
let sum = 0
for (var val of args) {
sum += val
}
return sum
}
add(2, 5, 3) // 10
复制代码
function submit() {
this.$post('/login').then( ({code, data}) => {} )
}
复制代码
中后台系统中权限管理是必备的功能,从控制页面的访问权限到某个功能的操做权限。这里以权限管理为应用场景,看如何用ES6优雅地进行条件判断。axios
function hasPer(route) {
const { permissions=[] } = userInfo // 获取用户权限列表
const routePer = route.meta.per // 预先为每一个路由配置的访问权限
return permissions.includes(routePer)
}
复制代码
function checkPer(per) {
const { permissions=[] } = userInfo // 获取用户权限列表
return Array.isArray(per) ?
// per是个列表,permissions须要包含per里面要求的全部权限
per.every(each => permissions.includes(each)) :
(
// per是个正则,permissions须要存在某个权限经过per的正则验证
per instanceof RegExp ? permissions.some(each => per.test(each)) :
// per是个字符串,
permissions.includes(per)
)
}
复制代码
假定咱们有一个订单数据列表:
const orderList = [
{oid: 'ON0001', pid: 'PN0001', pname: '蘸酱短袖', clinet: '杨过', total: 100, date: '2019-07-10'},
{oid: 'ON0002', pid: 'PN0002', pname: '椒盐短袖', clinet: '小龙女', total: 122, date: '2019-07-10'},
{oid: 'ON0003', pid: 'PN0001', pname: '蘸酱短袖', clinet: '郭襄', total: 100, date: '2019-07-14'},
{oid: 'ON0004', pid: 'PN0003', pname: '炭炙短袖', clinet: '郭襄', total: 137 , date: '2019-07-18'},
{oid: 'ON0005', pid: 'PN0001', pname: '蘸酱短袖', clinet: '小龙女', total: 100, date: '2019-07-20'}
]
复制代码
// 取出列表中的全部订单号
const oIds = orderList.map(each => each.oid)
复制代码
// 给列表每一个订单加一个isCP属性,默认值为false
orderList.forEach(each => each.isCP = false)
复制代码
若是购买顾客是杨过和小龙女,将其标识为一对CP(换句话说,循环列表过程,遇到郭襄就执行continue
命令):
orderList.forEach(each => ['杨过', '小龙女'].includes(each.clinet) && (each.isCP = true))
复制代码
商家搞优惠活动,按序给每一个订单总价核减5元(核减的5元返还给顾客),直到遇到第一个奇数总价的订单(换句话说,虚幻列表过程,遇到第一个奇数总价就执行break
命令,终止循环)。
orderList.some(each => each.total % 2 ? true : each.total -= 5)
// 能够验证,当循环到{oid: 'ON0004', pid: 'PN0003', pname: '炭炙短袖', clinet: '郭襄', total: 137 , date: '2019-07-18'},这条数据以后,由于结果返回true,循环将结束,即后面的数据不会再被循环。
复制代码
假定场景:给每一个订单的对帐状况加上状态样式(success
、warning
、danger
),有以下数据:
// 状态码说明: 100->对帐成功、101->对帐中、102->审核中、103->对帐失败、104->取消对帐
// 规定有以下样式映射表:
const style2StatusMap = {
success: [100],
warning: [101,102],
danger: [103,104]
}
复制代码
实现功能:将样式映射表转化为状态映射表,形如:
const status2styleMap = {
100: 'success',
101: 'warning',
102: 'warning',
103: 'danger',
104: 'danger'
}
复制代码
使用ES6的entries
和reduce
等API实现:
// 实现toPairs函数取出Map的键值对,函数返回形如[[key1,val1]...]的数组
const toPairs = (obj) => Object.entries(obj)
// 实现head/last函数取出列表头尾元素
const head = list => list[0]
const last = list => list.reverse()[0]
// 将对象转换为数组
const pairs = toPairs(style2StatusMap)
// 再将数组转化为对象
const status2styleMap = pairs.reduce((acc, curr) => {
const style = head(curr)
const status = last(curr)
return status.reduce((accer, each) => (accer[each] = style,accer), acc)
}, {})
// output -> {100: "success", 101: "warning", 102: "warning", 103: "danger", 104: "danger"}
复制代码
有时候咱们须要在一个数据对象中取出部分的数据项,通过必定的组装再传递给某个组件或发送到服务器。
假定场景:对一个对帐单进行修改,有一个form
变量存储表单数据,router
上还带有一些参数queryData
。实现将修改的数据从新组装后发给服务器,但存在部分数据不须要发送。
// 基础数据
let form = { name: '帐单名', no: '对帐单编号', oNo: '订单编号', pNo: '商品编号', pName: '商品名'@, pNum: '商品数量', applicant: 'ange@gmail.com(安歌)', ... }
const query= { id: '对帐单ID'}
复制代码
// 先覆写applicant参数
Object.assign(form, {
applicant: form.applicant.split('(')[0]
// ...其余可能须要覆写或追加的参数
}
复制代码
// 采集出须要的数据项
const exclude = ['pName', 'pPrice']
const formData = Object.keys(form)
.filter(each => !exclude.includes(each))
.reduce((acc, key) => (acc[key] = form[key], acc), {}
复制代码
// 使用扩展运算符组合form和query的参数
const postData = {
...formData,
...query
}
复制代码
ES6提供了Promise
对象和async
函数用于处理异步操做,用于http
请求的axios
库就是基于Promise
实现的。利用Promise
的特性,咱们能够对http
请求的返回内容进行拦截而不让开发者有任何感知。
应用场景:中后台系统的接口通常会有严格的权限要求以及咱们须要对接口异常进行捕获。经过拦截封装,能够在业务层代码拿到数据以前,先作一层验证。
// 根据须要自定义建立Axios对象实例
const axios = new Axios.create()
// 封装post函数
const $post = (url, postData) => {
return axios.post(url, postData) // 在未封装的状况下,咱们通常经过这种方式发起一个post请求
.then(interceptors.bind(this)) // 针对权限验证的拦截
.catch(reject.bind(this)) // 捕获接口异常
}
// 业务层调用
$post('./get_order_list', postData).then(res => {})
复制代码
// 预处理后端错误码
const interceptors = (res) => {
const code = res.data.code
if(code === 100) { // 未登陆
console.log('请先登陆') // or redirect to login_error_url
return Promise.reject('LoginError')
} else if (code === 101) { // 缺失访问/修改/删除权限
console.log('没有访问权限') // or redirect to permission_error_url
return Promise.reject('PermissionError')
} else { // 正常将数据返回
return response.data
}
}
复制代码
// 针对接口异常的捕获
const reject = (error) => {
if(error instanceof Error) {
const errMsg = error.message
let type = 'error'
let message = '服务器出错了',请联系管理员
if(/timeout/.test(errMsg)) { // 服务器请求超时
message = '请求超时...'
type = 'warning'
}
// ...
console.log(message)
}
return Promise.reject(error)
}
复制代码
效果以下:
// 配合await关键字一块儿使用
const submit = async () => {
const data = await $post(url)
// ...
}
// 也能够用在Vue等框架的钩子函数中
async mounted () {
const data = await $post(url)
// ...
}
复制代码
系统中可能存在某种功能(好比打印)须要引入第三方库(html2canvas.js
,print.js
等),但有时候某些第三方库可能体量惊人,而用户访问页面又不必定会触发该功能,这时候就能够考虑动态引入。
function print () {
import('html2canvas.js').then((html2pdf) => {
html2pdf().outputPdf()
})
}
复制代码
import()
返回一个Promise
对象,只在运行时加载指定模块。另一个常见的应用场景是:Vue
的路由懒加载。
const productListModule = () => import(/* webpackChunkName: "product" */ 'view/product/List') // 商品列表
复制代码
假定场景:在一个商品列表页面,存在两个可排序的列:单价(price
)和款式(style
)。若是先点击了按单价排序,再点击按款式排序,要求款式基于单价的排序上再排序。
// 基础数据
const products = [
{ name: "椒盐T恤", price: 3, style: 'Japanese' },
{ name: "蘸酱短袖", price: 5, style: 'Chinese' },
{ name: "碳炙短袖", price: 4, style: 'Chinese' },
{ name: "印花短袖", price: 8, style: 'England' },
{ name: "写意短袖", price: 3, style: 'Chinese' },
{ name: "清蒸T恤", price: 4, style: 'Japanese' },
]
// 定义不一样的排序规则
const byPrice = (a, b) => a.price - b.price
const byStyle = (a, b) => a.style > b.style ? 1 : a.style === b.style ? 0 : -1
// 利用reduce组合排序
const sortByFlattened = fns => (a,b) =>
fns.reduce((acc, fn) => acc || fn(a,b), 0)
// 组合后的排序函数,排序优先级按数组内元素位置编号
const byPriceStyle = sortByFlattened([byPrice,byStyle])
console.log(products.sort(byPriceStyle))
复制代码
排序结果以下:先按价格升序,再按款式升序
结语
以上仅是我的在平常开发中的ES6使用总结,并未包含所有ES6特性,尚有许多特性较少应用,欢迎你们一块儿交流,补充分享大家实际项目中应用ES6的情形。
最后,若是您以为本文对您有所启发,请勿吝啬您的点赞哈哈~