现代 js 使用笔记(es6 之后)

历史包袱

js 是一个独特的语言,因为他大部分情况下运行在浏览器里,你不能确定客户的浏览器是什么版本
而且有些时候,可以确定客户用的就是非常非常老的版本
他的历史包袱太重了:

  • “不好的特性”不能被删掉。删掉之后,老项目就不能在新浏览器里正常运行
  • 新特性难推广。既然客户端不支持,我干嘛要学习呢(“什么?Babel?我不用新特性不照样写项目?)

但新特性还是在不断地推出
es6 是非常重要且具有标志意义的一个“新特性集”
主要是因为它的内容多,真的使 js 改头换面
关于 es6,阮一峰老师写了一本《ES6 入门教程》
说是“入门”,其实非常详细、易懂,强烈推荐

但 es6 毕竟已经发布六年了,这六年里,js 还在不断成长
尽管速度慢了下来,但每一个新特性都令我激动,希望能尽快在项目中使用
尽管事实上,很难在实际项目中使用(客户端不支持)
但 es6 当年也是“广泛不支持”,现在也已经广泛使用

向前看

以下内容,你的客户的浏览器很可能不支持,慎用
最新的 node 和 最新的 chrome 支持程度也不一样
用之前一定要试一下你的环境

Array.prototype.includes()

用来判断一个数组是否包含某元素
以前:

[1,2,3].indexOf(3) > -1

现在(或以后):

[1,2,3].includes(3)
指数运算符

以前:

Math.pow(2, 5) // 2 * 2 * 2 * 2 * 2

现在(或以后):

2 ** 5
async/await

尽管 Promise 看起来解决了“回调地狱”,但写法依然不够好看
以前:

axios(url)
.then(res => {
  doSth(res)
  return axios.get(url2)
})
.then(res => {
  doSth(res)
})

现在(或以后):

async function main(){
  let res = await axios(url)
  doSth(res)
  res = await axios(url2)
  doSth(res)
})
main()

注意!await 关键字只能在 async 函数内使用:
普通函数:

function f1() {}
let f2 = () => {}
let f3 = function() {}
let obj = {
  f4: function(){},
  f5(){}
}

async 函数:

async function f1() {}
let f2 = async () => {}
let f3 = async function() {}
let obj = {
  f4: async function() {},
  async f5() {}
}

了解更多 async/await 的用法,可以看阮一峰老师的这篇

Object.values()
Object.values({
  a: 1,
  b: 2,
  c: {
    name: 'c'
  }
}) // [1, 2, { name: 'c' }]
Object.entrys()

Object.entries

String.prototype.padStart() & String.prototype.padEnd()

填充字符串到指定长度

let a = '1'
a.padStart(4) // '   1'
a // '1'
a.padStart(4, '0') // '0001'
a.padEnd(4) // '1   '
a.padEnd(4, '0') // '1000'
Object.getOwnPropertyDescriptors()

如果你用过 Object.defineProperty(),那对这个函数应该不陌生
如果你没用过,那就先用用Object.defineProperty().
用过你就知道 getOwnPropertyDescriptors有什么用了

SharedArrayBuffer & Atomics

mdn 对此有详细的介绍:

for await

先说它不是什么
假如你有一个循环操作,每次循环都要删除、修改、更新(他们都是异步的)一些东西,你可以:

async function main() {
  for(let item of list) {
    await drop(item.id)
    await update(xxx)
    await insert(xxx)
  }
}

这样的写法没问题,但跟 for await 没任何关系
for await 的作用在于:

let process = [drop(), update(), insert()] // drop, update, insert 都是异步操作,都返回 Promise 对象
for await (let result of process) {
  // ...
}

当然,上面的操作要在一个 async 函数里进行
drop 结束了,会执行第一次循环
第一次循环结束了,且 update 也结束了,会执行第二次
……

Promise.prototype.finally()

以前,你可以对 Promise 对象进行 then(成功)和 catch(失败)操作
现在,可以使用 finally,不论成功还是失败,都会执行

正则表达式之“对捕获组命名”

你可能会写一个这样的正则表达式来检验时间(时:分:秒)格式:

let re = /\d{2}:\d{2}:\d{2}/
re.test('11:27:04') // true

或者,你需要取出目标字符串的时、分、秒:

let re = /(\d{2}):(\d{2}):(\d{2})/ // 给每个“待取出”部分,加括号
let result = re.exec('11:27:04')
result[0] // '11:27:04'
result[1] // '11'
result[2] // '27'
result[3] // '04'

很方便,但这些都是“老功能”,老功能在什么情况下不好使了呢?
假设,你原来写了这么个“校验并取出时分秒”的正则表达式
但第二天,需求变了,要“校验并取出年月日时分秒”
你的新正则:/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/.
原先,你可能在你的代码中,大量使用 result[1]来代表“时”
这一改,就需要把 result[1]一个一个改为 result[3].
哦不好意思,不是 result[3]而是 result[4].
没错,不仅改得麻烦,而且非常容易出错、遗漏
命名捕获组的出现,就是为了解决这个问题:他给每个“待取出”部分,起名字
这样,就不用依赖顺序了:

let re = /(?<hour>\d{2}):(?<minute>\d{2}):(?<second>\d{2})/ // 给每个“待取出”部分,加括号后,在加上 ?<name>
let result = re.exec('11:27:04')
result.groups.hour // '11'
result.groups.minute // '27'
result.groups.second // '04'
Array.prototype.flat() & Array.prototype.flatMap()
let a = [1,2,3,[4,5,[6,7]]]
let b = a.flat() // flat 接收一个参数,默认值为 1
b // [1,2,3,4,5,[6,7]]
let c = a.flat(2)
c // [1,2,3,4,5,6,7]
a // [1,2,3,[4,5,[6,7]]]

另外,a.flatMap(cb)a.map(cb).flat()的效果是一样的

String.prototype.trimStart() 等
'  a'.trimStart() // 'a'
'  a'.trimLeft() // 'a'
'a  '.trimEnd() // 'a'
'a  '.trimRight() // 'a'
Object.fromEntries()

Object.entries的作用是相反的:

let obj = { name: 'js', year: '2021' }
let objEntries = Object.entries(obj) // [['name', 'js'], ['year', '2021']]
let objAgain = Object.fromEntries(objEntries) // { name: 'js', year: '2021' }
obj == objAgain // false
Symbol.prototype.description
let s = Symbol('hello')
s.description // 'hello'
String.prototype.matchAll()

String.prototype.match类似
区别在于返回所有匹配的结果(以 Iterator 的形式)
mdn

try-catch 写法调整
try {
  throw '某种错误'
// } catch(e) { // 以前必须这样写
} catch { // 现在可以这样写
  // ...
}
新的基本数据类型 BigInt

mdn

globalThis

浏览器里有一个 windows 对象
node 里有一个 global 对象
globalThis 把这俩全局变量统一了

私有成员
class A {
  #name = 'js'
  getName() {
    this.#log()
    return this.#name
  }
  #log() {
    console.log('获取 name')
  }
}
let a = new A()
a.name // undefined
a.#name // Uncaught SyntaxError: Private field '#name' must be declared in an enclosing class
a.getName() // 'js'
a.#log() // Uncaught SyntaxError: Private field '#log' must be declared in an enclosing class
(可选链操作符)?.

英文名:Optional chaining operator
个人觉得还不如叫“问号点”
以前:

function f(a) {
  if(a.b && a.b.c && a.b.c.d)
    return a.b.c.d.e
}

现在(或以后):

function f(a) {
  return a?.b?.c?.d?.e
}

mdn

(空值合并运算符)??

英文名:Nullish coalescing operator
就叫“双问号”运算符或“俩问号”吧
以前:

function f(a) {
  return a.num || 2
}

表示:如果 a.num 存在,就返回 a.num,否则返回 2
但是 a.num 为 0 或 false 时,也会返回 2
如果你只想在 a.num 为 null 或 undefined 时返回 2,就可以:

function f(a) {
  return a.num ?? 2
}

mdn

静态方法

以前:

class A() {}
A.go = function() {}
module.exports = A

现在(或以后):

module.exports = class {
  static go() {}
}
逻辑或赋值、逻辑且赋值

以前:

let a = null
function getA() {
  if(a)
    return a
  a = 2
  return a
}
getA() // 2
a // 2

现在(或以后):

let a = null
function getA() {
  return a ||= 2
}
getA() // 2
a // 2

在单例模式中,很方便
类似的,还有 ??=&&=
我想你应该知道怎么用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值