javaScript es6+系列

此文章会持续更新……
在这里插入图片描述

ES6 新增特性 (2015)

Set

类似于数组, 但是成员的值都是唯一的,没有重复的值
Set 本身是一个构造函数, 用来生成Set数据结构

const s = new Set();
[2, 3, 4, 5, 6, 3, 1, 2, 4, 5].forEach(item => s.add(item));
for (let i of s) {
	console.log(i);
}
// add() 方法 向Set结构加入成员, 不会添加重复的值

// Set函数可以接受一个数组 (或者具有 iterable 接口的其他数据结构)作为参数,用来初始化

// 例子一:
const set = new Set([1, 2, 3, 4, 4]);
console.log([...set]);
        
// 例子二:
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
console.log(items.size); // 5
        
// 例子三:
const set3 = new Set(document.querySelectorAll('div'));

// 例子四:
const set4 = new Set();
document.querySelectorAll('div').forEach(v => set4.add(v));
console.log(set4.size);

// 也可以去掉字符串里面的重复字符
console.log([...new Set('abbcca')].join(""));

// Set内部判断两个值是否相同, 使用的算法叫做 Same-value-zero equality, 类似于 ===
// 主要区别: 向 Set 加入值的时候 认为NaN 等于自身, === NaN不等于自身
console.log(NaN === NaN); // false
let Nan = new Set();
let a = NaN;
let b = NaN;
Nan.add(a);
Nan.add(b);
console.log(Nan); // Set(1) {NaN}
// 只加入一个,说明NaN等于自身

// 两个对象是不相等的
let setObj = new Set();
setObj.add({});
console.log(setObj.size); // 1
setObj.add({});
console.log(setObj.size); // 2

Set 实例的属性和方法

属性:

  1. Set.prototype.constructor:构造函数,默认就是Set函数
  2. Set.prototype.size:返回Set实例的成员总数

方法:

  1. Set.prototype.add(value):添加某个值,返回 Set 结构本身。
  2. Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  3. Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员
  4. Set.prototype.clear():清除所有成员,没有返回值

遍历操作:

  1. Set.prototype.keys():返回键名的遍历器
  2. Set.prototype.values():返回键值的遍历器
  3. Set.prototype.entries():返回键值对的遍历器
  4. Set.prototype.forEach():使用回调函数遍历每个成员

Map

js对象(object),本质上是键值对的集合(Hash结构), 传统上只能用字符串作为键, 给它的使用带来了限制
ES6提供了Map数据结构, 类似于对象。也是键值对的集合,但是键的集合 不限于字符串, 各种类型的值都能当作键。
Object: [ 字符串 - 值 ]
Map: [ 值 - 值 ] , 更完善的Hash结构, Map比Object更合适

const m = new Map();
const o = { p: 'hello world' };
m.set(o, 'content');
console.log(m.get(o)); // content

console.log(m.has(o)); // true
console.log(m.delete(o)); // true
console.log(m.has(o)); // false

Map 也可以接受一个数组作为参数

const map = new Map([
	['name', '章三'],
	['title', 'Author']
]);
console.log(map.size); // 2
console.log(map.has('name')); // true
console.log(map.get("name")); // 章三
console.log(map.has("title")); // true
console.log(map.get("title")); // Author

原理:

const items = [
	['name', '章三'],
	['title', 'Author']
];

const maps = new Map();
items.forEach(([key, value]) => map.set(key, value));

注意: 只有对同一个对象的引用, Map结构才将其视为同一个键。

const mapDiff = new Map();
mapDiff.set(['a'], 555);
console.log(mapDiff.get(['a'])) // undefined
// 内存地址不一样, js中无法直接访问内存
// 引用地址不同,也无法访问

实例的属性和操作方法

  1. size 属性: 返回成员总数
  2. Map.prototype.set(key, value)
    set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。
  3. Map.prototype.get(key)
    get方法读取key对应的键值,如果找不到key,返回undefined。
  4. Map.prototype.has(key)
    has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
  5. Map.prototype.delete(key)
    delete方法删除某个键,返回true。如果删除失败,返回false。
  6. Map.prototype.clear()
    clear方法清除所有成员,没有返回值。

遍历方法:
Map.prototype.keys():返回键名的遍历器。
Map.prototype.values():返回键值的遍历器。
Map.prototype.entries():返回所有成员的遍历器。
Map.prototype.forEach():遍历 Map 的所有成员

与其他数据结构相互转换

// (1) Map转为数组
const myMap = new Map();
myMap.set(true, 7);
myMap.set({ foo: 3 }, ['abc']);
console.log([...myMap]); // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]

// (2) 数组转为Map
console.log(new Map([
	[true, 7],
	[{ foo: 3 }, ['abc']]
]));

// (3) Map转为对象
// 如果所有 Map 的键都是字符串,它可以无损地转为对象。
function strMapToObj(strMap) {
let obj = Object.create(null);
	for (let [k, v] of strMap) {
		obj[k] = v;
	}
	return obj;
}
const myMapA = new Map()
	.set('yes', true)
	.set('no', false);
console.log(strMapToObj(myMapA))

// (4) 对象转为Map
// 对象转为 Map 可以通过Object.entries()。
let obj = { "a": 1, "b": 2 };
let mapsss = new Map(Object.entries(obj));
console.log(mapsss);

// Map转为JSON, JSON转为Map 参考 1,2,3,4

Promise

(1) 对象的状态不受外界的影响。Promise对象代表一个异步操作,三种状态:pending(进行中)、fulfilled(已成功) 和 rejected(已失败)
(2) 状态发生改变,就不会再变。任何时候都可以得到这个结果
Promised的存在,主要是将异步操作以同步操作的流程表达出来。避免了层层嵌套的回调函数(回调地狱)

Promise.resolve

将现有对象转换为Promise对象

const jsPromise = Promise.resolve($.ajax('/whatever.json'));

Promise.resolve方法的参数:

  1. 如果参数是Promise实例, 那么Promise.resolve不做任何修改,原封不动返回这个实例
let p1 = Promise.resolve("res");
let p2 = Promise.resolve(p1).then(res => console.log(res));
console.log(p2);
  1. 参数是一个thenable对象
    thenable对象: 具有then方法的对象
const starMapToObject = (strMap) => {
	let obj = Object.create(null);
	for (const [key, value] of strMap) {
		obj[key] = value;
	}
	return obj;
}

let thenable = new Map();
const thenF = (resolve, reject) => {
	resolve(42);
}
thenable.set('then', thenF);
thenable = starMapToObject(thenable)
let thenablePromise = Promise.resolve(thenable);
thenablePromise.then(res => {
	console.log(res);
});
  1. 参数不具有then方法的对象, 或根本不是对象
    如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。
const p = Promise.resolve("Hello");
p.then(res => {
	console.log(res); // Hello
});
  1. 不带有任何参数
let nullPromise = Promise.resolve();
nullPromise.then(res => {
	console.log(res);
})
// Pormise执行时间
// 立即resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时
setTimeout(function () { 
	console.log('three'); // 3
}, 0);

Promise.resolve().then(function () {
	console.log('two'); // 2
});

console.log('one'); // 1

Promise.reject

注意,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。

const thenable = {
  then(resolve, reject) {
    reject('出错了');
  }
};
Promise.reject(thenable)
.catch(e => {
  console.log(e === thenable)
})
// true
//上面代码中,Promise.reject方法的参数是一个thenable对象,执行以后,后面catch方法的参数不是reject抛出的“出错了”这个字符串,而是thenable对象。

应用

  • 加载图片
    我们可以将图片的加载写成一个Promise,一旦加载完成,Promise的状态就发生变化。
const preloadImage = function (path) {
  return new Promise(function (resolve, reject) {
    const image = new Image();
    image.onload  = resolve;
    image.onerror = reject;
    image.src = path;
  });
};
  • Generator 函数与 Promise 的结合
    使用 Generator 函数管理流程,遇到异步操作的时候,通常返回一个Promise对象。
function getFoo () {
  return new Promise(function (resolve, reject){
    resolve('foo');
  });
}
const g = function* () {
  try {
    const foo = yield getFoo();
    console.log(foo);
  } catch (e) {
    console.log(e);
  }
};
function run (generator) {
  const it = generator();
  function go(result) {
    if (result.done) return result.value;
    return result.value.then(function (value) {
      return go(it.next(value));
    }, function (error) {
      return go(it.throw(error));
    });
  }
  go(it.next());
}
run(g);

//上面代码的 Generator 函数g之中,有一个异步操作getFoo,它返回的就是一个Promise对象。函数run用来处理这个Promise对象,并调用下一个next方法。

Promise.prototype.finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
ES2018引入标准的
finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。

let p = new Promise((resolve, reject) => {
	resolve("res");
	// reject("111")
});

p.then(res => {
	console.log(res);
}).catch(() => {
	throw new Error("error")
}).finally(() => {
	console.log('finally');
})

Promise.all()

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
等所有的promise状态结束之后,才会调用Promise.all()方法后面的回调函数
其中一个Promise实例返回rejected的话,就会报错

// 生成一个Promise对象的数组
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
	return getJSON('/post/' + id + ".json");
});
Promise.all(promises).then(function (posts) {
	// ...
}).catch(function (reason) {
	// ...
});

Promise.race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.race([
  fetch('/resource-that-may-take-a-while'),
  new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('request timeout')), 5000)
  })
]);
p
.then(console.log)
.catch(console.error);

// 上面代码中,如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数。

Promise.allSettled()

Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束
ES2020引入标准

const promises = [
  fetch('/api-1'),
  fetch('/api-2'),
  fetch('/api-3'),
];
await Promise.allSettled(promises);
removeLoadingIndicator();

// 上面代码对服务器发出三个请求,等到三个请求都结束,不管请求成功还是失败,加载的滚动图标就会消失。
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
// 过滤出成功的请求
const successfulPromises = results.filter(p => p.status === 'fulfilled');
// 过滤出失败的请求,并输出原因
const errors = results
  .filter(p => p.status === 'rejected')
  .map(p => p.reason);

有时候,我们不关心异步操作的结果,只关心这些操作有没有结束。这时,Promise.allSettled()方法就很有用。如果没有这个方法,想要确保所有操作都结束,就很麻烦。Promise.all()方法无法做到这一点。

Promise.any()

Promise.any()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态

Promise.any()Promise.race()方法很像,只有一点不同,就是不会因为某个 Promise 变成rejected状态而结束。

Proxy 和 Reflect

ProxyReflect 是 ES6 为了操作对象引入的API
Proxy 可以对目标对象的读取、函数调用等操作进行拦截, 然后进行操作处理。它不直接操作对象, 而是像代理模式。通过对象的代理对对象进行操作, 在进行这些操作的时候,可以添加一些额外的操作
Reflect 可以用于获取目标对象的行为, 它与Object类似, 但是更易读,为操作对象提供了一种更优雅的方式, 他的方法和 Proxy 是对应的

Proxy

Proxy 对象两个部分组成: targethandler。构造函数的生成实例对象时候就需要提供这两个参数, target即目标对象, handler是一个对象, 声明了代理target的指定行为

let target = { // 目标对象 
	name: "Cheng",
	age: 27
}

let handler = { // 代理对象的指定行为
	get(target, key) {
		console.log('getting ' + key);
		return target[key];	
	},
	set(target, key, value) {
		console.log('setting ' + key);
        target[key] = value;
    }
}

let proxy = new Proxy(target, handler);
console.log(proxy.name); // 实际执行 handler.get
// getting name
// Cheng
console.log(proxy.name = "Zi"); // 实际执行 handler.set
// setting name
// Zi
console.log(proxy);
// {  name: "Zi", age: 27 }
console.log(target);
// {  name: "Zi", age: 27 }
// 通过构造函数新建实例时其实是对目标对象进行了浅拷贝,因此目标对象与代理对象会互相影响

Reflect

ES6 中将 Object 内部的一些方法移植到了 Reflect 对象上(当前的方法会同时存在ObjectReflect对象上), 未来的新方法只会在Reflect对象上添加
Reflect 对象对某些方法的返回结果进行了修改, 使其更合理
Reflect 对象使用函数的方式实现了Object的命令式操作

  • Reflect.get(target, name, receiver)
let exam = {
	name: "Cheng",
	age: 24,
	get info() {
		return this.name + this.age;
	}
}
Reflect.get(exam, 'name') // Cheng

// 当target对象上存在 name 属性的 getter 方法, getter 方法的this会被绑定
let receiver = {
	name: "Shuo",
    age: 27,
}
console.log(Reflect.get(exam, 'info', receiver)); // Shuo27
// name属性不存在,会返回 undefined
console.log(Reflect.get(exam, 'birth')); // undefined
// target不是对象,会报错
console.log(Reflect.get(1, 'name')); // Uncaught TypeError: Reflect.get called on non-object
  • Reflect.set(target, name, value, receiver)
let examSet = {
    name: "Cheng",
    age: 24,
    set info(value) {
       	return this.age = value
	}
}
console.log(examSet.age); // 24
console.log(Reflect.set(examSet, 'age', 27));
console.log(examSet.age); // 27
// value 为空的话,属性消除
console.log(Reflect.set(examSet, 'age',));
console.log(examSet.age); // undefined

// 当 target 对象中存在 name 属性 setter 方法时,setter 方法中的 this 会绑定  receiver , 所以修改的实际上是 receiver 的属性
let receiverSet = {
	age: 33,
}
console.log(Reflect.set(examSet, 'age', 44, receiverSet));
console.log(receiverSet.age); // 44
console.log(examSet); // undefined
  • Reflect.has(obj, name)
// 是name in object 的函数化, 用于查找 name 属性在obj对象中是否存在,返回值为boolean 如果obj不是对象则会报错 TypeError
console.log(Reflect.has(exam, 'name')); // true

// Reflect.deleteProperty(obj, property)
// delete obj[property] 的函数化, 用于删除 obj 对象的 property 属性, 返回值为 boolean 如果 obj 不是对象则会报错 TypeError
console.log(Reflect.deleteProperty(examSet, 'name')); // true
console.log(examSet); // {age: undefined}
  • Reflect.deleteProperty(obj, property)
// delete obj[property] 的函数化, 用于删除 obj 对象的 property 属性, 返回值为 boolean 如果 obj 不是对象则会报错 TypeError
console.log(Reflect.deleteProperty(examSet, 'name')); // true
console.log(examSet); // {age: undefined}

组合使用

Reflect 对象的方法与 Proxy 对象的方法是一一对应的, 所以 Proxy 对象的方法可以通过调用 Reflect 对象的方法获取默认行为, 然后进行额外操作

let examMap = {
    name: "xxx",
	age: 40,
}
let proxyMap = new Proxy(examMap, {
	get(target, key) {
		return Reflect.get(target, key);
    },
    set(target, key, value) {
      	Reflect.set(target, key, value)
    }
});
console.log(proxyMap.name); // xxx
console.log(Reflect.get(proxyMap, 'name')); // xxx
proxyMap.name = "Cheng";
console.log(proxyMap.name); // Cheng
Reflect.set(proxyMap, 'name', 'Cheng Zi Shuo'); 
console.log(proxyMap.name); // Cheng Zi Shuo
delete proxyMap.name;
console.log(proxyMap); // age: 40
console.log(Reflect.has(examMap, "name")) // false
Reflect.deleteProperty(examMap, "age") // true
console.log(examMap); // {}

使用场景拓展

// 实现观察者模式

// 定义Set集合
const queuedObservers = new Set();
// 把观察者函数都放入 Set 集合中
const observe = fn => queuedObservers.add(fn);
// observable 返回原始对象的代理,拦截赋值操作
const observable = obj => new Proxy(obj, {set});
function set(target, key, value, receiver) {
	// 获取对象的赋值操作
    const result = Reflect.set(target, key, value, receiver);
    // 执行所有观察者
    observe.forEach(fn => fn());
    // 执行赋值操作
    return result; 
}

ES7 新增特性 (2016)

ES8 新增特性 (2017)

  • 字符串填充(padStart和padEnd)
  • Object.values(), Object.entries(), Object.getOwnPropertyDescriptors()
  • Async Functions(异步函数)
  • 链式调用多个异步函数
  • 共享内存和Atomics

字符串填充(padStart和padEnd)

Object.values(), Object.entries(), Object.getOwnPropertyDescriptors()

Object.values()

Object.entries()

Object.getOwnPropertyDescriptors()

Async Functions(异步函数)

链式调用多个异步函数

共享内存和Atomics

ES9 新增特性 (2018)

  • for await of
  • Promise.finally()
  • Rest/Spread 属性

for await of

for of方法能够遍历具有 Symbol.iterator 接口的同步迭代器数据,不能遍历异步迭代器
新增的 fow await of 可以用来遍历具有 Symbol.asyncIterator 方法的数据结构,也就是异步迭代器, 并且会等待上一个成员的状态改变之后才会遍历下一个成员。

// for of 循环
function Gen(time) {
	return new Promise(function (resolve, reject) {
    	setTimeout(() => {
          resolve(time)
        }, time);
	})
};

async function test () {
	let arr = [Gen(1000), Gen(2000), Gen(3000)];

    for (let item of arr) {
		console.log(Date.now(), item.then(console.log));
	}
}

test();

在这里插入图片描述
使用for await of

async function test1() {
	let arr = [Gen(1000), Gen(2000), Gen(3000)];
	for await (let item of arr) {
		console.log(Date.now(), item);
	}
}

test1();

输出结果:
在这里插入图片描述

Promise.finally()

不管Promise最后的状态是什么,执行完then或者catch指定的回调函数之后,都会执行Promise.finally()方法

let promise = new Promise((resolve, reject) => {
  console.log('promise');
  setTimeout(() => {
    if(false){
      resolve('nice')
    } else {
      debugger
      reject('error')
    }
  }, 1000)
}).then(() => {
  console.log('then');
}).catch(() => {
  console.log('catch');
}).finally(() => {
  console.log('finally');
})

输出结果:
error
catch
finally
如果为true的话,输出结果为:
nice
then
finally

Rest/Spread 属性

ES6引入了Rest参数和扩展运算符,当时三个点 仅用于数组

Rest参数允许我们将一个剩余参数表示为一个数组

function foo(a,b,...rest) {
  console.log(rest);// [3, 4, 5, 6, 7]
}
foo(1,2,3,4,5,6,7);

展开操作符则是将数组转化为可传递给函数的单独参数

const nums = [1, 2, 3, 4, 5];
Math.max(...nums));
// 5

现在对象也可以使用他们了

function foo({a,b,...rest}) {
  console.log(rest); // {c: 3, f: 5, g: 6}
}

function foo({a,b,...rest}) {
  console.log(rest); // {c: 3, d: 4, f: 5, g: 6}
}

foo({
  a: 1,
  b: 2,
  c: 3,
  d: 4,
  f: 5,
  g: 6
});
const object = {
  a: 1,
  b: 2,
  c: 3,
  d: 4,
  f: 5,
  g: 6
};
const {a, ...rest} = object;
console.log(rest); // {b: 2, c: 3, d: 4, f: 5, g: 6}
  • 其余的都是正则表达式,之后补充。。。。。。

ES10 新增特性 (2019)

  • Object.fromEntries() 方法把键值对列表转换为一个对象
  • String.prototype.trimStart()和String.prototype.trimEnd()
  • Array.prototype.flat与flatMap
  • Symbol.prototype.description

Object.fromEntries()

接收参数: Array(二维数组)、Map等其他可迭代的对象
返回一个对应属性和属性值的新对象

// 二维数组
let newObject = Object.fromEntries([
	['name', "cheng"],
	['age', 23]
])

console.log(newObject);
// {name: "cheng", age: 23}

// Map
const m = new Map();
m.set('name', 'ATGUIGU');
const result = Object.fromEntries(m);
console.log(result);
// {name: "ATGUIGU"}

Object.entries是和Object.fromEntries相反的方法

// Object.entries ES8
const arr = Object.entries({
	name: "尚硅谷"
})

console.log(arr);
// ["name", "尚硅谷"]

String.prototype.trimStart()和String.prototype.trimEnd()

trimStart: 删除开头的空格 , trimLeft() 是此方法的别名
trimEnd: 删除结尾的空格, trimRight()是此方法的别名

// trim
let str = '   iloveyou   ';

console.log(str);//     iloveyou   
console.log(str.trimStart()); // iloveyou   
console.log(str.trimEnd());//    iloveyou
// trim() 去除两端空白
console.log(str.trim()) // iloveyou

Array.prototype.flat与flatMap

flat: 将多维数组转换为低维数组
flatMap: 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 连着深度值为1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些

//将多维数组转化为低位数组
const arr = [1,2,3,4,[5,6]];
const arr1 = [1,2,3,4,[5,6,[7,8,9]]];
// 参数为深度 是一个数字
console.log(arr.flat(2));

//flatMap
const arr = [1,2,3,4];
const result = arr.flatMap(item => [item * 10]);
console.log(result);
// [10, 20, 30]

const arr  = [[1],[2],[3]];
const result = arr.flatMap(item => [item * 5]);
console.log(result);
// [5, 10, 15]

Symbol.prototype.description

description 是一个只读属性,它会返回 Symbol 对象的可选描述的字符串

//创建 Symbol
let s = Symbol('尚硅谷');

console.log(s.description);
// 尚硅谷
console.log(s.toString())
// Symbol(尚硅谷)

ES11 新增特性 (2020)

  • 可选链操作符 ( ?. )
  • Promise.allSettled()
  • String.prototype.matchAll()
  • BigInt
  • globalThis
  • 空值合并运算符
  • 动态import加载
  • class的私有属性

可选链操作符 ( ?. )

可选链操作符:允许读取对象链深处的属性的值,不必明确验证每个引用是否有效
引用为null或者undefined的情况下不会报错,该表达式短路返回值是undefined
与函数一起使用时,如果给定的函数不存在,则返回undefined

语法:

obj?.prop
obj?.[expr]
arr?.[index]
func?.(args)

访问对象:

const animal = {
	name:"xxxx",
	cat:{
		name:"xiao hei"
	}
}
// 使用 ?.
const dogName = animal?.dog?.name;
console.log(dogName); // undefined

// 不使用
// 首先得保证 animal.monky 值不是null也不是undefined
const monkyName = animal.monky && animal.monky.name;
console.log(monkyName); // undefined

// 如果直接访问,不对animal.monky校验, 有可能就会抛出错误
const monkyName1 = animal.monky.name;
console.log(monkyName1)// Uncaught TypeError: Cannot read property 'name' of undefined

可选链与函数调用:

let result = {
	// sayName() {
	//		console.log(`my last name is cheng`);
 	// }
	sayName:"1"
}

result?.sayName?.();

Promise.allSettled()

返回一个所有给定的promise都已经成功或者失败后的 promise
接收一个可迭代的对象,例如Array, 每个成员都是promise
多个彼此不依赖的异步任务完成时,想知道每个promise的结果时,通常使用它
Promise.all():适用于彼此相互依赖或者其中一个 rejected时立即结束

//声明三个promise对象
const p1 = new Promise((resolve, reject) => {
	setTimeout(() => {
		resolve("商品数据 - 1");
	}, 1000);
});

const p2 = new Promise((resolve, reject) => {
	setTimeout(() => {
		resolve("商品数据 - 2");
        // reject('出错啦!');
	}, 1000);
});

const p3 = new Promise((resolve, reject) => {
	setTimeout(() => {
		reject("没有请求到数据?");
	}, 1000);
});

//调用 allsettled 方法
const result = Promise.allSettled([p1, p2, p3]).then((result) => {
	console.log(result);
	/**
        0: {status: "fulfilled", value: "商品数据 - 1"}
        1: {status: "fulfilled", value: "商品数据 - 2"}
        2: {status: "rejected", reason: "没有请求到数据?"}
	*/
});

// 调用 all 方法
// 遇到 rejected 会立即结束
const res = Promise.all([p1, p2, p3]).then((result) => {
	console.log(result);
});

String.prototype.matchAll()

返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器
适用于做页面内容提取,方便做爬虫类的项目
接收一个正则表达式,如果不是正则表达式对象,则会隐式的调用new RegExp(obj), 将其转换为一个RegExp
RegExp 必须设置全局模式 g,否则会抛出异常TypeError

// matchAll() 获取正则批量匹配的结果
let str = `
      <ul>
        <li>
          <a>肖生克的救赎</a>
          <p>2020-01-01</p>
        </li>
        <li>
          <a>阿甘正传</a>
          <p>2021-01-01</p>
        </li>
      </ul>
`;
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs;
const result = str.matchAll(reg);
console.log(result); // 返回结果是一个可迭代对象 具有next()

// 1、使用for...of遍历
// for (let item of result) {
//   console.log(item)
// }

// 2、使用扩展运算符展开
const arr = [...result];
console.log(arr);

BigInt

可以表示任意大的整数
可以在整数字面量后面加n表示 BigInt,如10n, 或者调用函数BigInt()

//大整形
let n = 521n;
console.log(n, typeof(n));

//函数
let h = 123;
console.log(BigInt(h));
console.log(BigInt(1.2)); // Uncaught RangeError: The number 1.2 cannot be converted to a BigInt because it is not an integer

//大数值运算
let max = Number.MAX_SAFE_INTEGER;
console.log(max);
console.log(max + 1);
console.log(max + 2); // 失去精度

console.log(BigInt(max))
console.log(BigInt(max) + BigInt(1))
console.log(BigInt(max) + BigInt(2))
console.log(BigInt(max) + 2) // Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions	

globalThis

在以前,从不同的 JavaScript 环境中获取全局对象需要不同的语句。在 Web 中,可以通过 window、self 或者 frames 取到全局对象,但是在 Web Workers 中,只有 self 可以。在 Node.js 中,它们都无法获取,必须使用 global。
globalThis 提供了一个标准的方式来获取不同环境下的全局 this 对象(也就是全局对象自身)

// web中:
globalThis 指向 window
// node中:
globalThis 指向 node全局对象 global

空值合并运算符

?? 表示空值合并运算符, 只有当左侧操作数为undefined或者null的时候返回右侧操作数,否则返回左侧操作数

console.log("" || 'default value');
console.log("" ?? 'default value');

const val = {
	name: {
		letName: 'a'
	}
}

let defaultVal = val?.name?.letName ?? "default value"
console.log("defaultVal", defaultVal); // a

动态import加载

标准用法的import导入的模块是静态的,会使所有被导入的模块,在加载时就被编译(无法做到按需编译,降低首页加载速度)。有些场景中,你可能希望根据条件导入模块或者按需导入模块,这时你可以使用动态导入代替静态导入。下面的是你可能会需要动态导入的场景:

  1. 当静态导入的模块很明显的降低了代码的加载速度且被使用的可能性很低,或者并不需要马上使用它。
  2. 当静态导入的模块很明显的占用了大量系统内存且被使用的可能性很低。
  3. 当被导入的模块,在加载时并不存在,需要异步获取
  4. 当导入模块的说明符,需要动态构建。(静态导入只能使用静态说明符)
  5. 当被导入的模块有副作用(这里说的副作用,可以理解为模块中会直接运行的代码),这些副作用只有在触发了某些条件才被需要时。(原则上来说,模块不能有副作用,但是很多时候,你无法控制你所依赖的模块的内容)

main.js

//获取元素
const btn = document.getElementById('btn');
// 动态导入:返回结果是一个promise,成功的值是导入的模块
btn.onclick = function(){
    import('./hello.js').then(module => {
        console.log(module);
        module.hello();
    });
}


// import函数 可以支持await
btn.onclick = async () => {
	let modules = await import('./hello.js');
	modules.hello();
}

定义hello.js:

export function hello(){
    alert('Hello');
}

类的私有属性

不能在类的实例中访问私有属性,只能在类的内部访问,外部无法访问到

class Person{
	//公有属性
	name;
	//私有属性
	#age;
	#weight;
	//构造方法
	constructor(name, age, weight){
		this.name = name;
		this.#age = age;
		this.#weight = weight;
	}

	intro(){
		console.log(this.name);
		console.log(this.#age);
		console.log(this.#weight);
	}
}

//实例化
const girl = new Person('晓红', 18, '45kg');
        
// console.log(girl.name);
// console.log(girl.#age);
// console.log(girl.#weight);

girl.intro();

ES12 新增特性 (2021)

  • String.prototype.replaceAll
  • Promise.any
  • WeakRefs
  • 逻辑运算符和赋值表达式
  • 数字分隔符号

String.prototype.replaceAll

所有符合匹配规则的字符都会被替换掉,替换规则可以是字符串或者正则表达式

// 不会改变原字符串,
// replaceAll: 所有符合匹配规则的字符都会被替换掉,替换规则可以是字符串或者正则表达式
let str = "My name is cheng zi shuo, cheng is my last name";

// 使用replace
let replaceStr = str.replace("cheng", "xxxx");
// My name is xxxx zi shuo, cheng is my last name

// 使用replace, 正则匹配所有
let replaceStrEXP = str.replace(/cheng/g, "reg");
// My name is reg zi shuo, reg is my last name

let replaceAllStr = str.replaceAll("cheng", "all");
// My name is all zi shuo, all is my last name

// 注意:使用正则的时候, 如果不是全局匹配(/g),就会抛出一个异常错误
let replaceAllStrEXP = str.replaceAll(/cheng/, "allexp"); // TypeError

Promise.any()

Promise.any(): 接收一个可迭代的对象, 比如数组(Array)
即使第一个返回的是失败的,Promise.any()依然使用第一个成功状态的promise来返回
这与首个(无论rejected还是fullfiled)promise来返回的Promise.race()相反

const pErr = new Promise((resolve, reject) => {
	reject("总是失败?")
})

const pSlow = new Promise((resolve, reject) => {
	setTimeout(resolve, 500, "最终完成");
});

const pFast = new Promise((resolve, reject) => {
	setTimeout(resolve, 100, "很快完成");
})

// 使用Promise.any()
Promise.any([pErr, pSlow, pFast]).then(value => {
	console.log(value); // 很快完成
});

// 使用Promise.race()
Promise.race([pErr, pSlow, pFast]).then(value => {
	console.log(value); // 总是失败
});

如果没有成功的promise, Promise.any()返回AggregateError错误

Promise.any([pErr]).then(value => {
	console.log(value);
	// Uncaught (in promise) AggregateError: All promises were rejected
})

数字分隔符号

数字分隔符使我们能够用数字文字中的下划线( _ )分隔数千个字符
有什么用? 它使我们的代码更具信息性和可读性

let num1 = 100_000_000_000; // 100000000000

let num2 = 544_111.654; // 544111.654
        
let num3 = 3.141_592_653_589_7; // 3.1415926535897

let num4 = 0xA0_B0_C0; // 10531008

let num5 = 0o1234_5670; // 2739128

逻辑赋值运算符

逻辑运算符(&& || ??)和赋值操作符( = )的结合。
逻辑赋值运算符有: ( &&= ||= ??= )

a ||= b; 如果a为true, 则返回a, 如果a为false, 则返回b

let a;
let b = '123';

// 之前的写法:
if (!a) a = b;
// 或者
a = a || b;

// 现在的写法
a ||= b;
// 等价于:
a = a || (a = b);

a &&= b; 如果a为true,则返回b。 如果a为false, 返回a。

let a = "";
let b = "123";

// 之前的写法:
if (a) a = b;
// 或者
a = a && b;

// 现在的写法:
a &&= b;
// 等价于:
a = a && (a = b);

a ??= b; 如果a为undefined或null, 则返回b, 否则的话返回a

let a = {};
let b = "123";

// 之前的写法:
a = a ?? b;

// 现在的写法
a ??= b;
// 等价于:
a = a ?? (a = b);

WeakRefs

emmmm。。。。下次补上吧。。。

babel上体验新特性

babel: 传送门

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值