Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ES知识点记录 #29

Open
bill-lai opened this issue Mar 22, 2022 · 0 comments
Open

ES知识点记录 #29

bill-lai opened this issue Mar 22, 2022 · 0 comments

Comments

@bill-lai
Copy link
Owner

记录

ES6

Intl

对象是 ECMAScript 国际化 API 的一个命名空间,它提供了精确的字符串对比、数字格式化,和日期时间格式化。

日期格式化

// 例如我们希望出现的日期信息格式是:“xxxx年xx月xx日 xx:xx:xx”。
const res = new Intl.DateTimeFormat("zh", {
  year: "numeric",
  /* 
        '2-digit'表示一定使用2位数字表示,
        因此,如果数值不超过10,会自动在前面加0
  */
  month: "2-digit",
  day: "2-digit",
  hour: "2-digit",
  minute: "2-digit",
  second: "2-digit",
  // 设置为false表示我们采用24小时制
  hour12: false,
}).format(new Date());

// IE11浏览器下的效果 完全符合我们的预期, - 2021年05月29日 10:15:27
// 但是在Chrome浏览器和Firefox浏览器下,却不是中文的年月日而是斜杠-2021/05/29 10:15:27, 还需要进一步字符处理下
console.log(res);

数字格式化,

new Intl.NumberFormat().format(12345.6789);
// 结果是:"12,345.679"

new Intl.NumberFormat(undefined, {
  minimumIntegerDigits: 2,
}).format(8);
// 结果是:"08"

new Intl.NumberFormat("zh", {
  style: "currency",
  currency: "CNY",
  currencyDisplay: "name",
}).format(12345.6789);
// 结果是:"12,345.68 人民币"

new Intl.NumberFormat("zh", {
  style: "currency",
  currency: "CNY",
}).format(12345.6789);
// 结果是:"¥12,345.68 "

const res = `星期${new Intl.NumberFormat("zh-Hans-CN-u-nu-hanidec").format(
  new Date().getDay(),
)}`;
// 结果是:"星期五"

判断是否在模块中

模块中顶层的 this 等于 undefined

const isModuleScript = this === undefined

Number.EPSILON

Number.EPSILON 属性表示 1 与Number可表示的大于 1 的最小的浮点数之间的差值。

判断数值是否相等

const isEqual = (target, origin) => Math.abs(origin - target) < Number.EPSILON

Math.hypot

计算两数平方开方Math.hypot(3,4) === 5

ES7

求幂运算符

// 2的平方
let squared = 2 ** 2;
// same as: 2 * 2   Math.Pow(2,2)

// 2的三次方
let cubed = 2 ** 3;
// same as: 2 * 2 * 2   Math.Pow(2,3)

ES8

String padding

如果某个字符串不够指定长度,会在头部或尾部补全。padStart()用于头部补全,padEnd()用于尾部补全。

"1".padStart(10, "0"); // "0000000001"
"12".padStart(10, "0"); // "0000000012"
"123456".padStart(10, "0"); // "0000123456"

// 格式提示
"12".padStart(10, "YYYY-MM-DD"); // "YYYY-MM-12"
"09-12".padStart(10, "YYYY-MM-DD"); // "YYYY-09-12"

Object.getOwnProperDescriptors

const shallowClone = (object) =>
  // 创建一个新对象
  Object.create(
    // 返回指定对象的原型
    Object.getPrototypeOf(object),
    // 返回指定对象所有自身属性
    Object.getOwnPropertyDescriptors(object),
  );
// 或者
const shallowMerge = (target, source) =>
  Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));

Atomics

Atomics 对象提供了一组静态方法对 SharedArrayBuffer 和 ArrayBuffer 对象进行原子操作。多线程之间操作数据不会引起混乱的api

es9

u 修饰符

ES6 对正则表达式添加了u修饰符,含义为“Unicode 模式”,用来正确处理大于\uFFFF的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。

/^\uD83D/u.test('\uD83D\uDC2A') // false
/^\uD83D/.test('\uD83D\uDC2A') // true

var s = '𠮷';

/^.$/.test(s) // false
/^.$/u.test(s) // true

// 新增大括号在u模式下标识一个字符 内放置16进制数
/\u{61}/.test('a') // false
/\u{61}/u.test('a') // true
/\u{20BB7}/u.test('𠮷') // true

/a{2}/.test('aa') // true
/a{2}/u.test('aa') // true
/𠮷{2}/.test('𠮷𠮷') // false
/𠮷{2}/u.test('𠮷𠮷') // true

/^\S$/.test('𠮷') // false
/^\S$/u.test('𠮷') // true

function codePointLength(text) {
  var result = text.match(/[\s\S]/gu);
  return result ? result.length : 0;
}

var s = '𠮷𠮷';

s.length // 4
codePointLength(s) // 2

y修饰符

除了u修饰符,ES6 还为正则表达式添加了y修饰符,叫做“粘连”(sticky)修饰符。

var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;

r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]

r1.exec(s) // ["aa"]
r2.exec(s) // null

const REGEX = /a/y;

// 指定从2号位置开始匹配
REGEX.lastIndex = 2;

// 不是粘连,匹配失败
REGEX.exec('xaya') // null

// 指定从3号位置开始匹配
REGEX.lastIndex = 3;

// 3号位置是粘连,匹配成功
const match = REGEX.exec('xaya');
match.index // 3
REGEX.lastIndex // 4

const TOKEN_Y = /\s*(\+|[0-9]+)\s*/y;
const TOKEN_G  = /\s*(\+|[0-9]+)\s*/g;

tokenize(TOKEN_Y, '3 + 4')
// [ '3', '+', '4' ]
tokenize(TOKEN_G, '3 + 4')
// [ '3', '+', '4' ]

tokenize(TOKEN_Y, '3x + 4')
// [ '3' ]
tokenize(TOKEN_G, '3x + 4')
// [ '3', '+', '4' ]

function tokenize(TOKEN_REGEX, str) {
  let result = [];
  let match;
  while (match = TOKEN_REGEX.exec(str)) {
    result.push(match[1]);
  }
  return result;
}

s 修饰符:dotAll 模式

正则表达式中,点(.)是一个特殊字符,代表任意的单个字符,但是有两个例外。一个是四个字节的 UTF-16 字符,这个可以用u修饰符解决;另一个是行终止符

/foo.bar/.test('foo\nbar')
// false

// es5
/foo[^]bar/.test('foo\nbar')
// true

// ES2018 引入s修饰符,使得.可以匹配任意单个字符。
/foo.bar/s.test('foo\nbar') // true

后行断言

/(?<=\$)\d+/.exec('Benjamin Franklin is on the $100 bill')  // ["100"]
/(?<!\$)\d+/.exec('its is worth about 90')                // ["90"]

const RE_DOLLAR_PREFIX = /(?<=\$)foo/g;
'$foo %foo foo'.replace(RE_DOLLAR_PREFIX, 'bar');

/(?<=(\d+)(\d+))$/.exec('1053') // ["", "1", "053"]
/^(\d+)(\d+)$/.exec('1053') // ["1053", "105", "3"]

// 后行断言引用必须在之前
/(?<=(o)d\1)r/.exec('hodor')  // null
/(?<=\1d(o))r/.exec('hodor')  // ["r", "o"]

具名组匹配

// es5
const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj[1]; // 1999
const month = matchObj[2]; // 12
const day = matchObj[3]; // 31

// es6
const RE_DATE = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // "1999"
const month = matchObj.groups.month; // "12"
const day = matchObj.groups.day; // "31"


let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;

'2015-01-02'.replace(re, '$<day>/$<month>/$<year>')
// '02/01/2015'


'2015-01-02'.replace(re, (
   matched, // 整个匹配结果 2015-01-02
   capture1, // 第一个组匹配 2015
   capture2, // 第二个组匹配 01
   capture3, // 第三个组匹配 02
   position, // 匹配开始的位置 0
   S, // 原字符串 2015-01-02
   groups // 具名组构成的一个对象 {year, month, day}
 ) => {
 let {day, month, year} = groups;
 return `${day}/${month}/${year}`;
});

// 引用 
const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
RE_TWICE.test('abc!abc') // true
RE_TWICE.test('abc!ab') // false

const RE_TWICE = /^(?<word>[a-z]+)!\1$/;
RE_TWICE.test('abc!abc') // true
RE_TWICE.test('abc!ab') // false

const RE_TWICE = /^(?<word>[a-z]+)!\k<word>!\1$/;
RE_TWICE.test('abc!abc!abc') // true
RE_TWICE.test('abc!abc!ab') // false

正则匹配索引

const text = 'zabbcdef';
const re = /ab/;
const result = re.exec(text);

result.index // 1
result.indices // [ [1, 3] ]

const text = 'zabbcdef';
const re = /ab+(cd)/;
const result = re.exec(text);

result.indices // [ [ 1, 6 ], [ 4, 6 ] ]

const text = 'zabbcdef';
const re = /ab+(cd(ef))/;
const result = re.exec(text);

result.indices // [ [1, 8], [4, 8], [6, 8] ]

const text = 'zabbcdef';
const re = /ab+(?<Z>cd)/;
const result = re.exec(text);

result.indices.groups // { Z: [ 4, 6 ] }

用 for...await...of 的语法来操作

function TimeOut(time) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve(time)
        }, time)
    })
}

async function test() {
    let arr = [TimeOut(2000), TimeOut(1000), TimeOut(3000)]
    for await (let item of arr) {
        console.log(Date.now(), item)
    }
}
test()
// 1560092345730 2000
// 1560092345730 1000
// 1560092346336 3000

ES10

flat flatMap

const arr1 = [0, 1, 2, [3, 4]];

console.log(arr1.flat());
// expected output: [0, 1, 2, 3, 4]

const arr2 = [0, 1, 2, [[[3, 4]]]];

console.log(arr2.flat(2));
// expected output: [0, 1, 2, [3, 4]]

[1, 2, 3, 4].flatMap((x) => [[x * 2]]);
// [[2], [4], [6], [8]]

Object.fromEntries

Object.fromEntries([
  ['foo', 'bar'],
  ['baz', 41]
])
// { foo: bar, baz: 41 }

const entries = new Map([
  ['foo', 'bar'],
  ['baz', 41]
])
Object.fromEntries(entries)
// { foo: bar, baz: 41 }

const map = new Map().set("foo", true).set("bar", false);
Object.fromEntries(map);
// { foo: true, bar: false }

const arr = [
  ["0", "a"],
  ["1", "b"],
  ["2", "c"],
];
const obj = Object.fromEntries(arr);
// { 0: "a", 1: "b", 2: "c" }


let query = Object.fromEntries(new URLSearchParams("foo=bar&baz=qux"));
// {foo: "bar", baz: "qux"}
console.log(query);

let arr = [
  { name: "Alice", age: 40 },
  { name: "Bob", age: 36 },
];
let obj = Object.fromEntries(arr.map(({ name, age }) => [name, age]));
// {Alice: 40, Bob: 36}
console.log(obj);

Symbol#description

const sym = Symbol('foo')
String(sym); // "Symbol(foo)"
sym.toString(); // "Symbol(foo)"
sym.description // foo

ES11

String.prototype.matchAll

String.prototype.matchAll返回一个正则表达式在当前字符串的所有匹配

const string = "test1test2test3";
const regex = /t(e)(st(\d?))/g;

const newdata = string.matchAll(regex);

for (const match of newdata) {
  console.log(match);
}
// ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"]
// ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"]
// ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"]

// 转为数组的方法一
[...newdata];

// 转为数组的方法二
Array.from(newdata);

Promise

Promise静态方法

  • Promise.race 只要任意一个 promise 的状态改变(不管成功 or 失败),那么就返回那个 promise
  • Promise.allSettled 只有等到所有实例都返回结果,不管是fulfilled还是rejected,实例才会结束
  • Promise.any 只要其中任意一个 promise 成功,就返回那个已经成功的 promise。

Promise实例属性status可以反应成功与否成功为fulfilled,失败为rejected,实例中有finally无论成功与否

async function test() {
  const promises = [fetch("./index.html"), fetch("https://does-not-exist/")];
  const results = await Promise.allSettled(promises);

  // 过滤出成功的请求
  const successfulPromises = results
    .filter((p) => p.status === "fulfilled")
    .map((p) => p.value);

  // 过滤出失败的请求,并输出原因
  const errors = results
    .filter((p) => p.status === "rejected")
    .map((p) => p.reason);

  console.log(errors);
}

Promise.allSettled(requests).finally(() => {
  console.log("所有请求已结束,并且我不关心是成功还是失败");
  // 关闭loading状态
  removeLoadingIndicator();
});

globalThis

globalThis 提供了一个标准的方式来获取不同环境下的全局 this 对象(也就是全局对象自身)

// 以前:
var getGlobal = function () {
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('unable to locate global object');
};

var globals = getGlobal();

// 现在
globalThis

空位合并操作符(??)

空值合并操作符(??)只有当左侧为null|undefined事会返回右侧的值

const a = null ?? 1           // 1
const a = undefined ?? 1      // 1
const a = '' ?? 1             // ''
const a = NaN ?? 1            // NaN
const a = 0 ?? 1              // 0

可选链式操作符(?.)

?. 也叫链判断运算符。它允许开发人员读取深度嵌套在对象链中的属性值,而不必验证每个引用。当引用为空时,表达式停止计算并返回 undefined。

var travelPlans = {
  destination: "DC",
  monday: {
    location: "National Mall",
    budget: 200,
  },
};
// new
console.log(travelPlans.tuesday?.location); // => undefined

// 可选链不能用于赋值
let object = {};
object?.property = 1; // Uncaught SyntaxError: Invalid left-hand side in assignment

// old
const res = travelPlans.tuesday && travelPlans.tuesday.location

bigInt

旧版本的 JS 标准最大的整数只能是 2^53-- 即Number.MAX_SAFE_INTEGER或Math.pow(2, 53)

如果无法准确计算大于2^53的数字的

现在使用BigInt 用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。

const theBiggestInt = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991);

ES12

String.protype.replaceAll

// old
const str = "a+b+c+";
const newStr = str.replace(/\+/g, "🤣");
console.log(newStr); //a🤣b🤣c🤣

// 获取使用 split,join进行字符串分割
const queryString = "q=query+string+parameters";
const withSpaces = queryString.split("+").join(" ");

// new
const str = "a+b+c+";
const newStr = str.replaceAll("+", "🤣");
console.log(newStr); //a🤣b🤣c🤣

const queryString = "q=query+string+parameters";
const withSpaces = queryString.replaceAll("+", " ");

WeakRef 和 FinalizationRegistry

WeakRef 提案主要包含两个新功能:

可以通过 WeakRef 类来给某个对象创建一个弱引用
可以通过 FinalizationRegistry 类,在某个对象被垃圾回收之后,执行一些自定义方法

WeakRef 主要用来缓存和映射一些大型对象,当你希望某个对象在不被其它地方引用的情况下及时地被垃圾回收,那么你就可以使用它。

const cache = new Map();

const finalizationGroup = new FinalizationGroup((iterator) => {
  for (const name of iterator) {
    const ref = cache.get(name);
    // WeakRef 通过 deref方法返回引用对象, 如果被收回则为空
    if (ref !== undefined && ref.deref() === undefined) {
      cache.delete(name);
    }
  }
});

function getImageCached(name) {
  const ref = cache.get(name); // 1
  if (ref !== undefined) { // 2
    // 查看弱引用是否有对象
    const deref = ref.deref();
    if (deref !== undefined) return deref;
  }
  const image = performExpensiveOperation(name); // 3

  // 创建weakRef
  const wr = new WeakRef(image); // 4
  cache.set(name, wr); // 5
  // image 注册垃圾回收 传入回调参数
  finalizationGroup.register(image, name); // 6
  return image; // 7
}

逻辑赋值运算符

// better 👶
function example(opts) {
  // Setters are not needlessly called.
  opts.foo ??= "bar";

  // No repetition of `opts.baz`.
  opts.baz ??= "qux";
}

// 默认参数值
function example(opts = { foo: "foo", baz: "baz" }) {
  return opts;
}




const giveKey = () => {
  return "somekey";
};
let userDetails = { name: "chika", age: 5, room: 10, key: "" };

// userDetails.key = userDetails.key || giveKey();
userDetails.key ||= giveKey();

console.log(userDetails.key);
//output : somekey




const deleteKey = () => {
  return " ";
};
let userDetails = { name: "chika", age: 5, room: 10, key: "990000" };

// userDetails.key = userDetails.key && deleteKey();
userDetails.key &&= deleteKey();

console.log(userDetails.key);
//output : ""



var x = null;
var y = 5;

// x = x ?? y;
console.log((x ??= y)); // => 5




function gameSettingsWithNullish(options) {
  options.gameSpeed ??= 1;
  options.gameDiff ??= "easy";
  return options;
}

function gameSettingsWithDefaultParams(gameSpeed = 1, gameDiff = "easy") {
  return { gameSpeed, gameDiff };
}

gameSettingsWithNullish({ gameSpeed: null, gameDiff: null }); // => {gameSpeed: 1, gameDiff: 'easy'}
gameSettingsWithDefaultParams(undefined, null); // => {gameSpeed: 1, gameDiff: null}



const getKey = () => {
  return "somekey";
};
let userDetails = { name: "chika", age: 5, room: 10 };
userDetails.key ??= getKey();
console.log(userDetails.key);
//output : "somekey"

数字分隔符

此功能使开发人员可以通过在数字组之间创建视觉分隔来使其数字文字更具可读性。

1_000_000_000; // 十亿
101_475_938.38; // 亿万

const amount = 12345_00; // 1234500
const amount = 123_4500; // 1234500
const amount = 1_234_500; // 1,234,500

0.000_001; // 百万分之一
1e10_000; // 10^10000

//
const binary_literals = 0b1010_0001_1000_0101;
const hex_literals = 0xa0_b0_c0;
//
const bigInt_literals = 1_000_000_000_000n;
//
const octal_literal = 0o1234_5670;

ES13

Class

// 可以使用声明式的类字段
class Pokemon {
  name = "Pikachu";

  attack() {}
  static starter() {}
}

// 私有属性/私有方法 我们将使用# 符号表示类的私有变量
class Student {
  #name = "something";

  #name2 = "name2";

  // 实例私有方法-只能在此Class内部使用
  #instancePrivateMethod() {
    console.log("实例私有方法");
  }
}

// static字段
class Student {
  // 静态私有字段声明-只能在此Class内部使用
  static #staticFieldPrivateName = "静态私有字段声明:真名-王撕葱";

  // 静态公有字段声明
  static staticFieldPublicName = "静态公有字段声明:名-撕葱";

  // 静态公有方法-ES6已支持
  static staticPublicMethod() {
    console.log("静态公有方法");
  }

  // 静态私有方法-只能在此Class内部使用
  static #staticPrivateMethod() {
    console.log("#staticPrivateMethod:", "静态私有方法");
  }
}

// 私有属性也可以设置 getter 和 setter 方法
class Student {
  #name = "something";

  #name2 = "name2";

  // 只能在此Class内部使用
  get #privateGet() {
    return this.#name;
  }
  // 只能在此Class内部使用
  set #privateSet(value) {
    this.#name = value;
  }

  get publicGet() {
    return this.#name2;
  }
  set publicSet(value) {
    this.#name2 = value;
  }
}

// 静态公有/私有字段
class ColorFinder {
  static #red = "#ff0000";
  static #green = "#00ff00";
  static #blue = "#0000ff";

  static colorName(name) {
    switch (name) {
      case "red":
        return ColorFinder.#red;
      case "blue":
        return ColorFinder.#blue;
      case "green":
        return ColorFinder.#green;
      default:
        throw new RangeError("unknown color");
    }
  }

  // Somehow use colorName
}

顶层 await

// awaiting.mjs
import { process } from "./some-module.mjs";
const dynamic = import(computedModuleSpecifier);
const data = fetch(url);
export const output = process((await dynamic).default, await data);

// usage.mjs
import { output } from "./awaiting.mjs";
export function outputPlusValue(value) { return output + value }

console.log(outputPlusValue(100));
setTimeout(() => console.log(outputPlusValue(100), 1000);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant