前言
本人比较喜欢一些js的新特性,所以平时就喜欢关注一些新的写法,这篇文章也会持续更新,整理一些常用的、好用的api,也欢迎大家在评论区补充。。。。
ES2015
var、let、const
var | let | const | |
---|---|---|---|
是否声明提升 | 是,并使用undefined初始化 | 是,未初始化 | 是,未初始化 |
作用域 | 全局、函数作用域 | 块级作用域 | 块级作用域 |
初始化 | 可以仅声明不初始化 | 可以仅声明不初始化 | 声明时必须初始化 |
是否可以重复定义 | 是 | 否 | 否 |
是否可以多次赋值 | 是 | 是 | 基本数据类型:否引用数据类型:是 |
声明前是否可以访问 | 是,值为undefined | 否 | 否 |
模板字符串(标签函数)
console.log`hello world` // ["hello world", raw: Array(1)]
const name = 'jcode'
const gender = true
function myTagFn (strings, name, gender1) {
// 被插值表达式分割的数组
// ["hello, ", " i am ", "", raw: Array(3)]"jcode" true
// console.log(strings, name, gender1)
// return 123
const sex = gender1 ? 'man' : 'woman'
return strings[0] + name + strings[1] + sex + strings[2]
}
const result = myTagFn`hello, ${name} i am ${gender}.`
console.log(result)
字符串扩展方法
const msg = 'abcd'
console.log(
// msg.startsWith('a')
// msg.endsWith('cd')
msg.includes('c')
)
Object.assign
const source1 = {
a: 1,
b: 2
}
const source2 = {
e: 4,
f: 5
}
const target = {
a: 11,
c: 3
}
const result = Object.assign(target, source1, source2)
console.log(result) // { a: 1, c: 3, b: 2, e: 4, f: 5 }
console.log(result === target) // true
Object.is
console.log(
// 0 == false // true
// 0 === false // false
// +0 === -0 // true
// Nan === Nan // false
// Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
)
Number.isInteger()方法用来判断给定的参数是否为整数。
function fits(x, y) {
if (Number.isInteger(y / x)) {
return 'Fits!';
}
return 'Does NOT fit!';
}
console.log(fits(5, 10));
// expected output: "Fits!"
console.log(fits(5, 11));
// expected output: "Does NOT fit!"
Proxy
const user = {
name: 'ace',
age: 19,
brother: {
name: '路飞',
age: 17
}
}
const proxyUser = new Proxy(user, {
// 获取目标对象的某个属性值
get (target, p, receiver) {
console.log('get方法调用了')
return Reflect.get(target, p)
},
// 修改目标对象的属性值/为目标对象添加新的属性
set (target, p, value, receiver) {
console.log('set方法调用了')
return Reflect.set(target, p, value)
},
// 删除目标对象上的某个属性
deleteProperty (target, p) {
console.log('deleteProperty调用了')
return Reflect.deleteProperty(target, p)
}
})
console.log(user)
// 通过代理对象更新目标对象
proxyUser.name = '萨博'
// 通过代理对象获取目标对象中的某个属性值
console.log(proxyUser.name, proxyUser.brother)
console.log(user)
// 通过代理对象向目标对象中添加一个新的属性
proxyUser.gender = '男'
console.log(user)
// 通过代理对象删除目标对象中的属性
delete proxyUser.age
console.log(user)
// 更新目标对象中的某个属性对象中的属性值(深度监听)
proxyUser.brother.name = '白胡子'
console.log(user)
/**
* 总结:如果操作代理对象,目标对象中的数据也会随之变化,同时如果想要在操作数据的时候,界面也要跟着重新更新渲染,那么也是操作代理对象
* 通过当前的代理对象找到该对象中的某个属性,更改该属性中的某个数组的数据
* */
Reflect
const o = {
name: '路飞',
age: 19
}
console.log('name' in o)
console.log(delete o['age'])
console.log(Object.keys(o))
// 等同于
console.log(Reflect.has(o, 'name'))
console.log(Reflect.deleteProperty(o, 'age'))
console.log(Reflect.ownKeys(o))
静态方法 - MDN链接
Reflect.apply(target, thisArgument, argumentsList)
- 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。
Reflect.construct(target, argumentsList[, newTarget])
- 对构造函数进行 new 操作,相当于执行 new target(...args)。
Reflect.defineProperty(target, propertyKey, attributes)
- 和 Object.defineProperty() 类似。如果设置成功就会返回 true
Reflect.deleteProperty(target, propertyKey)
- 作为函数的delete操作符,相当于执行 delete target[name]。
Reflect.get(target, propertyKey[, receiver])
- 获取对象身上某个属性的值,类似于 target[name]。
Reflect.getOwnPropertyDescriptor(target, propertyKey)
- 类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符, 否则返回 undefined.
Reflect.getPrototypeOf(target)
- 类似于 Object.getPrototypeOf()。
Reflect.has(target, propertyKey)
- 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。
Reflect.isExtensible(target)
- 类似于 Object.isExtensible().
Reflect.ownKeys(target)
- 返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable影响).
Reflect.preventExtensions(target)
- 类似于 Object.preventExtensions()。返回一个Boolean。
Reflect.set(target, propertyKey, value[, receiver])
- 将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。
Reflect.setPrototypeOf(target, prototype)
- 设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返回true。
类(class)
类的静态方法
- 定义:类(class)通过 static 关键字定义静态方法。不能在类的实例上调用静态方法,而应该通过类本身调用。这些通常是实用程序方法,例如创建或克隆对象的功能。
- 静态方法调用直接在类上进行,不能在类的实例上调用。静态方法通常用于创建实用程序函数。
class Person {
constructor (name) {
this.name = name
}
say () {
console.log(this)
console.log(`hello, ${this.name}`)
}
static bye (name) {
console.log(this, name)
}
}
const p = new Person('ace')
p.say()
Person.bye('static bye')
类的继承
-
- 父类的静态属性和方法也能继承
- super关键字用于访问和调用一个对象的父对象上的函数。
-
- 在构造函数中使用时,super关键字将单独出现,并且必须在使用this关键字之前使用。super关键字也可以用来调用父对象上的函数。
Set
- 定义:Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用
- 值得相等
-
- 因为 Set 中的值总是唯一的,所以需要判断两个值是否相等。在ECMAScript规范的早期版本中,这不是基于和===操作符中使用的算法相同的算法。具体来说,对于 Set s, +0 (+0 严格相等于-0)和-0是不同的值。然而,在 ECMAScript 2015规范中这点已被更改。有关详细信息,请参阅浏览器兼容性 表中的“Key equality for -0 and 0”。
另外,NaN和undefined都可以被存储在Set 中,NaN之间被视为相同的值(NaN被认为是相同
的,尽管 NaN !== NaN)。
const s = new Set()
s.add(1).add(2).add(3).add(2).add('2').add(4)
console.log(s)
s.forEach(item => console.log(item))
console.log('-----------')
for (let val of s) {
console.log(val)
}
console.log('============')
console.log(s.size)
console.log(s.has(100))
console.log(s.delete(3))
const arr = [9, 8, 7, 6]
// const res = Array.from(new Set(arr))
const res = [...new Set(arr)]
console.log(res)
Map
- Map对象
-
- Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。
- 普通对象的键只能是字符串,不管传入什么类型的值,都会被toString()
const obj = {}
obj[123] = '123'
obj[true] = 'true'
obj[{ a: 1 }] = 'a'
console.log(Object.keys(obj)) // [ '123', 'true', '[object Object]' ]
Symbol
- symbol 是一种基本数据类型 (primitive data type)。Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。
- 最主要的作用就是为对象添加独一无二的属性名
const obj = {
[Symbol()]: 'abc',
[Symbol()]: 123
}
console.log(obj) // { [Symbol()]: 'abc', [Symbol()]: 123 }
const name = Symbol()
const o = {
[name]: 'o-name',
name: 'ha'
}
// console.log(name)
// 由于Symbol的唯一性,外部不能访问,这就创建了一个私有变量
console.log(o[name]) // 'o-name'
console.log(o.name) // 'ha'
console.log(
// Symbol() === Symbol() // false
Symbol('foo') === Symbol('foo') // false
)
const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2) // true
console.log(
Symbol.for(true) === Symbol.for('true') // true
)
const ob = {
[Symbol.toStringTag]: 'hhh'
}
console.log(ob.toString()) // [object hhh]
// for in / Object.keys() / JSON.stringify() 都不能获取到Symbol键值
// Object.getOwnPropertySymbols() 这个属性可以获取到, 与Object.keys()类似
console.log(Object.getOwnPropertySymbols(ob)) // [ Symbol(Symbol.toStringTag) ]
for of
- for...of 语句在可迭代对象(包括Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句
iterator
- Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被for...of循环使用。
// 实现迭代器接口
const obj = {
store: ['a', 'b', 'c'],
[Symbol.iterator]: function () {
let index = 0
const self = this
return {
next: function () {
const result = {
value: self.store[index],
done: index >= self.store.length
}
index++
return result
}
}
}
}
for (const value of obj) {
console.log(value)
}
// 迭代器模式
const todos = {
life: ['弛放', '睡觉', '打豆豆'],
learn: ['语文', '数学', '英语'],
work: ['喝茶'],
each: function (callback) {
const all = [].concat(this.life, this.learn, this.work)
for (const item of all) {
callback(item)
}
},
[Symbol.iterator]: function () {
const all = [...this.life, ...this.learn, ...this.work]
let index = 0
return {
next: function () {
return {
value: all[index],
done: index++ >= all.length
}
}
}
}
}
todos.each(item => {
console.log(item)
})
console.log('----------')
for (const value of todos) {
console.log(value)
}
Generator
function * foo () {
console.log('start')
const res = yield 'foo'
console.log(res)
const error = yield 'e'
console.log(error)
}
const generator = foo()
const result = generator.next()
console.log(result) // {value: 'foo', done: false}
const result2 = generator.next('2') // 第5行会打印 2,这里传递的参数会作为yield的返回值
console.log(result2) // {value: undefined, done: false)
const error = generator.next(new Error('这是一个错误'))
console.log(error)
/**
* 调用next()方法会返回 yield 后面的值,next(data)传递的参数,会作为yield的返回值
* */
// 使用 Generator 应用发号器
function * createIdMaker () {
let id = 1
while (true) {
yield id++
}
}
const idMaker = createIdMaker()
console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)
// 使用 Generator 函数实现 iterator 方法
const todos = {
life: ['弛放', '睡觉', '打豆豆'],
learn: ['语文', '数学', '英语'],
work: ['喝茶'],
[Symbol.iterator]: function * () {
const all = [...this.life, ...this.learn, ...this.work]
for (const item of all) {
yield item
}
}
}
for (const value of todos) {
console.log(value)
}
ES 2016
includes 数组的查找
const arr = ['foo', 1, 2, NaN, true]
// includes 数组的查找, 能查找判断NaN
console.log(arr.indexOf('foo')) // 0
console.log(arr.indexOf('bar')) // -1
console.log(arr.indexOf('NaN')) // -1
console.log(arr.includes(true)) // true
console.log(arr.includes(NaN)) // true
指数运算 **
// 指数运算符
console.log(Math.pow(2, 3))
console.log(2 ** 4)
ES 2017
Objece 扩展方法
Object.values() 和 Object.entries()
const obj = {
name: 'jcode',
age: 18,
height: 183
}
console.log(Object.values(obj)) // [ 'jcode', 18, 183 ]
console.log(Object.entries(obj)) // [ [ 'name', 'jcode' ], [ 'age', 18 ], [ 'height', 183 ] ]
// 转换完后用for of 遍历
for (const [key, value] of Object.entries(obj)) {
console.log(key, value) // name jcode \n age 18 \n height 183
}
// 转换完后,符合Map对象格式
console.log(new Map(Object.entries(obj))) // Map(3) { 'name' => 'jcode', 'age' => 18, 'height' => 183 }
Object.getOwnPropertyDescriptors()
const o1 = {
firstName: 'j',
lastName: 'code',
get fullName () {
return this.firstName + ' ' + this.lastName
},
sayHi () {
console.log('haha')
}
}
// 这种方式,只会把fullName定义为普通键值对, sayHi定义为函数
const o2 = Object.assign({}, o1)
o2.firstName = 'jj'
console.log(o2.fullName) // 'j code'
console.log(o2) // { firstName: 'jj', lastName: 'code', fullName: 'j code' }
const descriptors = Object.getOwnPropertyDescriptors(o1)
const o3 = Object.defineProperties({}, descriptors)
o3.firstName = 'jjj'
console.log(o3.fullName)
const obj = {
name: 'jcode',
age: 18,
height: 183
}
console.log(Object.values(obj)) // [ 'jcode', 18, 183 ]
String.prototype.padStart | String.prototype.padEnd
console.log('12'.padStart(5, '0')) // 00012
Async / Await
ES 2020/2021 新特性
- 空值合并运算符
function foo (option) {
// 只有 size = null 或者 undefined
option.size = option.size ?? 100
const mode = option.mode || 'hash'
console.log(option)
}
foo({ size: 0 })
- 可选链运算符
const list = [
{
title: 'foo',
author: {
name: 'zs',
email: 'zs@qq.com'
}
},
{
title: 'bar'
}
]
list.forEach(item => {
console.log(item.author?.name)
})
ES11(2020)
1. Nullish coalescing Operator(空值处理)
// 表达式在 ?? 的左侧 运算符求值为undefined或null,返回其右侧。
let user = {
u1: 0,
u2: false,
u3: null,
u4: undefined
u5: '',
}
let u2 = user.u2 ?? '用户2' // false
let u3 = user.u3 ?? '用户3' // 用户3
let u4 = user.u4 ?? '用户4' // 用户4
let u5 = user.u5 ?? '用户5' // ''
2. Optional chaining(可选链)
- ?. 用户检测不确定的中间节点
let user = {}
let u1 = user.childer.name // TypeError: Cannot read property 'name' of undefined
let u1 = user.childer?.name // undefined
3. Promise.allSettled
- 返回一个在所有给定的promise已被决议或被拒绝后决议的promise,并带有一个对象数组,每个对象表示对应 的promise结果
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => reject('我是失败的Promise_1'));
const promise4 = new Promise((resolve, reject) => reject('我是失败的Promise_2'));
const promiseList = [promise1,promise2,promise3, promise4]
Promise.allSettled(promiseList)
.then(values=>{
console.log(values)
});
4. import('a' + 'xxx.js') - 动态按需导入
5. 新基本数据类型BigInt - 任意精度的整数
6. globalThis
- 浏览器:window
- worker:self
- node:global
ES12(2021)
1. replaceAll
- 返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉
const str = 'hello world';
str.replaceAll('l', ''); // "heo word"
2. Promise.any
- Promise.any() 接收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的
- promise 。如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise
const promise1 = new Promise((resolve, reject) => reject('我是失败的Promise_1'));
const promise2 = new Promise((resolve, reject) => reject('我是失败的Promise_2'));
const promiseList = [promise1, promise2];
Promise.any(promiseList)
.then(values=>{
console.log(values);
})
.catch(e=>{
console.log(e);
});
3. WeakRefs
- 使用WeakRefs的Class类创建对对象的弱引用(对对象的弱引用是指当该对象应该被GC回收时不会阻止GC的回收行为)
4. 逻辑运算符和赋值表达式
- 逻辑运算符和赋值表达式,新特性结合了逻辑运算符(&&,||,??)和赋值表达式而JavaScript已存在的复
// 合赋值运算符有:
a ||= b
//等价于
a = a || (a = b)
a &&= b
//等价于
a = a && (a = b)
a ??= b
//等价于
a = a ?? (a = b)
5. 数字分隔符
- 数字分隔符,可以在数字之间创建可视化分隔符,通过_下划线来分割数字,使数字更具可读性
const money = 1_000_000_000;
//等价于
const money = 1000000000;
1_000_000_000 === 1000000000; // true
结尾
文中如有错误欢迎读者们在评论区中指出,如果有其他好用的新特性,也欢迎在评论区补充,持续更新中。。。