js语法:es12-es6

#ES2022


ES2022批准于2022-6-22

引用小焱编程: ECMAScript 2022 正式发布 JavaScript新功能了解一下 - 知乎

1、Top-level Await  : 顶层 await

ES2017,引入async 和 await简化Promise,但是await只能在async内部使用,不然报错

顶层 await允许在async外使用await,它允许模块充当大型异步函数,通过顶层 await,这些 ECMAScript 模块可以等待资源加载。

//***********旧的写法**********************************

// a.js
  import fetch  from "node-fetch";
  let users;

  export const fetchUsers = async () => {
    const resp = await fetch('https://jsonplaceholder.typicode.com/users');
    users =  resp.json();
  }
  fetchUsers();

  export { users };

  // usingAwait.js
  import {users} from './a.js';
  console.log('users: ', users);
  console.log('usingAwait module');



//***********新的写法**********************************
// a.js
  const resp = await fetch('https://jsonplaceholder.typicode.com/users');
  const users = resp.json();
  export { users};

  // usingAwait.js
  import {users} from './a.mjs';

  console.log(users);
  console.log('usingAwait module');

使用场景:

  • 动态加载模块:
const strings = await import(`/i18n/${navigator.language}`);
  • 资源初始化:
const connection = await dbConnector();
  • 依赖回退:
let translations;
try {
  translations = await import('https://app.fr.json');
} catch {
  translations = await import('https://fallback.en.json');
}

2. Object.hasOwn()

在ES2022之前,可以使用 Object.prototype.hasOwnProperty() 来检查一个属性是否属于对象。

Object.hasOwn 特性是一种更简洁、更可靠的检查属性是否直接设置在对象上的方法:

const example = {
  property: '123'
};

console.log(Object.prototype.hasOwnProperty.call(example, 'property'));
console.log(Object.hasOwn(example, 'property'));

3. at()

at() 是一个数组方法,用于通过给定索引来获取数组元素。当给定索引为正时,这种新方法与使用括号表示法访问具有相同的行为。当给出负整数索引时,就会从数组的最后一项开始检索:

const array = [0,1,2,3,4,5];

console.log(array[array.length-1]);  // 5
console.log(array.at(-1));  // 5

console.log(array[array.lenght-2]);  // 4
console.log(array.at(-2));  // 4

除了数组,字符串也可以使用at()方法进行索引:

const str = "hello world";

console.log(str[str.length - 1]);  // d
console.log(str.at(-1));  // d

4. error.cause

在 ECMAScript 2022 规范中,new Error() 中可以指定导致它的原因:

function readFiles(filePaths) {
  return filePaths.map(
    (filePath) => {
      try {
        // ···
      } catch (error) {
        throw new Error(
          `While processing ${filePath}`,
          {cause: error}
        );
      }
    });
}

5. 正则表达式匹配索引

该特性允许我们利用 d 字符来表示我们想要匹配字符串的开始和结束索引。以前,只能在字符串匹配操作期间获得一个包含提取的字符串和索引信息的数组。在某些情况下,这是不够的。因此,在这个规范中,如果设置标志 /d,将额外获得一个带有开始和结束索引的数组。

const matchObj = /(a+)(b+)/d.exec('aaaabb');

console.log(matchObj[1]) // 'aaaa'
console.log(matchObj[2]) // 'bb'

由于 /d 标识的存在,matchObj还有一个属性.indices,它用来记录捕获的每个编号组:

console.log(matchObj.indices[1])  // [0, 4]
console.log(matchObj.indices[2])  // [4, 6]

我们还可以使用命名组:

const matchObj = /(?<as>a+)(?<bs>b+)/d.exec('aaaabb');

console.log(matchObj.groups.as);  // 'aaaa'
console.log(matchObj.groups.bs);  // 'bb'

这里给两个字符匹配分别命名为as和bs,然后就可以通过groups来获取到这两个命名分别匹配到的字符串。

它们的索引存储在 matchObj.indices.groups 中:

console.log(matchObj.indices.groups.as);  // [0, 4]
console.log(matchObj.indices.groups.bs);  // [4, 6]

匹配索引的一个重要用途就是指向语法错误所在位置的解析器。下面的代码解决了一个相关问题:它指向引用内容的开始和结束位置。

const reQuoted = /“([^”]+)”/dgu;
function pointToQuotedText(str) {
  const startIndices = new Set();
  const endIndices = new Set();
  for (const match of str.matchAll(reQuoted)) {
    const [start, end] = match.indices[1];
    startIndices.add(start);
    endIndices.add(end);
  }
  let result = '';
  for (let index=0; index < str.length; index++) {
    if (startIndices.has(index)) {
      result += '[';
    } else if (endIndices.has(index+1)) {
      result += ']';
    } else {
      result += ' ';
    }
  }
  return result;
}

console.log(pointToQuotedText('They said “hello” and “goodbye”.'));
// '           [   ]       [     ]  '

6. 类的实例成员

(1)公共实例字段

旧的:在构造函数中定义了实例字段和绑定方法

新的:公共类字段语法允许我们直接将实例属性作为属性添加到类上,而无需使用构造函数方法。这样就简化了类的定义,使代码更加简洁、可读:

import React from "react";

export class Incrementor extends React.Component {
  state = { count: 0 };

  increment = () => this.setState({ count: this.state.count + 1 });

  render = () => (
    <button onClick={this.increment}>Increment: {this.state.count}</button>
  );
}

有些小伙伴可能就疑问了,这个功能很早就可以使用了呀。但是它现在还不是标准的 ECMAScript,默认是不开启的,如果使用 create-react-app 创建 React 项目,那么它默认是启用的,否则我们必须使用正确的babel插件才能正常使用(@babel/preset-env)。

下面来看看关于公共实例字段的注意事项:

  • 公共实例字段存在于每个创建的类实例上。它们要么是在Object.defineProperty()中添加,要么是在基类中的构造时添加(构造函数主体执行之前执行),要么在子类的super()返回之后添加:
class Incrementor {
  count = 0
}

const instance = new Incrementor();
console.log(instance.count); // 0
  • 未初始化的字段会自动设置为 undefined:
class Incrementor {
  count
}

const instance = new Incrementor();
console.assert(instance.hasOwnProperty('count'));
console.log(instance.count);  // undefined
  • 可以进行字段的计算:
const PREFIX = 'main';

class Incrementor {
  [`${PREFIX}Count`] = 0
}

const instance = new Incrementor();
console.log(instance.mainCount);   // 0

(2)私有实例字段、方法和访问器-在前面添加一个#

默认情况下,ES6 中所有属性都是公共的,可以在类外检查或修改。

而私有类字段将使用哈希#前缀定义,从上面的示例中,可以修改它以包含私有类字段,以防止在类方法之外更改属性:

class TimeTracker {
  name = 'zhangsan';
  project = 'blog';
  #hours = 0;  // 私有类字段

  set addHours(hour) {
    this.#hours += hour;
  }

  get timeSheet() {
    return `${this.name} works ${this.#hours || 'nothing'} hours on ${this.project}`;
  }
}

let person = new TimeTracker();
person.addHours = 4; // 标准 setter
person.timeSheet     // zhangsan works 4 hours on blog


person.hours = 4 // Error Private field '#hours' must be declared in an enclosing class


//*****还可以将方法或 getter/setter 设为私有,只需要给这些方法名称前面加#即可:***********************************************************

class TimeTracker {
  name = 'zhangsan';
  project = 'blog';
  #hours = 0;   // 私有类字段

  set #addHours(hour) {
    this.#hours += hour;
  }

  get #timeSheet() {
    return `${this.name} works ${this.#hours || 'nothing'} hours on ${this.project}`;
  }

  constructor(hours) {
    this.#addHours = hours;
    console.log(this.#timeSheet);
  }
}

let person = new TimeTracker(4); // zhangsan works 4 hours on blog

由于尝试访问对象上不存在的私有字段会发生异常,因此需要能够检查对象是否具有给定的私有字段。可以使用 in 运算符来检查对象上是否有私有字段:

class Example {
  #field

  static isExampleInstance(object) {
    return #field in object;
  }
}

(3)静态公共字段

在ES6中,不能在类的每个实例中访问静态字段或方法,只能在原型中访问。ES 2022 提供了一种在 JavaScript 中使用 static 关键字声明静态类字段的方法。下面来看一个例子:

class Shape {
  static color = 'blue';

  static getColor() {
    return this.color;
  }

  getMessage() {
    return `color:${this.color}` ;
  }
}

可以从类本身访问静态字段和方法:

console.log(Shape.color); // blue
  console.log(Shape.getColor()); // blue
  console.log('color' in Shape); // true
  console.log('getColor' in Shape); // true
  console.log('getMessage' in Shape); // false

实例不能访问静态字段和方法:

const shapeInstance = new Shape();
  console.log(shapeInstance.color); // undefined
  console.log(shapeInstance.getColor); // undefined
  console.log(shapeInstance.getMessage());// color:undefined

静态字段只能通过静态方法访问:

console.log(Shape.getColor()); // blue
console.log(Shape.getMessage()); //TypeError: Shape.getMessage is not a function

这里的 Shape.getMessage() 就报错了,因为 getMessage 不是一个静态函数,所以它不能通过类名 Shape 访问。可以通过以下方式来解决这个问题:

getMessage() {
  return `color:${Shape.color}` ;
}

静态字段和方法是从父类继承的:

class Rectangle extends Shape { }

console.log(Rectangle.color); // blue
console.log(Rectangle.getColor()); // blue
console.log('color' in Rectangle); // true
console.log('getColor' in Rectangle); // true
console.log('getMessage' in Rectangle); // false

(4)静态私有字段和方法

与私有实例字段和方法一样,静态私有字段和方法也使用哈希 (#) 前缀来定义:

class Shape {
  static #color = 'blue';

  static #getColor() {
    return this.#color;
  }

  getMessage() {
    return `color:${Shape.#getColor()}` ;
  }
}
const shapeInstance = new Shape();
shapeInstance.getMessage(); // color:blue

私有静态字段有一个限制:只有定义私有静态字段的类才能访问该字段。这可能在使用 this 时导致出乎意料的情况:

class Shape {
  static #color = 'blue';
static #getColor() {
  return this.#color;
}
static getMessage() {
  return `color:${this.#color}` ;
}
getMessageNonStatic() {
  return `color:${this.#getColor()}` ;
}
}

class Rectangle extends Shape {}

console.log(Rectangle.getMessage()); // Uncaught TypeError: Cannot read private member #color from an object whose class did not declare it
const rectangle = new Rectangle();
console.log(rectangle.getMessageNonStatic()); // TypeError: Cannot read private member #getColor from an object whose class did not declare it

在这个例子中,this 指向的是 Rectangle 类,它无权访问私有字段 #color。当我们尝试调用 Rectangle.getMessage() 时,它无法读取 #color 并抛出了 TypeError。可以这样来进行修改:

class Shape {
  static #color = 'blue';
  static #getColor() {
    return this.#color;
  }
  static getMessage() {
    return `${Shape.#color}`;
  }
  getMessageNonStatic() {
    return `color:${Shape.#getColor()} color`;
  }
}

class Rectangle extends Shape {}
console.log(Rectangle.getMessage()); // color:blue
const rectangle = new Rectangle();
console.log(rectangle.getMessageNonStatic()); // color:blue

5)类静态初始化块

静态私有和公共字段只能让我们在类定义期间执行静态成员的每个字段初始化。如果我们需要在初始化期间像 try…catch 一样进行异常处理,就不得不在类之外编写此逻辑。该规范就提供了一种在类声明/定义期间评估静态初始化代码块的优雅方法,可以访问类的私有字段。

先来看一个例子:

class Person {
    static GENDER = "Male"
    static TOTAL_EMPLOYED;
    static TOTAL_UNEMPLOYED;

    try {
        // ...
    } catch {
        // ...
    }
}

上面的代码就会引发错误,可以使用类静态块来重构它,只需将try...catch包裹在 static 中即可:

class Person {
    static GENDER = "Male"
    static TOTAL_EMPLOYED;
    static TOTAL_UNEMPLOYED;
    
  static {
  	try {
        // ...
    } catch {
        // ...
    }
  }
}

此外,类静态块提供对词法范围的私有字段和方法的特权访问。这里需要在具有实例私有字段的类和同一范围内的函数之间共享信息的情况下很有用。

let getData;

class Person {
  #x
  
  constructor(x) {
    this.#x = { data: x };
  }

  static {
    getData = (obj) => obj.#x;
  }
}

function readPrivateData(obj) {
  return getData(obj).data;
}

const john = new Person([2,4,6,8]);

readPrivateData(john); // [2,4,6,8]

这里,Person 类与 readPrivateData 函数共享了私有实例属性。

# es12 (es2021)  


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、逻辑运算符和赋值表达式

逻辑运算符和赋值表达式,新特性结合了逻辑运算符(&&,||,??)和赋值表达式而JavaScript已存在的 复合赋值运算符有:

a ||= b
//等价于
a = a || (a = b)

a &&= b
//等价于
a = a && (a = b)

a ??= b
//等价于
a = a ?? (a = b)

4、数字分隔符

数字分隔符,可以在数字之间创建可视化分隔符,通过_下划线来分割数字,使数字更具可读性

const money = 1_000_000_000;
//等价于
const money = 1000000000;

1_000_000_000 === 1000000000; // true

# es11 (es2020 )


1、类的私有变量

通过在变量或函数前面添加一个简单的哈希符号,我们可以将它们完全保留为类内部使用

class Message {
 #message = "Howdy"

 greet() { console.log(this.#message) }
}

const greeting = new Message()

greeting.greet() // Howdy
console.log(greeting.#message) // Private name #message is not defined

2、Promise.allSettled

多个相互依赖的promise,通过Promise.allSettled,我们可以创建一个新的Promise,它只在所有传递给它的Promise都完成时返回一个数组,其中包含每个Promise的数据。

const p1 = new Promise((res, rej) => setTimeout(res, 1000));

const p2 = new Promise((res, rej) => setTimeout(rej("被拒绝"), 1000));

Promise.allSettled([p1, p2]).then(data => console.log(data));

//logs data 如下
// [
//   Object { status: "fulfilled", value: undefined},
//   Object { status: "rejected", reason: "被拒绝"}
// ]

3、合并空运算符 ---  ??

||    会将 ‘’和 0值也返回默认值

??  只会匹配null或者undefined不会匹配‘’和0

const x = null ?? 'string';         const x = null || 'string';

//logs x='string'                    //logs x='string'



const x = '' ?? 'string'            const x = '' || 'string'

//logs x = ''                       //logs x = 'string'


4、可选的链接操作符 ---  ?

在路劲后面点的前面加?    如 person?.profile?.name

有的时候我们不确定路径那一部分为null,就会报错:如

let person = {};

console.log(person.profile.name ?? "Anonymous"); // person.profile is undefined

//es2020解决方案
console.log(person?.profile?.name ?? "Anonymous");
console.log(person?.profile?.age ?? 18);

5、BigInt

当数值大于js最大安全整数,可以用bigint类型来处理,通过把字母n放在末尾,ps:标准数值不可以与bigint混在一起,所以都转成bigint型

const bigNum = 100000000000000000000000000000n;

console.log(bigNum * 2n); // 200000000000000000000000000000n

6、按需导入 -- import()

动态引入async / await

//main.js
const add = (num1, num2) => num1 + num2;

export { add };


//引用页面

const doMath = async (num1, num2) => {
  if (num1 && num2) {
    const math = await import('./math.js');
    console.log(math.add(5, 10));
  };
};

doMath(4, 2);

es2020转发ECMAScript 2020(ES2020)的新增语法 - 简书

# es10 (es2019)


1、trimStart(),trimEnd()

trimStart(),trimEnd()来头和尾进行单独控制空格文本去掉。trimLeft()、trimRight()是他们的别名

ES8为我们引入了Object.entries把一个对象转为[key, value]键值对的形式,Object.fromEntries()用于把键值对还原成对象结构。

const entries = [ ['foo', 'bar'] ];
const object = Object.fromEntries(entries);
// { foo: 'bar' }

2、Array.prototype.flat() / Array.prototype.flatMap()

如果原数组有空位,flat()方法会跳过空位。

flat() 每次只能展开一层,想要展开多少层,可传参数,如下

[1, 2, [3, 4]].flat();                 // [ 1, 2, 3, 4 ]

[1, 2, [3, 4, [5, 6]]].flat(2);        // [ 1, 2, 3, 4, 5, 6 ]



//如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数。

[1, [2, [3]]].flat(Infinity)             // [1, 2, 3]                                

flatMap() 方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行flat()方法。该方法返回一个新数组,不改变原数组。

flatMap()只能展开一层数组。

// 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]


const array1 = [1, 4, 9, 16];

// expected output: Array [8, 18, 32]

旧:const map1 = array1
    .filter(x => x !== 1)
    .map(x => x * 2);


flatMap:const map1 = array1.flatMap(x => x === 1? [] :[ x*2 ])

3、try...catch

在进行try...catch错误处理过程中,可以省略catch绑定的参数和括号,不会报错

try {
    return true;
  } catch {
    return false;
  }

es10转发:ES2019(ES10)带来的9个新特性 - 简书

4、String.prototype.matchAll

matchAll()为所有匹配的匹配对象返回一个迭代器

const raw_arr = 'test1  test2  test3'.matchAll((/t(e)(st(\d?))/g));
const arr = [...raw_arr];

5、Object.fromEntries()

返回一个给定对象自身可枚举属性的键值对数组

// 通过 Object.fromEntries, 可以将 Map 转化为 Object:

const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);

console.log(Object.fromEntries(map)); 

// { foo: "bar", baz: 42 }

# es9 (es2018)


What's New in ES2018 - SitePoint

1、async/await 异步迭代

await可以和for…of循环一起使用,以串行的方式运行异步操作

async function process(array) {
  for await (let i of array) {
    // doSomething(i);
  }
}

2、Promise.finally()

ES6为我们带来了Promise,但是它的结果要么成功then要么失败catch,使得我们的一些逻辑,如执行状态修改,结束回调都得在两边写一遍。

选择有了finally(),逻辑只可以放在一个地方了。

eturn new Promise((reslove, reject) => {
  // ...
}).then((res) => {
  // reslove
}).catch((err) => {
  // reject
}).finally(() => {//不传参
  // complete
});

3、Rest/Spread 属性

ES2015引入了Rest参数和扩展运算符。当时三个点...仅用于数组。Rest参数语法允许我们将一个剩余参数表示为一个数组。跟数组一样,Rest参数只能在声明的结尾处使用。

//数组
function foo(a, b, ...rest) {
  // a = 1
  // b = 2
  // rest = [3, 4, 5]
}
foo(1, 2, 3, 4, 5);

const values = [1, 2, 3, 5, 6];
console.log( Math.max(...values) ); // 6



//对象
const object = {
  a: 1,
  b: 2,
  c: 3,
  d: 4,
  e: 5
};
function foo({ a, b, ...rest }) {
  // a = 1
  // b = 2
  // rest = { c: 3, d: 4, e: 5 }
foo(object); 


//解构赋值
const { a, ...rest } = object;
// a = 1
// b = 2
// rest = { c: 3, d: 4, e: 5 }

es9转发:ES2018(ES9)新特性 - 简书

4. 正则表达式命名捕获组

const reg = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;
const match = reg.exec('2021-02-23');

5. 正则表达式反向断言

(?=p)、(?<=p) p 前面(位置)、p 后面(位置)
(?!p)、(?<!p>) 除了 p 前面(位置)、除了 p 后面(位置)

6. 正则表达式dotAll模式

正则表达式中点.匹配除回车外的任何单字符,标记s改变这种行为,允许行终止符的出现

/hello.world/.test('hello\nworld'); // false

#es8 (es2017)


1、字符串填充---padStart、padEnd

(padStart(目标长度[, 在前面要添加的字符串]) 和 padEnd(目标长度[, 在后面要添加的字符串]))

要添加的长度 = 目标长度 - 原本字符串的长度,

为负,则不变,

为正,只添加差值,

若差值>要添加的字符串长度,用空格填补(只有一个空格)

// padStart
'hello'.padStart(10);      // "     hello"

// padEnd
'hello'.padEnd(10)         // "hello     "

'test'.padEnd(4,'abcd')    //test

'test'.padEnd(5,'abcd')    //testa

2、Object.values() 、Object.entries()

Object.values() 以数组形式返回对象或者数组的值

对应的 Object.keys() 以数组形式返回对象或者数组的key

Object.entries () 返回一个包含所有对象自身属性的数组,作为 [key,value] 对的数组。

const data={name:'szz',age:18} ;

Object.values(data);

// ["szz", 18]



Object.entries(data);

// [["name", "szz"],["age", 18]]




const arr= [ 'a', 'b' ]
Object.entries(arr);

// [["0", "a"],["1", "b"]]

3、SharedArrayBuffer对象

SharedArrayBuffer 对象用来表示一个通用的,固定长度的原始二进制数据缓冲区,
/**
 * 
 * @param {*} length 所创建的数组缓冲区的大小,以字节(byte)为单位。
 * @returns {SharedArrayBuffer} 一个大小指定的新 SharedArrayBuffer 对象。其内容被初始化为 0。
 */
new SharedArrayBuffer(10)

4、Object.getOwnPropertyDescriptors()

此方法返回对象的所有自有(非继承的)属性描述符。

Object.getOwnPropertyDescriptors(obj) 接受一个对象,并返回一个带有描述符集的对象。

作用:ES2015 给我们带来了 Object.assign() 方法,它从一个或多个对象复制所有可枚举的属性,并返回一个新对象。

但是存在问题,它无法正确复制具有非默认特性(attribute) 的属性 (property)(getter,setter,不可写属性,等)。

5、函数参数列表和调用中的尾随逗号

参数后面跟的逗号是合法的

1、允许在参数定义和函数调用后面使用逗号,如下

const doSomething = (var1, var2,) => {

  //...

}

doSomething('test2', 'test2',)

2、对象和数组字面量后面跟的逗号会被忽略

let obj = {first: 'Jane',last: 'Doe',}和

let arr = ['red','green','blue',];
3、解构中的尾随逗号,在使用解构赋值时,尾随逗号也可以用于左侧:

// 带有尾后逗号的数组解构
[a, b,] = [1, 2];
 
// 带有尾后逗号的对象解构
var o = {
  p: 42, 
  q: true,
};
var {p, q,} = o;

不合法的尾随逗号

function f(,) {} // SyntaxError: missing formal parameter
(,) => {};       // SyntaxError: expected expression, got ','
f(,)             // SyntaxError: expected expression, got ','
 
function f(...p,) {} // SyntaxError: parameter after rest parameter
(...p,) => {}        // SyntaxError: expected closing parenthesis, got ','

//使用剩余参数(会报错)
var [a, ...b,] = [1, 2, 3];
// SyntaxError: rest element may not have a trailing comma

6、Async Functions (异步函数)

ps:在 async(异步) 函数中容易犯的一个错误就是在调用 async(异步) 函数时忘记使用 await :

async(异步) 函数变体,下面是已存在的

  • 异步函数声明: async function foo() {}
  • 异步函数表达式: const foo = async function () {};
  • 异步函数定义:let obj = { async foo() {} }
  • 异步箭头函数: const foo = async () => {};

async(异步) 函数总是返回 Promise

/*async(异步) 函数的 Promise 完成状态:*/
async function asyncFunc() {
    return 123;
}
 
asyncFunc()
.then(x => console.log(x));
    // 123


/*async(异步) 函数的 Promise 拒绝状态:*/
async function asyncFunc() {
    throw new Error('Problem!');
}
 
asyncFunc()
.catch(err => console.log(err));
    // Error: Problem!

/**
处理单个async
**/
async function asyncFunc() {
    const result = await otherAsyncFunc();
    console.log(result);
}
 
// 等价于:
function asyncFunc() {
    return otherAsyncFunc()
    .then(result => {
        console.log(result);
    });
}




/**
处理多个async
**/
async function asyncFunc() {
    const result1 = await otherAsyncFunc1();
    console.log(result1);
    const result2 = await otherAsyncFunc2();
    console.log(result2);
}
 
// 等价于:
function asyncFunc() {
    return otherAsyncFunc1()
    .then(result1 => {
        console.log(result1);
        return otherAsyncFunc2();
    })
    .then(result2 => {
        console.log(result2);
    });
}




/**
并行处理多个 async(异步) 返回值:
**/
async function asyncFunc() {
    const [result1, result2] = await Promise.all([
        otherAsyncFunc1(),
        otherAsyncFunc2(),
    ]);
    console.log(result1, result2);
}
 
// 等价于:
function asyncFunc() {
    return Promise.all([
        otherAsyncFunc1(),
        otherAsyncFunc2(),
    ])
    .then([result1, result2] => {
        console.log(result1, result2);
    });
}


/**
错误处理:
**/
async function asyncFunc() {
    try {
        await otherAsyncFunc();
    } catch (err) {
        console.error(err);
    }
}
 
// 等价于:
function asyncFunc() {
    return otherAsyncFunc()
    .catch(err => {
        console.error(err);
    });
}

客户端fetch api 为例

function fetchJson(url) {
    return fetch(url)
    .then(request => request.text())
    .then(text => {
        return JSON.parse(text);
    })
    .catch(error => {
        console.log(`ERROR: ${error.stack}`);
    });
}
fetchJson('http://example.com/some_file.json')
.then(obj => console.log(obj));



//通过async(异步)函数来编写异步代码

async function fetchJson(url) {
    try {
        let request = await fetch(url);
        let text = await request.text();
        return JSON.parse(text);
    }
    catch (error) {
        console.log(`ERROR: ${error.stack}`);
    }
}

不需要使用 await 的情况

有时,你只想触发异步计算,并且不需要关注它什么时候完成。可以用reurn替换掉await

await 是顺序执行的,Promise.all() 是并行的

/**
当两个方法,是并行请求的,那么下面的foo2()写法更好
**/
async function foo1() {
    const result1 = await asyncFunc1();
    const result2 = await asyncFunc2();
}

async function foo2() {    //更好
    const [result1, result2] = await Promise.all([
        asyncFunc1(),
        asyncFunc2(),
    ]);
}

异步函数和回调(map,foreach)

数组的 forEach() 方法在控制台中打印几个通过 URLs 加载的文件的内容:

async function logContent(urls) {
    urls.forEach( async url => {
        const content = await httpGet(url);
        console.log(content);
    });
 // Not finished here
}

//这段代码起作用了,但是会出现一个警告:httpGet() 返回的 Promise 对象是异步完成的,这也意味着当 forEach() 返回的时候回调可能还没有结束,因此你无法等到 logContent() 只能结束。

//如果你并不想要这个结果,你可以将 forEach() 转换为 for-of 循环。
async function logContent(urls) {
    for (const url of urls) {
        const content = await httpGet(url);
        console.log(content);
    }
}

//现在一切都在 for-of 循环完成后完成。但是,处理步骤依次发生:httpGet() 只是在第一次调用完成后再次调用。
//如果您希望处理步骤并行执行,你必须使用 Promise.all():
async function logContent(urls) {
    await Promise.all(urls.map(
        async url => {
            const content = await httpGet(url);
            console.log(content);
        }));
}

//map() 用于创建一个 Promises 数组。 
//我们对他们的完成结果并不感兴趣,我们只要 await(等待) 所有方法执行完成。
//这意味着我们希望的是在 async(异步) 函数完成之后所有的执行都已经完成。
//我们也可以返回 Promise.all() ,但是该函数的结果是一个数组,其元素都是未完成状态的。

例子:XMLHttpRequest

function httpGet(url, responseType="") {
    return new Promise(
        function (resolve, reject) {
            const request = new XMLHttpRequest();
            request.onload = function () {
                if (this.status === 200) {
                    // Success
                    resolve(this.response);
                } else {
                    // Something went wrong (404 etc.)
                    reject(new Error(this.statusText));
                }
            };
            request.onerror = function () {
                reject(new Error(
                    'XMLHttpRequest Error: '+this.statusText));
            };
            request.open('GET', url);
            xhr.responseType = responseType;
            request.send();
        });
}


//XMLHttpRequest 的 API 是基于回调的。
//通过一个 async(异步) 函数来实现它,意味着你必须在回调中返回 Promise 的完成(fulfill) 或拒绝(reject) 状态。
//这是不可能的,因为你只能通过 return 或者 throw 来完成这样的操作。
//你不能从回调函数内部 return 一个函数的结果。throw也有类似的约束。

因此,异步函数的通用编码风格是:

  • 直接使用 Promise 对象来构建异步原语。
  • 用异步函数来使用这些原语。

立即调用异步函数表达式

//可以创建一个异步函数 main()  并立即调用它:
async function main() {
    console.log(await asyncFunction());
}
main();


//或者您可以使用立即调用异步函数表达式:

(async function () {
    console.log(await asyncFunction());
})();


//另一个选择是立即调用异步箭头函数:
(async () => {
    console.log(await asyncFunction());
})();

7、共享内存 和 Atomics

并行(Parallelism) vs. 并发(Concurrency)

  • 并行(Parallelism) (parallel 并行 vs. serial 串行):同时执行多个任务;
  • 并发(Concurrency) (concurrent 并发 vs. sequential 连续):在重叠的时间段内(而不是一个接一个)执行几个任务。

两者密切相关,但不一样:

  • 并行中没有 并发 :单个指令,多数据(SIMD)。多次计算并行发生,但是在任何给定的时刻都只执行一个任务(指令)。
  • 并发中没有 并行:单核 CPU 上通过时间分配进行多任务处理。

转载:JavaScript 新书:探索 ES2016 与 ES2017(Exploring ES2016 and ES2017)-WEB前端开发

JS语法 ES6、ES7、ES8、ES9、ES10、ES11、ES12新特性_twinkle||cll的博客-CSDN博客_es8 js

#ES7(2016)


1. Array.prototype.includes()

[1].includes(1); // true
  • 1

2. 指数操作符

2**10; // 1024

转载 : JS语法 ES6、ES7、ES8、ES9、ES10、ES11、ES12新特性_twinkle||cll的博客-CSDN博客_es8 js

#es6 (2015)


详情请看:ES6 入门教程https://es6.ruanyifeng.com/

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值