ES6(2015)
let 和 const
块级作用域
声明的对象不在 window/global 下,在 script 下
箭头函数
1.没有 arguments,可以用剩余参数来代替
没有 arguments,可以用剩余参数来代替
const breeze = (...args) => {
console.log(args[1])
}
breeze(1, 2, 3) // 2
2.箭头函数不会改变 this 指向,也就是箭头函数没有自己的 this 指向
普通函数:
如果该函数是一个构造函数,this 指针指向一个新的对象
在严格模式下的函数调用下,this 指向 undefined
如果该函数是一个对象的方法,则它的 this 指针指向这个对象
字符串
模板字符串
支持 for of
includes(): 判断是否存在字符串
startsWith(): 判断字符串开头部分
endsWith(): 判断字符串结束部分
repeat(): 将字符串重复几次
数值
Number.MIN_SAFE_INTEGER: 最小安全数(-2^53)
Number.MAX_SAFE_INTEGER: 最大安全数(2^53)
Number.isSafeInteger(): 是否在安全数内
Number.isNaN()/isNaN(): 是否为 NaN // 区别(isNaN 先调用 Number())
Math.sign(): 数值类型(返回值:正数 1,负数 -1,零 0)
Math.imul(): 两数相乘
对象
for in: 遍历可枚举、可继承属性
Object.keys(): 可枚举属性
Object.is(): 和 "===" 用法基本相同,区别是 +0 不等于 -0,NaN 等于自身
Object.assign(): 合并对象(浅拷贝)
Object.create(): 新建对象,继承原型链,用于实现继承的方法
function Parent () {
this.name = 'breeze'
}
function Child () {
Parent.call(this)
this.address = 'shanghai'
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child // 注意这边的构造函数,如果不加的话还是 Parent
let breeze = new Child();
数组
扩展运算符:
数组合并,
克隆数组,
转换字符串为数组,
转换类数组为数组(arguments),
转换可迭代对象为数组(Set,Map,String)(拥有 Symbol.iterator)
Array.from(): 将类数组、可迭代对象转为数组
Array.of(): 将一组值,转为数组
fill(): 填充数组(返回原数组)
解构赋值
// 对象、数组解构
[x, y] = [y, x]
const [x, y = 2] = [1] // 默认值(目标值为 undefined 的时候生效)
const { a, b: c } = { a: 1, b: 2 } // 重命名
const [a, , c] = [1, 2, 3] // 忽略某些值
const [a, ...b] = [1, 2, 3, 4]; // b: [2, 3, 4]
const [a, b] = [1, 2]
const [a, b = 2] = [1]
const [a, b, c, d, e, f] = "breeze" // 字符串解构
// 数值和布尔值会转成包装对象,可以解构出其中原型对象上的属性方法
const { toString: breeze } = 666 // 数值解构,可重命名
const { toString: breeze } = false // 布尔解构
Symbol
声明方式: Symbol('') // 不需要 new,不属于引用类型
独一无二的值,即使是相同的参数,不可被 for in 和 Object.keys 访问,可以用作私有属性的创建
Symbol.for() // 也可以创建,相同参数创建的值相同
可以用 String 或者 toString 转为字符串类型
Symbol.iterator: 指向默认遍历器的方法,当用到 for of 的时候会去调用指定的默认遍历器
部署 Symbol.iterator 了的有:
1.Array
2.Map
3.Set
4.String
5.arguments 对象
6.Nodelist 对象, 获取的 dom 列表集合
Set
声明: new Set() // 参数为拥有 Iterator 接口的数据结构
去重数组、字符串: new Set([])
方法:
size: 长度
add(): 添加值,返实例
delete(): 删除值,返布尔
has(): 检查值,返布尔
clear(): 清除
keys(): 返回属性值的对象,可用 for of 遍历
values(): 返回属性值的对象,可用 for of 遍历
entries(): 返回 key -> value 结构的对象
forEach(): 遍历
WeakSet
成员只能是对象
没有 Symbol.iterator,所以不可用 for of 和 forEach
成员都是弱引用,可以被垃圾回收机制回收
方法:
add(): 添加值,返实例
delete(): 删除值,返布尔
has(): 判断是否存在,返布尔
Map
声明方式: new Map([[], []]) // Iterator接口,成员都是一个双元素数组的数据结构
任意值做 key,比如对象,可以解决的是同名属性冲突的问题(注:在 Map 和 Set 中 NaN 视为 同一个值)
方法:
size: 长度
get(key): 通过键查找特定的数值并返回
set(key, value): 添加,返实例
delete(key): 删除,返布尔
has(key): 判断是否存在,返布尔
clear(): 清除所有成员
keys(): 返回以 key 为值的对象
values(): 返回以 value 为值得对象
entries(): 返回以 key->value 的对象,和原来结构类似
forEach(): 遍历
Proxy
new Proxy(target, handler); // target 表示需要拦截的对象,handler 也是对象,拦截的行为
let obj = { name: 'breeze' };
let proxy = new Proxy(obj, {
get (obj, key, proxy) {
return obj[key];
},
set (obj, key, value, proxy) {
obj[key] = value;
},
has (obj, key) { // 可以隐藏属性,不被 in 发现
if (key === name) {
return false;
}
return key in obj;
},
ownKeys(obj) { // 可以
return Reflect.ownKeys(obj).filter(
value => value !== 'name'
)
},
})
deleteProperty(item, propKey): 拦截操作 delete proxy[propKey] , 返回一个布尔值
ownKeys(item): 拦截操作,比如 Object.getOwnPropertyNames(proxy), Object.getOwnPropertySymbols(proxy), Object.keys(proxy), for...in, 返回一个数组。
创建新的实例,必须修改实例才会触发 handler 中的行为
十三种拦截操作:
1.get():拦截属性读取
2.set():拦截属性设置,返回布尔
3.has():拦截属性检查,返回布尔
4.deleteProperty():拦截属性删除,返回布尔
5.defineProperty():拦截属性定义 Object.defineProperty()、Object.defineProperties(),返回布尔
6.ownKeys():拦截属性遍历for-in、Object.keys()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols(),返回数组
7.getOwnPropertyDescriptor():拦截属性描述读取 Object.getOwnPropertyDescriptor(),返回对象
8.getPrototypeOf():拦截原型读取instanceof、Object.getPrototypeOf()、Object.prototype.__proto__、Object.prototype.isPrototypeOf()、Reflect.getPrototypeOf(),返回对象
9.setPrototypeOf():拦截原型设置 Object.setPrototypeOf(),返回布尔
10.isExtensible():拦截是否可扩展读取 Object.isExtensible(),返回布尔
11.preventExtensions():拦截不可扩展设置Object.preventExtensions(),返回布尔
12.apply():拦截 Proxy 实例作为函数调用 proxy()、proxy.apply()、proxy.call()
13.construct():拦截 Proxy 实例作为构造函数调用 new proxy()
Object.defineProperty(obj, prop, descriptor)
let obj = {};
let value = 'breeze'
Object.defineProperty(obj, "name", {
value : 'breeze', // 属性的值
writable : true, // 为 true 时才能被赋值运算符改变,默认为 false。
enumerable : true, // 为 true 时候是可枚举的,默认为 false
configurable : true // 为 true 时才能够被改变,也能够被删除,默认 false
get() {
return value; // 获取值的时候执行
},
set(newValue) {
value = newValue; // 设置值的时候执行,newValue 为设置的值
}
});
Reflect
Reflect 是一个内建的对象,用来提供方法去拦截 js 的操作。
Reflect 是不可构造的,不能通过 new 去新建,Reflect 的所有属性和方法都是静态的。
将 Object 属于语言内部的方法放到 Reflect,让其操作变成函数行为
Proxy 与 Reflect相辅相成,方法是一一对应的,Proxy 和 Reflect 联合使用,前者负责拦截赋值操作,后者负责完成赋值操作
方法:
1.get(obj, key):返回对象属性
2.set(obj, key, value):设置对象属性,返回布尔
3.has(obj, key):检查对象属性,返回布尔
4.deleteProperty(obj, key):删除对象属性,返回布尔
5.defineProperty():定义对象属性,返回布尔
6.ownKeys():遍历对象属性,返回数组(Object.getOwnPropertyNames() + Object.getOwnPropertySymbols())
7.getOwnPropertyDescriptor(obj, key):返回对象属性描述,返回对象
8.getPrototypeOf(obj):返回对象原型,返回对象
9.setPrototypeOf(obj, prototype):设置对象原型,返回布尔
10.apply(target, thisArgument, argumentsList):绑定 this 后执行指定函数
target 为目标函数,
thisArgument 为需要指定 this 的对象,
argumentsList 为目标函数调用时传入的实参列表,应该是一个类数组的对象
let obj = {
name: 'breeze',
getName: function () {
console.log(this.name)
}
}
let fun = obj.getName;
Reflect.apply(fun, obj, {})
11.construct(target, args, newTarget):调用构造函数创建实例
target 是构造函数,
args 是调用构造函数传递的参数数组或伪数组,
newTarget 是指向新的构造函数,可以用 instanceof 来判断
function Breeze (name) {
this.name = name;
}
function Wing () {};
Wing.prototype.getName = function () {};
let breeze = new Breeze('breeze');
let breeze = Reflect.construct(Breeze, ['breeze'], Wing);
注意:如果有第三个参数的话,那么实例由两部分组成,实列的属性部分由第一个参数构造函数生成,实列的方法由第三个参数对象生成。
12.isExtensible():返回对象是否可扩展,返回布尔
13.preventExtensions():设置对象不可扩展,返回布尔
const obj = {};
// 判断该对象是否可以扩展,使用 Reflect.isExtensible
Reflect.isExtensible(obj); // true
// 使用 Reflect.preventExtensions 来阻止该对象扩展
Reflect.preventExtensions(obj);
Reflect.isExtensible(obj); // false
目的:简化写法
比如:
Object.prototype.hasOwnProperty.call(obj, 'name') -> Reflect.ownKeys(obj)
将某些 Object 方法报错情况改成返回 false
不同写法:
try {
Object.defineProperty(target, property, attributes);
} catch(e) {
// 失败
}
if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// failure
}
ES7(2016)
includes
指数操作符
2**10; // 1024
ES8(2017)
Object.values()
Object.values({a: 1, b: 2, c: 3}); // [1, 2, 3]
Object.entries()
Object.entries({a: 1, b: 2, c: 3}); // [["a", 1], ["b", 2], ["c", 3]]
字符串填充空格 (String padding)
// 字符串开头加空格'breeze'.padStart(10);// 字符串结尾加空格'breeze'.padEnd(10);
async/await 同步语法糖
Object.getOwnPropertyDescriptors()
// 可以获取对象的自身所有属性的描述符,如果没有自身属性,返回空对象
let obj = {
name: 'breeze',
[Symbol()]: 21,
}
console.log(Object.getOwnPropertyDescriptors(obj)) // { Symbol(): { configurable: true, enumerable: true, value: 21, writable: true } }
SharedArrayBuffer 对象
Atomics 对象
ES9(2018)
异步迭代
await 可以和 for of 循环一起使用,运行异步操作async function breeze(arr) {
for await (let i of arr) {
// do(i);
} }
Promise.finally()
Promise.resolve().then().catch(e => e).finally();
ES10(2019)
Array.flat(deep)
参数决定了可以扁平化到哪一个层级,没有参数的时候默认为 1,返回新的数组,对原数组不产生影响
[1, 2, [[3, 4]]].flat(1); // [1, 2, [3, 4]]
[1, 2, [[3, 4]]].flat(2); // [1, 2, 3, 4]
// 无论嵌套多深,都会变成一维数组
[1, 2, [[3, 4]]].flat(Infinity); // [1, 2, 3, 4]
// 删除数组中的空值
[1, , 2, 3].flat() // [1, 2, 3]
Array.flatMap()
区别:
[1, 2, 3, 4].flatMap(a => [a*2]); // [2, 4, 6, 8]
[1, 2, 3, 4].map(a => [a*2]); // [[2], [4], [6], [8]]
String.trimStart() 和 String.trimEnd()
去除字符串首尾空白字符
Object.fromEntries()
返回一个给定对象自身可枚举属性的键值对数组// 通过 Object.fromEntries, 将 Map 转为 Object:const map = new Map([ ['a', 1], ['b', 2] ]);
console.log(Object.fromEntries(map)); // { a: 1, b: 2 }
ES11(2020)
?? 空值处理
?? 左侧的值必须是 undefined 或者 null 的时候,才会返回返右侧的值,否则返回左侧的值。
let breeze = {
a: 0,
b: false,
c: null,
d: undefined,
e: 'ceshi',
}
let a = breeze.a ?? '1' // 0
let b = breeze.b ?? '2' // false
let c = breeze.c ?? '3' // 3
let d = breeze.d ?? '4' // 4
let e = breeze.e ?? '5' // 'ceshi'
?. 可选链 判断是否存在
?. 判断不确定的中间节点,代替 && 节省代码空间
let breeze = {}
let name = breeze.people.name // TypeError: Cannot read property 'name' of undefined
let color = breeze.people?.color // undefined
基本数据类型 BigInt
BigInt(value)
可以表示任意大的整数
任意精度的整数
ES12(2021)
replaceAll
返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉
const breeze = 'breeze';
breeze.replaceAll('e', 'k'); // "brkkzk"
数字分隔符
数字分隔符,可以在数字之间创建可视化分隔符,通过_下划线来分割数字,使数字更具可读性
const money = 1_000_000_000;
//等价于
const money = 1000000000;
1_000_000_000 === 1000000000; // true
ES13(2022)
await
引入了顶层 await,允许关键词 在模块的顶层使用
之前要想使用 await 必须在函数外面使用 async 关键字,否则报错,新的特性支持在顶层写 await 了
// 之前使用
const request = async () => {
await axios.get('');
};
// 现在使用,可以直接在顶层使用
const fun = (timeout) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, timeout);
});
};
await fun(3000);
类 Class
类的私有字段
通过 # 关键字,可以创建私有字段或者方法,不可以在外部被访问到,内部可以访问
class Breeze {
constructor() {
this.age = 18;
}
name = 'zhang';
#girl = 'bao';
#eat() {
console.log('eating');
}
miss() {
console.log(this.#girl);
}
meet() {
this.#eat();
}
}
const boy = new Breeze();
console.log(boy.#girl); // Uncaught SyntaxError: Private field '#girl' must be declared in an enclosing class
console.log(boy.miss()); // bao
console.log(boy.meet()); // eating
如果给 私有属性或者方法加上 static 关键字
加上 static 只能通过类名来调用,不能被实例对象调用(和之前的概念没有差别)
class Breeze {
constructor() {
this.age = 18;
}
static name = 'zhang';
static #girl = 'bao';
static #eat() {
console.log('eating');
console.log(this.#girl); // this 指向构造函数
this.#miss(); // 同样带有 static 才可以访问
}
static #miss() {
console.log(this.#girl);
}
meet() {
this.constructor.#eat(); // 没加 static 关键字,需要加上 constructor,这边的 this 指向实例
}
}
const boy = new Breeze();
console.log(boy.meet());
使用 in 判断是否是对象的私有属性
接着上面的代码
class Breeze {
constructor() {
this.age = 18;
}
name = 'zhang';
#girl = 'bao';
#eat() {
console.log('eating');
}
testEat() {
return #eat in this;
}
}
const boy = new Breeze();
console.log(boy.testEat()) // true
console.log('name' in boy) // true
类内的静态块(static blocks inside classes)
类内的静态块,用于执行每个类的评估初始化
可以在类中包含静态块,可以做初始化处理,比如在外部如何获取到内部的私有字段
可以声明多个静态块
let getPrivateField;
class Breeze {
constructor() {
this.age = 18;
}
name = 'zhang';
#girl = 'bao';
static {
console.log(1);
}
static {
console.log(2);
getPrivateField = (el) => el.#girl; // 内部可以访问到私有属性
}
}
const boy = new Breeze();
console.log(getPrivateField(boy)); // bao
/d 正则
正则表达式通过/d 标志匹配索引,该标志为匹配的子字符串提供开始和结束索引
用人话讲就是提供了一个字段 indices,值为数组,分别标志了匹配到的字符串的开始和结束位置,下面来看一看
const str = 'Hello world!';
//查找"Hello"
const patt = /Hello/;
const res = patt.exec(str);
console.log(res); // ['Hello', index: 0, input: 'Hello world!', groups: undefined]
// 加上 d
const patt = /Hello/d;
const res = patt.exec(str);
console.log(res); // ['Hello', index: 0, input: 'Hello world!', groups: undefined, indices: [[0, 5], groups: undefined]]
console.log(res.indices); // [[0, 5], groups: undefined]
Error 对象的 cause 属性
可用于记录错误的因果链
就是可以传递我们错误信息,易于理解深度嵌套的错误
function capture() {
try {
fun();
} catch (err) {
throw new Error('error message', { cause: err });
}
}
// 调用
try {
capture();
} catch (err) {
console.log(err);
console.log(`Cause by: ${err.cause}`);
}
at
这个方法支持相对索引
一直以来 js 的数组只支持正序索引,要是想拿到 arr 数组最后一个元素只能 arr[arr.length - 1],不方便
const arr = [1, 2, 3, 4, 5];
arr[0]; // 1
arr.at(0); // 1
arr.at(-1); // 5
at 方法不仅支持数组,同样可以在字符串中使用
const str = 'hello';
str.at(0); // h
str.at(-1); // o
Object.hasOwn
一个替代 Object.prototype.hasOwnProperty 的方案
const obj = { name: 'breeze' };
// 原先用法
Object.prototype.hasOwnProperty.call(obj, 'name'); // true
// 现在
Object.hasOwn(obj, 'name');
若有收获,就点个赞吧