JavaScript ES2021 新特性解析

JavaScript ES2021 新特性解析

新特性列表
  • String.prototype.replaceAll
  • Promise.any
  • 逻辑运算符和赋值表达式
  • 数值分隔符
  • Intl.ListFormat
  • Intl.DateTimeFormat 的 dateStyle 和 timeStyle 选项
replaceAll

String.prototype.replaceAll

在 JavaScript 中,replace() 方法仅替换字符串中一个模式的第一个实例。如果我们要替换字符串中某个模式的所有匹配项,则唯一的方法是使用全局正则表达式。

提案方法 replaceAll() 返回一个新字符串,其中模式的所有匹配都会被替代项替换。模式可以是字符串或正则表达式,而替换项可以是字符串或针对每次匹配执行的函数。

let str = 'I use linux, I love linux'
str = str.replaceAll('linux', 'windows');

console.log(str)

/**** Output ****/
// I use windows, I love windows
Promise.any

ES2021 将引入 Promise.any() 方法,只要这个方法命中了 Promise 列表 / 数组中的第一个已解析的 Promise,就会短路并返回一个值(如示例 1a 中所述)。如果所有的 promise 都被拒绝,那么它将抛出一个汇总错误消息(如示例 1b 所示)。

它与 Promise.race() 不同,因为一旦给定的 Promise 之一被解析或拒绝,Promise.any() 方法就会短路。

示例 1a:即使一个 Promise 在一个已解析的 Promise 之前被拒绝,Promise.any() 仍将返回第一个已解析的 Promise。

Promise.any([
  new Promise((resolve, reject) => setTimeout(reject, 200, 'Third')),
  new Promise((resolve, reject) => setTimeout(resolve, 1000, 'Second')),
  new Promise((resolve, reject) => setTimeout(resolve, 2000, 'First')),
])
.then(value => console.log(`Result: ${value}`))
.catch (err => console.log(err))

/**** Output ****/
// Result: Second

示例 1b:当所有的 Promise 都被拒绝时,将抛出 AggregateError。

Promise.any([
  new Promise((resolve, reject) => setTimeout(reject, 200, 'Third')),
  new Promise((resolve, reject) => setTimeout(reject, 1000, 'Second')),
  new Promise((resolve, reject) => setTimeout(reject, 2000, 'First')),
])
.then(value => console.log(`Result: ${value}`))
.catch (err => console.log(`error of AggregateError: ` , err))

/**** Output ****/
// error of AggregateError: ... 
Promise.any([
  Promise.reject('Error 1'),
  Promise.reject('Error 2'),
  Promise.reject('Error 3')
])
.then(value => console.log(`Result: ${value}`))
.catch (err => console.log(err))
 
/****  输出  ****/
// AggregateError: All promises were rejected
逻辑运算符和赋值表达式

在 JavaScript 中有许多赋值运算符和逻辑运算符,如以下基本示例:

// Assignment Operator Example
let num = 5
num+=10
console.log(num) // 15
// Logical Operator Example
let num1 = 6
let num2 = 3
console.log(num1 === 6 && num2 === 2) // false
console.log(num1 === 6 || num2 === 2) // true

新的提案让我们将能把逻辑运算符和赋值运算符结合起来。以下是 &&、||和?? 运算符的一些示例:

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

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

a ??= b
//等价于
a = a ?? (a = b)
带有 && 运算符的逻辑赋值运算符

仅当 LHS 值为真时,才将 RHS 变量值赋给 LHS 变量。

// Logical Assignment Operator with && operator
let num1 = 5
let num2 = 10
num1 &&= num2
console.log(num1) // 10
// Line 5 can also be written as following ways
// 1. num1 && (num1 = num2)
// 2. if (num1) num1 = num2
带有||的运算符逻辑赋值运算符

仅当 LHS 值为假时,才将 RHS 变量值赋给 LHS 变量。

// Logical Assignment Operator with || operator
let num1
let num2 = 10
num1 ||= num2
console.log(num1) // 10
// Line 5 can also be written as following ways
// 1. num1 || (num1 = num2)
// 2. if (!num1) num1 = num2
带有?? 运算符的逻辑赋值运算符

ES2020 引入了空值合并运算符,其也可以与赋值运算符结合使用。仅当 LHS 为 undefined 或仅为 null 时,才将 RHS 变量值赋给 LHS 变量。

// Logical Assignment Operator with ?? operator
let num1
let num2 = 10
num1 ??= num2
console.log(num1) // 10
num1 = false
num1 ??= num2
console.log(num1) // false
// Line 5 can also be written as following ways
// num1 ?? (num1 = num2)
数值分隔符 _

新引入的数值分隔符使用 _(下划线)字符,在数值组之间提供分隔,使数值读起来更容易。例如:

let number = 100_000 
console.log(number)
/**** Output ****/
// 100000

数字分隔符有助于提高各种数字文字的可读性:

// A decimal integer literal with its digits grouped per thousand:
1_000_000_000_000
// A decimal literal with its digits grouped per thousand:
1_000_000.220_720
// A binary integer literal with its bits grouped per octet:
0b01010110_00111000
// A binary integer literal with its bits grouped per nibble:
0b0101_0110_0011_1000
// A hexadecimal integer literal with its digits grouped by byte:
0x40_76_38_6A_73
// A BigInt literal with its digits grouped per thousand:
4_642_473_943_484_686_707n

它们甚至适用于八进制整数文字(尽管 我想不出 其中分隔符为此类文字提供值 的示例)

// A numeric separator in an octal integer literal: ????‍♀️
0o123_456
请注意,JavaScript 还具有不带显式 0o 前缀的八进制文字的旧式语法。例如,017 === 0o17。在严格模式下或模块内不支持此语法,并且在现代代码中不应使用此语法。因此,这些文字不支持数字分隔符。使用 0o17 风格的文字代替。
Intl.ListFormat

ListFormat 对象带有两个参数,它们都是可选的。第一个参数是语言(语言环境),第二个参数是具有两个属性(样式和类型)的选项对象。

new Intl.ListFormat([locales[, options]])

Intl.ListFormat 有一个称为 format() 的方法,该方法接收一个数组作为一个参数,并以特定于语言环境的方式对其进行格式化。

下面给出了一些示例,这些示例结合了不同的语言环境和选项。

const arr = ['Pen', 'Pencil', 'Paper']
let obj = new Intl.ListFormat('en', { style: 'short', type: 'conjunction' })
console.log(obj.format(arr)) 
/**** Output ****/
// Pen, Pencil, & Paper

obj = new Intl.ListFormat('en', { style: 'long', type: 'conjunction' })
console.log(obj.format(arr)) 
/**** Output ****/
// Pen, Pencil, and Paper

obj = new Intl.ListFormat('en', { style: 'narrow', type: 'conjunction' })
console.log(obj.format(arr)) 
/**** Output ****/
// Pen, Pencil, Paper

// Passing in Italy language tag
obj = new Intl.ListFormat('it', { style: 'short', type: 'conjunction' })
console.log(obj.format(arr)) 
/**** Output ****/
// Pen, Pencil e Paper

// Passing in German language tag
obj = new Intl.ListFormat('de', { style: 'long', type: 'conjunction' })
console.log(obj.format(arr)) 
/**** Output ****/
// Pen, Pencil und Paper
dateStyle 和 timeStyle 选项

Intl.DateTimeFormat 对象是用来启用语言敏感的日期和时间格式的对象构造器。新提案的 dateStyle 和 timeStyle 选项可用于请求给定长度的,特定于语言环境的日期和时间。

下面是不同的选项和语言(区域设置)的一些示例:

// Time only with short format
let o = new Intl.DateTimeFormat('en' , { timeStyle: 'short' })
console.log(o.format(Date.now()))
// 11:27 PM

// Time only with medium format
o = new Intl.DateTimeFormat('en' , { timeStyle: 'medium'})
console.log(o.format(Date.now()))
// 11:27:57 PM

// Time only with long format
o = new Intl.DateTimeFormat('en' , { timeStyle: 'long' })
console.log(o.format(Date.now()))
// 11:27:57 PM GMT+11

// Date only with short format
o = new Intl.DateTimeFormat('en' , { dateStyle: 'short'})
console.log(o.format(Date.now()))
// 10/6/20

// Date only with medium format
o = new Intl.DateTimeFormat('en' , { dateStyle: 'medium'})
console.log(o.format(Date.now()))
// Oct 6, 2020

// Date only with long format
o = new Intl.DateTimeFormat('en' , { dateStyle: 'long'})
console.log(o.format(Date.now()))
// October 6, 2020

dateStyle 和 timeStyle 选项与不同的语言标记一起使用,如下例所示:

let abc
// English language
abc = new Intl.DateTimeFormat('en' , { timeStyle: 'short', dateStyle: 'long'})
console.log(abc.format(Date.now()))
// October 6, 2020 at 11:40 PM

// Italian language
abc = new Intl.DateTimeFormat('it' , { timeStyle: 'short', dateStyle: 'long'})
console.log(abc.format(Date.now()))
// 6 ottobre 2020 23:40

// German language
abc = new Intl.DateTimeFormat('de' , { timeStyle: 'short', dateStyle: 'long'})
console.log(abc.format(Date.now()))
// 6. Oktober 2020 um 23:40

英文原文:https://codeburst.io/exciting-features-of-javascript-es2021-es12-1de8adf6550b

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

当我们在使用多实例promises的时候,特别是当他们互相依赖的时候,记录每次调试发生的错误是非常有用的,通过使用Promise.allSettled,我们可以创建一个仅仅当全部的promises完成才返回的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));
// [
//   Object { status: "fulfilled", value: undefined},
//   Object { status: "rejected", reason: undefined}
// ]
3.空值合并运算符(??)

空值合并运算符(??)是一个逻辑运算符。当左侧操作数为 null 或 undefined 时,其返回右侧的操作数。否则返回左侧的操作数。

与逻辑或(||)操作符不同,逻辑或会在左操作数为 假值 时返回右侧操作数。也就是说,如果你使用 || 来为某些变量设置默认的值时,你可能会遇到意料之外的行为。比如为假值(例如,’'或 0)时。见下面的例子。

const foo = null ?? 'default string';
console.log(foo);
// expected output: "default string"

const baz = 0 ?? 42;
console.log(baz);
// expected output: 0
4.可选链式操作符(?.)

与空值合并操作符类似,JavaScript在处理错误值时可能无法按我们希望的方式工作。如果他是未定义的,我们可以返回一个值,但是如果到它的路径是未定义的呢?

通过在点表示法之前添加一个问号,我们可以使值的路径的任何部分成为可选的,这样我们仍然可以与它交互。

如果访问对象上不存在的属性上的属性,使用.操作符会直接报错。

let person = {};
// 如果person对象不包含profile会报错
console.log(person.profile.name ?? "Anonymous"); // person.profile is undefined
// 下面的路径是可选的,如果person对象不包含profile属性直接返回"Anonymous"
console.log(person?.profile?.name ?? "Anonymous");
console.log(person?.profile?.age ?? 18);
5.BigInt

我们不会深入讨论技术细节,但是由于JavaScript处理数字的方式,当您达到足够高的级别时,就会出现一些意料之外的事情。JavaScript能处理的最大数字是2的53次方,我们可以使用Number的MAX_SAFE_INTEGER属性得到这个值。

const max = Number.MAX_SAFE_INTEGER;
console.log(max); // 9007199254740991
// 如果超过了这个范围,事情就会变得有点奇怪……

console.log(max + 1); // 9007199254740992
console.log(max + 2); // 9007199254740992
console.log(max + 3); // 9007199254740994
console.log(Math.pow(2, 53) == Math.pow(2, 53) + 1); // true

我们可以使用新的BigInt数据类型来解决这个问题。通过把字母n放在末尾,我们可以开始使用并与大得离谱的数字进行交互。我们无法将标准数字与BigInt数字混合在一起,因此任何数学运算都需要使用BigInt来完成。

const bigNum = 100000000000000000000000000000n;
console.log(bigNum * 2n); // 200000000000000000000000000000n
6.动态引入(Dynamic Import)

如果你有一个工具函数文件,其中一些函数可能很少被使用,将他们完整导入可能只是浪费资源。现在我们可以使用async/await来动态地导入我们需要的依赖项。

这与我们当前Parcel的设置不兼容,因为我们使用的导入只能在Node.js环境中工作。

math.js

const add = (num1, num2) => num1 + num2;
export { add };

index.js

const doMath = async (num1, num2) => {
  if (num1 && num2) {
    const math = await import('./math.js');
    console.log(math.add(5, 10));
  };
};
doMath(4, 2);
ES2019
1.String.prototype.trimStart() / String.prototype.trimEnd()

在接收用户输入的文本,我们经常会把头尾的空格文本去掉,来规避展示的不受控情况。自ES5来,String.prototype.trim()被用于去除头尾上的空格、换行符等,现在通过trimStart(),trimEnd()来头和尾进行单独控制。trimLeft()、trimRight()是他们的别名。

const string = ' Hello ES2019! ';
string.trimStart();
// 'Hello ES2019! '
string.trimEnd();
// ' Hello ES2019!'

可以大胆想象一下,未来是不是可以通过传入指定字符串参数来对头尾进行删减。

2.Object.fromEntries()

ES8为我们引入了Object.entries把一个对象转为[key, value]键值对的形式,可以运用于像 Map这种结构中。凡事有来有回,Object.fromEntries()用于把键值对还原成对象结构。

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

把数组展平是Array原型给我们带来的新特性,通过传入层级深度参数(默认为1),来为下层数组提升层级。如果想提升所有层级可以写一个比较大的数字甚至是Infinity,当然不推荐这么做。

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

Array.prototype.flatMap()它是Array.prototype.map()和Array.prototype.flat()的组合,通过对map调整后的数据尝试展平操作。

[1, 2, [3, 4]].flatMap(v => {
  if (typeof v === 'number') {
    return v * 2
  } else {
    return v.map(v => v * 2)
  }
})
// [2, 4, 6, 8]

4.catch 的参数改为可选
在进行try…catch错误处理过程中,如果没有给catch传参数的话,代码就会报错。有时候我们并不关心错误情况,如:

const isValidJSON = json => {
  try {
    JSON.parse(json);
    return true;
  } catch (unusedError) {
    // Unused error parameter
    return false;
  }
};

在新规范中,我们可以省略catch绑定的参数和括号。

const isValidJSON = json => {
  try {
    JSON.parse(json);
    return true;
  } catch {
    return false;
  }
};
5.Symbol.description

Symbol是ES6中引入的基本数据类型,可以用作对象属性的标识符。描述属性是只读的,可用于获取符号对象的描述,更好了解它的作用。

const symbol = Symbol('This is a Symbol');
symbol;
// Symbol(This is a Symbol)
Symbol.description;
// 'This is a Symbol'
6.JSON Superset 超集

之前如果JSON字符串中包含有行分隔符(\u2028)和段落分隔符(\u2029),那么在解析过程中会报错。

JSON.parse('"\u2028"');
// SyntaxError

现在ES2019对它们提供了支持。

JSON.parse('"\u2028"');
// ''
7.JSON.stringify() 加强格式转化

我们看一下熟知的emoji表现:

'😎'.length;
// 2

JavaScript将emoji解释为两个字符的原因是UTF-16将emojis(以及其他不寻常的字符)表示为两个代理项的组合。我们的emoji用字符’\uD83D’和’\uDE0E’编码。但是如果试图单独编写这样一个字符,例如’\uD83D’,则会认为这是一个无效的文本字符串。在早期版本中,这些字符将替换为特殊字符:

JSON.stringify('\uD83D');
// '"�"'

现在在字符代码之前插入转义字符,结果仍是可读且有效的UTF-8/UTF-16代码:

JSON.stringify('\uD83D');
// '"\\ud83d"'
8.Array.prototype.sort() 更加稳定

之前,规范允许不稳定的排序算法,如快速排序。

let array = [
  {a: 1, b: 2},
  {a: 2, b: 2},
  {a: 1, b: 3},
  {a: 2, b: 4},
  {a: 5, b: 3}
];
array.sort((a, b) => a.a - b.a);
// [{a: 1, b: 2}, {a: 1, b: 3}...] / [{a: 1, b: 3}, {a: 1, b: 2}...]

在之前的排序中,可能出现[{a: 1, b: 2}, {a: 1, b: 3}…]、[{a: 1, b: 3}, {a: 1, b: 2}…]等多种情况。

现在所有主流浏览器都使用稳定的排序算法。实际上,这意味着如果我们有一个对象数组,并在给定的键上对它们进行排序,那么列表中的元素将保持相对于具有相同键的其他对象的位置。

9.Function.prototype.toString() 重新修订

从ES2019开始,Function.prototype.toString()将从头到尾返回源代码中的实际文本片段。这意味着还将返回注释、空格和语法详细信息。

function /* a comment */ foo() {}

之前,Function.prototype.toString()只会返回了函数的主体,但没有注释和空格。

foo.toString();
// 'function foo() {}'

但现在,函数返回的结果与编写的一致。

foo.toString();
// 'function /* a comment  */ foo () {}'​
ES2018
1.async/await 异步迭代

ES8为我们带来async/await,使我们能在同步的写法中执行异步函数,但是在循环中:

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

上面代码执行不符合预期,循环本身依旧保持同步,并在在内部异步函数之前全部调用完成。

ES2018引入异步迭代器(asynchronous iterators),使得await可以和for…of循环一起使用,以串行的方式运行异步操作。

async function foo(array) {
  for await (let i of array) {
    doSomething(i);
  }
}
2.Promise.finally()

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

选择有了finally(),逻辑只可以放在一个地方了,这有点像以前jQuery ajax的complete。

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

finally()没有参数传入。

3.Rest/Spread 属性

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

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

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

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

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

// 5

现在对象也可以使用它们了。

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

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

跟数组一样,Rest参数只能在声明的结尾处使用。

4.正则表达式 s(dotAll模式)标记

在正则中,.可以匹配任意字符,除了换行符。

/hello.es9/.test('hello\nes9');
// false

ES2018引入了dotAll模式,通过使用标记s选项,.就可以匹配换行符。

/hello.es9/s.test('hello\nes9');
// true
5.正则表达式 Unicode 转义

目前在正则中,可以通过字符集的名称来匹配字符。如s代表空白

/^\s+$/u.test(' ');
// true

在ES2018添加了Unicode属性转义,形式为\p{…}和\P{…},在正则表达式中使用标记u(unicode)选项。

/^\p{White_Space}+$/u.test(' ') // 空格
// true
/^\p{Script=Greek}+$/u.test('μετά') // 希腊字母
// true
/^\p{Script=Latin}+$/u.test('Grüße') // 匹配拉丁字母
// true
/^\p{Surrogate}+$/u.test('\u{D83D}') // 匹配单独的替代字符
// true
6.lookbehind 反向断言

目前JavaScript在正则表达式中支持先行断言(lookahead)。这意味着匹配会发生,但是断言没有包含在整个匹配字段中。

如匹配字符串“10 hours”中紧跟着是”hours”的数字:

const reg = /\d+(?= hours)/u;
const matched = reg.exec('10 hours');
matched[0];
// 42

匹配字符串“10 minutes”中紧跟着不是”hours”的数字:

const reg = /\d+(?! hours)/u;
const matched = reg.exec('10 minutes');
matched[0];
// 42

ES2018引入以相同方式工作但是匹配前面的反向断言(lookbehind)。

匹配字符串“hours10”中”hours”后面的数字:

const reg = /(?<=hours)\d+/u;
const matched = reg.exec('hours10');
matched[0];
// 10

匹配字符串“minutes10”中数字前面不是“hours”:

const reg = /(?<!hours)\d+/u;
const matched = reg.exec('minutes10');
matched[0];
// 10
7.Named capture groups 正则表达式命名捕获组

目前,正则表达式中小括号匹配的分组是通过索引编号的:

const reg = /(\d{4})-(\d{2})-(\d{2})/u;
const matched = reg.exec('2018-12-31');
matched[0];  // 2018-12-12
matched[1];  // 2018
matched[2];  // 12
matched[3];  // 31

代码可读性较差,并且改变正则表达式的结构有可能改变匹配对象的索引。

ES2018允许命名捕获组使用符号?, 可以指定小括号中匹配内容的名称放在groups里,这样可以提高代码的可读性。

const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const matched = reg.exec('2018-12-31');
matched.groups.year;  // 2018
matched.groups.month;  // 12
matched.groups.day;  // 31

命名捕获组也可以使用在replace()方法中。例如将日期转换为“年月日”格式:

const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
'2018-12-31'.replace(reg, '$<year>年$<month>月$<day>日');
// 2018年12月31日
8.非转义序列的模板字符串

ES2018移除对 ECMAScript在带标签的模版字符串中转义序列的语法限制。

之前,\u开始一个 unicode转义,\x开始一个十六进制转义,\后跟一个数字开始一个八进制转义。这使得创建特定的字符串变得不可能,例如Windows文件路径C:\uuu\xxx\111。

ES2017
1.String.prototype.padStart / String.prototype.padEnd(字符串填充)

正如其名,这俩函数的作用就是在字符串的头部和尾部增加新的字符串,并且返回一个具有指定长度的新的字符串。你可以使用指定的字符、字符串或者使用函数提供的默认值-空格来填充源字符串。

// 语法
str.padStart(targetLength [, padString])
str.padEnd(targetLength [, padString])

padStart从字符串开端左边开始填充,并且返回给定长度的字符串,如果不给定填充字符串,将使用默认字符空格,具体示例如下:

'es8'.padStart(2);          // 'es8',如果长度小于原字符串,返回原字符串
'es8'.padStart(5);          // '  es8'
'es8'.padStart(6, 'woof');  // 'wooes8',如果填充字符串+原字符串长度大于

给定长度,则从填充字符串左边开始截取(这是由于与其他语言PHP、Ruby保持一致)

'es8'.padStart(14, 'wow');  // 'wowwowwowwoes8'
'es8'.padStart(7, '0');     // '0000es8',如果填充字符串+原字符串长度小于给定长度,则从填充字符串重复填充

padEnd从字符串尾端右边开始填充,并且返回给定长度的字符串,如果不给定填充字符串,将使用默认字符空格,具体示例如下:

'es8'.padEnd(2);          // 'es8',如果长度小于原字符串,返回原字符串
'es8'.padEnd(5);          // 'es8  '
'es8'.padEnd(6, 'woof');  // 'es8woo',如果填充字符串+原字符串长度大于给定长度,则从填充字符串左边开始截取(这是由于与其他语言PHP、Ruby保持一致)
'es8'.padEnd(14, 'wow');  // 'es8wowwowwowwo'
'es8'.padEnd(7, '6');     // 'es86666',如果填充字符串+原字符串长度小于给定长度,则从填充字符串重复填充
2.Object.values / Object.entries

Object.values 函数将会返回一个数组,该数组的内容是函数参数(一个对象)可遍历属性的属性值。

// 语法
Object.values(obj)
// 示例
const obj = { x: 'xxx', y: 1 };
Object.values(obj); // ['xxx', 1]
const obj = ['e', 's', '8']; // same as { 0: 'e', 1: 's', 2: '8' };
Object.values(obj); // ['e', 's', '8']
// when we use numeric keys, the values returned in a numerical 
// order according to the keys
const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.values(obj); // ['yyy', 'zzz', 'xxx']
Object.values('es8'); // ['e', 's', '8']

Object.entries函数与Object.values函数类似,也是返回一个数组,只不过这个数组是对象自身可迭代属性 [key-value]的对数组。

// 语法
Object.entries(obj)
// 示例
const obj = { x: 'xxx', y: 1 };
Object.entries(obj); // [['x', 'xxx'], ['y', 1]]
const obj = ['e', 's', '8'];
Object.entries(obj); // [['0', 'e'], ['1', 's'], ['2', '8']]
const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.entries(obj); // [['1', 'yyy'], ['3', 'zzz'], ['10': 'xxx']]
Object.entries('es8'); // [['0', 'e'], ['1', 's'], ['2', '8']]
3.Object.getOwnPropertyDescriptors

该函数返回指定对象(参数)的所有自身属性描述符。所谓自身属性描述符就是在对象自身内定义,不是通过原型链继承来的属性。
Object.getOwnPropertyDescriptors(obj)
obj参数即为源对象,该函数返回的每个描述符对象可能会有的 key 值分别是:configurable、enumerable、writable、get、set和value。示例代码如下:

const obj = { 
  get es7() { return 777; },
  get es8() { return 888; }
};
Object.getOwnPropertyDescriptor(obj);
// {
//   es7: {
//     configurable: true,
//     enumerable: true,
//     get: function es7(){}, //the getter function
//     set: undefined
//   },
//   es8: {
//     configurable: true,
//     enumerable: true,
//     get: function es8(){}, //the getter function
//     set: undefined
//   }
// }
4.函数参数列表和调用中的尾逗号

此处结尾逗号指的是在函数参数列表中最后一个参数之后的逗号以及函数调用时最后一个参数之后的逗号。ES8允许在函数定义或者函数调用时,最后一个参数之后存在一个结尾逗号而不报 SyntaxError的错误。示例代码如下:

// 函数声明时
function es8(var1, var2, var3,) {
  // ...
}
// 函数调用时
es8(10, 20, 30,);

ES8的这项新特性受启发于对象或者数组中最后一项内容之后的逗号,如 [10, 20, 30,] 和 { x: 1, } 。

5.异步函数

由 async关键字定义的函数声明定义了一个可以异步执行的函数,它返回一个 AsyncFunction类型的对象。异步函数的内在运行机制和 Generator函数非常类似,但是不能转化为 Generator函数。
示例代码如下:

function fetchTextByPromise() {
  return new Promise(resolve => { 
    setTimeout(() => { 
      resolve("es8");
    }, 2000);
  });
}
async function sayHello() { 
  const externalFetchedText = await fetchTextByPromise();
  console.log(`Hello, ${externalFetchedText}`); // Hello, es8
}

sayHello();

有了 async/await,我们的代码执行异步看起来像执行同步一样。可以从头到尾读起来非常简单和易懂,因为出现结果顺序和函数题中从头到尾顺序一样啊!

6.共享内存与原子操作

当内存被共享时,多个线程可以并发读、写内存中相同的数据。原子操作可以确保那些被读、写的值都是可预期的,即新的事务是在旧的事务结束之后启动的,旧的事务在结束之前并不会被中断。这部分主要介绍了 ES8 中新的构造函数 SharedArrayBuffer 以及拥有许多静态方法的命名空间对象 Atomic 。
Atomic 对象类似于 Math对象,拥有许多静态方法,所以我们不能把它当做构造函数。 Atomic 对象有如下常用的静态方法:

  • add/sub - 为某个指定的value值在某个特定的位置增加或者减去某个值
  • and/or/xor - 进行位操作
  • load- 获取特定位置的值
ES2016
1.Array.prototype.includes(数组元素判断)
// 语法
Array.prototype.includes(value:任意值): return boolean
// 例子
['a', 'b', 'c'].includes('a')
> true
['a', 'b', 'c'].includes('d')
> false

includes方法与 indexOf 方法很相似——下面两个表达式是等价的:

arr.includes(x)
arr.indexOf(x) >= 0

唯一的区别是 includes()方法能找到 NaN,而 indexOf()不行:

[NaN].includes(NaN)
> true
[NaN].indexOf(NaN)
> -1

另外 includes不会区分 +0 和-0(这也与其他 JavaScript特性表现一致):

[-0].includes(+0)
> true
2.Exponentiation Operator(求冥运算)
// 语法
x ** y
// 例子
let a = 2 ** 3 // 2*2*2 = 2^3
let b = 3 ** 2 // 3*3 = 3^2

其实与以下表达式Math.pow(x, y)结果相同:

let a = 7 ** 12
let b = 2 ** 7
console.log(a === Math.pow(7,12)) // true
console.log(b === Math.pow(2,7)) // true

另外还可以简写:

let a = 7
a **= 12
let b = 2
b **= 7
console.log(a === Math.pow(7,12)) // true
console.log(b === Math.pow(2,7)) // true
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值