最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 你有一份 ECMAScript 特性速查表,请查收

    正文概述 掘金(大古同学)   2021-04-12   388

    本文以倒序的顺序并通过代码示例或简单的罗列展示所有 ECMAScript 版本提供的功能。 旨在为大家在编码时提供 ECMAScript 特性速查表

    ES2021-ES12

    String.protype.replaceAll

    在 ES2021 之前,要替换掉一个字符串中的所有指定字符,我们可以这么做:

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

    ES2021 则提出了 replaceAll 方法,并将其挂载在 String 的原型上,可以这么用:

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

    Promise.any

    Promise.any

    • 接收一个 Promise 可迭代对象,只要其中任意一个 promise 成功,就返回那个已经成功的 promise
    • 如果所有的 promises 都失败/拒绝,就返回一个失败的 promise

    Promise.race 的对比:

    • 只要任意一个 promise 的状态改变(不管成功 or 失败),那么就返回那个 promise

    Promise.all()的对比

    • 只要任意一个 promise 失败,则返回失败的 promise
    • 当所有异步操作都成功后,才返回 promise,返回值组成一个数组
    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, "很快完成");
    });
    
    // 使用 .then .catch
    Promise.any([pErr, pSlow, pFast])
      .then((value) => {
        // 返回最先成功的一个promise ,即: pFast-"很快完成"
        console.log(value);
      })
      .catch((err) => {
        // 所有的 promise 都失败时触发
      });
    
    // 使用 async-await
    try {
      const first = await Promise.any(promises); // 任何一个 promise 成功返回。
      console.log(first);
    } catch (error) {
      // 所有的 promise 都失败了
      console.log(error);
    }
    

    WeakRef

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

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

    上述两个新功能可以同时使用,也可以单独使用,取决于你的需求。一个 WeakRef 对象包含一个对于某个对象的弱引用,被称为目标引用。通过弱引用一个对象,可以让该对象在没有其它引用的情况下被垃圾回收机制回收。WeakRef 主要用来缓存和映射一些大型对象,当你希望某个对象在不被其它地方引用的情况下及时地被垃圾回收,那么你就可以使用它。

    function toogle(element) {
      const weakElement = new WeakRef(element);
      let intervalId = null;
    
      function toggle() {
        const el = weakElement.deref();
        if (!el) {
          return clearInterval(intervalId);
        }
        const decoration = weakElement.style.textDecoration;
        const style = decoration === "none" ? "underline" : "none";
        decoration = style;
      }
      intervalId = setInterval(toggle, 1000);
    }
    const element = document.getElementById("link");
    toogle(element);
    setTimeout(() => element.remove(), 10000);
    

    FinalizationRegistry 接收一个注册器回调函数,可以利用该注册器为指定对象注册一个事件监听器,当这个对象被垃圾回收之后,会触发监听的事件,具体步骤如下。首先,创建一个注册器:

    const registry = new FinalizationRegistry((heldValue) => {
      // ....
    });
    

    接着注册一个指定对象,同时也可以给注册器回调传递一些参数:

    registry.register(theObject, "some value");
    

    逻辑赋值运算符

    详细信息参考ts39-proposal-logical-assignment

    逻辑赋值运算符结合了逻辑运算符和赋值表达式。逻辑赋值运算符有两种:

    • 或等于(||=
    • 且等于(&&=
    • ??=

    ||=

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

    &&=

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

    ??= 空赋值运算符

    ??= 也被称为空赋值运算符,与上面的非空运算符相关。看看它们之间的联系:

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

    仅当值为 nullundefined 时,此赋值运算符才会赋值。上面的例子强调了这个运算符本质上是空赋值的语法糖(类似的语法糖:a = a + b 可写成 a += b )。接下来,让我们看看这个运算符与默认参数(默认参数是 ES6 引入的新语法,仅当函数参数为 undefined 时,给它设置一个默认值)的区别:

    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: null, gameDiff: null}
    

    上述函数处理空值的方式有一个值得注意的区别。默认参数将用空参数(这里的空参数,只能是 undefined)覆盖默认值,空赋值运算符将不会。默认参数和空赋值都不会覆盖未定义的值。MDN 官方文档

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

    数字分隔符

    通过这个功能,我们利用 \_,U+005F 分隔符来将数字分组,提高数字的可读性:

    1_000_000_000; // 十亿
    101_475_938.38; // 亿万
    
    const amount = 12345_00; // 12,345
    const amount = 123_4500; // 123.45 (保留 4 位小数)
    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;
    

    ES2020-ES11

    ES2020 是与 2020 年相对应的 ECMAScript 版本

    String.protype.matchAll

    matchAll()方法返回一个正则表达式在当前字符串的所有匹配

    不过,它返回的是一个遍历器(Iterator),而不是数组。遍历器转为数组是非常简单的,使用...运算符和 Array.from()方法就可以了。

    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);
    

    详细内容参考ES 入门-matchAll

    Dynamic import

    import(specifier)函数,支持动态加载模块, import 函数的参数 specifier,指定所要加载的模块的位置。import 命令能够接受什么参数,import()函数就能接受什么参数,两者区别主要是后者为动态加载。

    import()返回一个 Promise 对象

    const someVariable = "user";
    
    import(`./some-modules/${someVariable}.js`)
      .then((module) => {
        // 业务逻辑
        module.loadPageInto(main);
      })
      .catch((err) => {
        // 加载失败
      });
    

    详细内容参考ES 入门-import

    Promise.allSettled

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

    有时候,我们不关心异步请求的结果,只关心所有的请求有没有结束。这时,Promise.allSettled()方法就很有用

    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);
    

    globalThis

    ES2020 之前获取不同环境的this需要如下封装

    const getGlobalThis = () => {
      // 在 webworker 或 service worker 中
      if (typeof self !== "undefined") return self;
    
      // 在浏览器中
      if (typeof window !== "undefined") return window;
    
      // 在 Node.js 中
      if (typeof global !== "undefined") return global;
    
      // 独立的 JavaScript shell
      if (typeof this !== "undefined") return this;
    
      throw new Error("Unable to locate global object");
    };
    const theGlobalThis = getGlobalThis();
    
    if (typeof theGlobalThis.setTimeout !== "function") {
      // 此环境中没有 setTimeout 方法!
    }
    

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

    if (typeof globalThis.setTimeout !== "function") {
      // 此环境中没有 setTimeout 方法!
    }
    

    详细内容参考MDN-globalThis

    空位合并操作符(Nullish coalescing Operator)

    在 JS 中,?? 运算符被称为非空运算符。如果第一个参数不是 null/undefined(这里只有两个假值,但是 JS 中假值包含:未定义 undefined、空对象 null、数值 0、空数字 NaN、布尔 false,空字符串'',不要搞混了),将返回第一个参数,否则返回第二个参数。比如,

    null ?? 5; // => 5
    3 ?? 5; // => 3
    

    给变量设置默认值时,以前常用 ||逻辑或运算符,例如,

    const prevMoney = 1;
    const currMoney = 0;
    const noAccount = null;
    const futureMoney = -1;
    function moneyAmount(money) {
      return money || `账户未开通`;
    }
    console.log(moneyAmount(prevMoney)); // => 1
    console.log(moneyAmount(currMoney)); // => 账户未开通
    console.log(moneyAmount(noAccount)); // => 账户未开通
    console.log(moneyAmount(futureMoney)); // => -1
    

    上面我们创建了函数 moneyAmount,它返回当前用户余额。我们使用 || 运算符来识别没有帐户的用户。然而,当用户没有帐户时,这意味着什么?将无账户视为空而不是 0 更为准确,因为银行账户可能没有(或负)货币。在上面的例子中,|| 运算符将 0 视为一个虚假值,不应该包括用户有 0 美元的帐户。让我们使用?? 非空运算符来解决这个问题:

    const currMoney = 0;
    const noAccount = null;
    function moneyAmount(money) {
      return money ?? `账户未开通`;
    }
    moneyAmount(currMoney); // => 0
    moneyAmount(noAccount); // => `账户未开通`
    

    概括地说 ?? 运算符允许我们在忽略错误值(如 0 和空字符串)的同时指定默认值。

    可选链操作符(Optional Chaining)

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

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

    现在,把我们刚刚学到的结合起来

    function addPlansWhenUndefined(plans, location, budget) {
      if (plans.tuesday?.location == undefined) {
        var newPlans = {
          plans,
          tuesday: {
            location: location ?? "公园",
            budget: budget ?? 200,
          },
        };
      } else {
        newPlans ??= plans; // 只有 newPlans 是 undefined 时,才覆盖
        console.log("已安排计划");
      }
      return newPlans;
    }
    // 对象 travelPlans 的初始值,来自上面一个例子
    var newPlans = addPlansWhenUndefined(travelPlans, "Ford 剧院", null);
    console.log(newPlans);
    // => { plans:
    // { destination: 'DC',
    // monday: { location: '国家购物中心', budget: 200 } },
    // tuesday: { location: 'Ford 剧院', budget: 200 } }
    newPlans = addPlansWhenUndefined(newPlans, null, null);
    // logs => 已安排计划
    // returns => newPlans object
    

    上面的例子包含了我们到目前为止所学的所有运算符。现在我们已经创建了一个函数,该函数将计划添加到当前没有嵌套属性的对象 tuesday.location 中。我们还使用了非空运算符来提供默认值。此函数将错误地接受像“0”这样的值作为有效参数。这意味着 budget 可以设置为零,没有任何错误。

    BigInt primitive type

    旧版本的 JS 标准最大的整数只能是253 - 1, 现在使用BigInt 用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。 这是 ECMAScript 的又一种数据类型。

    可以用在一个整数字面量后面加 n 的方式定义一个 BigInt ,如:10n,或者调用函数 BigInt()。

    const theBiggestInt = 9007199254740991n;
    
    const alsoHuge = BigInt(9007199254740991);
    // ↪ 9007199254740991n
    
    • ES 入门-BigInt

    ES2019-ES10

    Array#{flat,flatMap}

    数组的成员有时还是数组,Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。

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

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

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

    详细内容参考ES 入门-flat

    Object.fromEntries

    Object.fromEntries()方法是Object.entries()的逆操作,用于将一个键值对数组转为对象。

    Object.fromEntries([
      ["foo", "bar"],
      ["baz", 42],
    ]);
    // { foo: "bar", baz: 42 }
    

    该方法的主要目的,是将键值对的数据结构还原为对象,因此特别适合将 Map 结构转为对象。

    // 例一
    const entries = new Map([
      ["foo", "bar"],
      ["baz", 42],
    ]);
    
    Object.fromEntries(entries);
    // { foo: "bar", baz: 42 }
    
    // 例二
    const map = new Map().set("foo", true).set("bar", false);
    Object.fromEntries(map);
    // { foo: true, bar: false }
    

    String#{trimStart,trimEnd}

    ES2019 对字符串实例新增了trimStart()trimEnd()这两个方法。它们的行为与trim()一致,trimStart()消除字符串头部的空格,trimEnd()消除尾部的空格。它们返回的都是新字符串,不会修改原始字符串。

    const s = "  abc  ";
    
    s.trim(); // "abc"
    s.trimStart(); // "abc  "
    s.trimEnd(); // "  abc"
    

    Symbol#description

    ES2019 提供了一个实例属性description,直接返回 Symbol 的描述。

    // 创建 Symbol 的时候,可以添加一个描述。
    const sym = Symbol("foo");
    
    sym.description; // "foo"
    

    上面代码中,sym 的描述就是字符串 foo

    try { } catch {} // optional binding

    旧版本的try / catch语句中的catch子句需要一个变量。 现在可以不加了

    // 旧版本
    try {
      console.log(a);
    } catch (error) {
      console.log("报错了");
    }
    
    // ES2019-SE10
    try {
      console.log(a);
    } catch {
      console.log("报错了");
    }
    

    U+2028 和 U+2029

    在 ES2019 之前的版本中,不接受不转义的

    • 行分隔符U + 2028
    • 段落分隔符U + 2029

    ES2019 允许 JavaScript 字符串直接输入 U+2028(行分隔符)和 U+2029(段分隔符)。

    /*
    ES2019之前,下面的代码会报错
    
    ES2019 下面代码不会报错。
    */
    const PS = eval("'\u2029'");
    

    ES 入门-U+2028 和 U+2029

    JSON-stringify-的改造

    为了确保返回的是合法的 UTF-8 字符,ES2019 改变了 JSON.stringify()的行为。如果遇到 0xD8000xDFFF 之间的单个码点,或者不存在的配对形式,它会返回转义字符串,留给应用自己决定下一步的处理。

    JSON.stringify("\u{D834}"); // ""\\uD834""
    JSON.stringify("\uDF06\uD834"); // ""\\udf06\\ud834""
    

    ES 入门-JSON-stringify-的改造

    Array.prototype.sort() 的稳定排序

    早先的 ECMAScript 没有规定,Array.prototype.sort()的默认排序算法是否稳定,留给浏览器自己决定,这导致某些实现是不稳定的。ES2019 明确规定,Array.prototype.sort()的默认排序算法必须稳定。这个规定已经做到了,现在 JavaScript 各个主要实现的默认排序算法都是稳定的。

    const arr = ["peach", "straw", "apple", "spork"];
    
    const stableSorting = (s1, s2) => {
      if (s1[0] < s2[0]) return -1;
      return 1;
    };
    
    arr.sort(stableSorting);
    // ["apple", "peach", "straw", "spork"]
    

    ES 入门-排序稳定性

    revised Function#toString

    ES2019 对函数实例的 toString()方法做出了修改。

    toString()方法返回函数代码本身,以前会省略注释和空格。

    function /* foo comment */ foo() {}
    
    // 老版本
    foo.toString();
    // function foo() {}
    
    // 新版
    foo.toString();
    // "function /* foo comment */ foo () {}"
    

    ES2018-ES9

    解除模板字面量限制(Lifting template literal restriction).

    ES2018 放松了对标签模板里面的字符串转义的限制。如果遇到不合法的字符串转义,就返回undefined,而不是报错,并且从raw属性上面可以得到原始字符串。

    function tag(strs) {
      strs[0] === undefined
      strs.raw[0] === "\\unicode and \\u{55}";
    }
    tag`\unicode and \u{55}`
    

    上面代码中,模板字符串原本是应该报错的,但是由于放松了对字符串转义的限制,所以不报错了,JavaScript 引擎将第一个字符设置为undefined,但是raw属性依然可以得到原始字符串,因此tag函数还是可以对原字符串进行处理。

    • ES 入门-模板字符串的限制
    • ES 入门-row
    • ES 入门-修饰符:u

    正则之 s 修饰符:dotAll 模式-(s (dotAll) flag for regular expressions).

    ES2018 引入 s 修饰符,使得.可以匹配任意单个字符。

    /foo.bar/s.test("foo\nbar"); // true
    

    这被称为dotAll模式,即点(dot)代表一切字符。所以,正则表达式还引入了一个dotAll属性,返回一个布尔值,表示该正则表达式是否处在dotAll模式。

    ES 入门-修饰符:dotAll 模式

    正则之具名组匹配(RegExp named capture groups)

    ES2018 引入了具名组匹配(Named Capture Groups),允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。

    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"
    

    ES 入门-修饰符:具名组匹配

    Rest/Spread Properties.

    ES6 为数组引入了扩展运算符的写法,

    在 ES2018 中,为对象也引入了此写法

    const obj = { a: "a", b: "b", c: "c", d: "d", e: "e" };
    
    // 对象结构
    const { a, b, c, ...rest } = obj;
    
    // 组成新对象
    const newObj = { a, ...rest };
    

    正则之后行断言(RegExp Lookbehind Assertions.)

    ES2018 引入后行断言

    “后行断言”指: x只有不在y后面才匹配,必须写成/(?<!y)x/。比如,只匹配不在美元符号后面的数字,要写成/(?<!\$)\d+/

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

    使用后行断言进行字符串替换。

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

    ES 入门-后行断言

    Unicode 属性类(RegExp Unicode Property Escapes)

    ES2018 引入了一种新的类的写法\p{...}\P{...},允许正则表达式匹配符合 Unicode 某种属性的所有字符。

    const regexGreekSymbol = /\p{Script=Greek}/u;
    regexGreekSymbol.test("π"); // true
    
    // 匹配所有空格
    const reg = /\p{White_Space}/;
    
    // 匹配所有的箭头字符
    const regexArrows = /^\p{Block=Arrows}+$/u;
    regexArrows.test("←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩"); // true
    

    ES 入门-Unicode 属性类

    Promise.prototype.finally.

    finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

    promise
    .then(result => {···})
    .catch(error => {···})
    .finally(() => {···});
    

    上面代码中,不管 promise 最后的状态,在执行完thencatch指定的回调函数以后,都会执行finally方法指定的回调函数。

    ES 入门-finally

    按顺序完成异步操作(Asynchronous Iteration)

    实际开发中,经常遇到一组异步操作,需要按照顺序完成。比如,依次远程读取一组 URL,然后按照读取的顺序输出结果。

    async function logInOrder(urls) {
      // 并发读取远程URL
      const textPromises = urls.map(async (url) => {
        const response = await fetch(url);
        return response.text();
      });
    
      // 按次序输出
      for (const textPromise of textPromises) {
        console.log(await textPromise);
      }
    }
    
    async function getData() {
      const promises = [fetch("url1"), fetch("url2"), fetch("url3"), fetch("url4")];
      for (const item of promises) {
        // 打印出promise
        console.log(item);
      }
    
      for await (const item of promises) {
        // 打印出请求的结果
        console.log(item);
      }
    }
    

    ES 入门-顺序异步操作


    ES2017-ES8

    Object.values/Object.entries

    Object.values 方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。

    const obj = { foo: "bar", baz: 42 };
    Object.values(obj);
    // ["bar", 42]
    
    const obj = { 100: "a", 2: "b", 7: "c" };
    Object.values(obj);
    // ["b", "c", "a"]
    

    Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。

    const obj = { foo: "bar", baz: 42 };
    Object.entries(obj);
    // [ ["foo", "bar"], ["baz", 42] ]
    

    Object.entries 的基本用途是遍历对象的属性。

    let obj = { one: 1, two: 2 };
    for (let [k, v] of Object.entries(obj)) {
      console.log(`${JSON.stringify(k)}: ${JSON.stringify(v)}`);
    }
    // "one": 1
    // "two": 2
    

    Object.entries 方法的另一个用处是,将对象转为真正的 Map 结构。

    const obj = { foo: "bar", baz: 42 };
    const map = new Map(Object.entries(obj));
    map; // Map { foo: "bar", baz: 42 }
    

    String padding

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

    "x".padStart(5, "ab"); // 'ababx'
    "x".padStart(4, "ab"); // 'abax'
    
    "x".padEnd(5, "ab"); // 'xabab'
    "x".padEnd(4, "ab"); // 'xaba'
    

    padStart()的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。

    "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.getOwnPropertyDescriptors

    ES2017 引入了 Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象

    • value — 属性实际的值
    • writable — 属性的值是否可以被修改
    • get — 获取函数,在读取属性时调用
    • set — 设置函数,在写入属性时调用
    • configurable — 属性是否可以通过 delete 删除并重新定义,是否可以修改它的特 性,以及是否可以把它改为访问器属性
    • enumerable — 属性是否可以通过 for-in 循环返回
    const obj = {
      foo: 123,
      get bar() {
        return "abc";
      },
    };
    
    Object.getOwnPropertyDescriptors(obj);
    // { foo:
    //    { value: 123,
    //      writable: true,
    //      enumerable: true,
    //      configurable: true },
    //   bar:
    //    { get: [Function: get bar],
    //      set: undefined,
    //      enumerable: true,
    //      configurable: true } }
    

    该方法的引入目的,主要是为了解决 Object.assign()无法正确拷贝 get 属性和 set 属性的问题。

    Object.getOwnPropertyDescriptors()方法的另一个用处,是配合 Object.create()方法,将对象属性克隆到一个新对象。这属于浅拷贝。

    const shallowClone = (obj) =>
      Object.create(
        Object.getPrototypeOf(obj),
        Object.getOwnPropertyDescriptors(obj),
      );
    

    更多详细内容参考ES 入门教程-getOwnPropertyDescriptors

    函数参数的尾逗号

    ES2017 允许函数的最后一个参数有尾逗号(trailing comma)。

    此前,函数定义和调用时,都不允许最后一个参数后面出现逗号。

    function clownsEverywhere(param1, param2,) {
      /* ... */
    }
    
    clownsEverywhere("foo", "bar",);
    

    更多详细内容参考ES 入门教程-函数参数的尾逗号

    异步函数(Async functions)

    ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

    async 函数是什么?一句话,它就是 Generator 函数的语法糖。

    function fakeRequest() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve("请求成功");
        }, 2000);
      });
    }
    
    async function getData() {
      console.log("start");
      const res = await fakeRequest();
      console.log(res);
      console.log("end");
    }
    getData();
    /*
    1.start
    2.请求成功
    3.end
    */
    

    使用 Atomics 共享内存

    Atomics 对象提供了一组静态方法对 SharedArrayBufferArrayBuffer 对象进行原子操作。

    更多详细内容参考MDN-Atomics


    ES2016-ES7

    Array.prototype.includes

    Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。

    [1, 2, 3]
      .includes(2) // true
      [(1, 2, 3)].includes(4) // false
      [(1, 2, NaN)].includes(NaN); // true
    

    求幂运算符(Exponentiation operator)

    // 2的平方
    2 ** 2; // 4
    // 2的三次方
    2 ** 3; // 8
    

    更多详细内容参考ES 入门教程-指数运算符


    ES2015-ES6

    推荐阮一峰大佬的ES 入门教程,中文文档没有比他更详细的了

    箭头函数(arrows)

    箭头函数是使用=>语法的函数简写。与一般函数不同的是

    1. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
    • this 对象的指向是可变的,但是在箭头函数中,它是固定的。
    1. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
    2. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
    3. 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
    var f = (v) => v;
    
    // 等同于
    var f = function (v) {
      return v;
    };
    
    function foo() {
      setTimeout(() => {
        console.log("id:", this.id);
      }, 100);
    }
    
    var id = 21;
    // 箭头函数导致this总是指向函数定义生效时所在的对象({id: 42}),所以打印出来的是42
    foo.call({ id: 42 });
    // id: 42
    
    // 对象不构成单独的作用域,使得this指向全局对象
    globalThis.s = 21;
    const obj = {
      s: 42,
      m: () => console.log(this.s),
    };
    
    obj.m(); // 21
    

    更多详细内容参考ES 入门教程-箭头函数

    类(Class)

    // ES5
    function Point(x, y) {
      this.x = x;
      this.y = y;
    }
    
    Point.prototype.toString = function () {
      return "(" + this.x + ", " + this.y + ")";
    };
    
    var p = new Point(1, 2);
    
    // ES6
    class Point {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
    
      toString() {
        return "(" + this.x + ", " + this.y + ")";
      }
    }
    

    更多详细内容参考ES 入门教程-Class

    对象的扩展(enhanced object literals)

    对象的属性的简洁表示法

    const foo = "bar";
    const method = function () {
      return "Hello!";
    };
    
    const filed = "name";
    
    const baz = {
      foo,
      method,
      [filed]: "小王",
    };
    
    // 等同于
    const baz = {
      foo: foo,
      method: function () {
        return "Hello!";
      },
      name: "小王",
    };
    

    更多详细内容参考ES 入门教程-对象扩展

    模板字符串

    // 字符串中嵌入变量
    let name = "Bob",
      time = "today";
    `Hello ${name}, how are you ${time}?`;
    

    更多详细内容参考ES 入门教程-字符串模板

    数组解构+扩展运算符

    var [a] = [];
    
    a === undefined; // true
    
    var [a = 1] = [];
    a === 1; // true
    

    更多详细内容参考ES 入门教程-数组的扩展运算符

    函数默认参数+剩余参数+扩展运算符

    //如果没有传递y 或者y===undefined ,则y=12
    function f(x, y = 12) {
      return x + y;
    }
    f(3) == 15;
    
    function f(x, ...y) {
      // y 是一个数组
      return x * y.length;
    }
    f(3, "hello", true) == 6;
    
    function f(x, y, z) {
      return x + y + z;
    }
    // Pass each elem of array as argument
    f(...[1, 2, 3]) == 6;
    

    更多详细内容参考ES 入门教程-函数默认参数

    块级作用域变量

    随着 ES6 中引入 let/const 关键字,JS 才具有函数作用域和全局作用域,现在 JS 也可以有块级作用域了。

    function f() {
      {
        let x;
        {
          // 正常,因为在一个新的块级作用域中
          const x = "sneaky";
          // const 定义的是常量无法被修改,因此会报错
          x = "foo";
        }
        // 在块级作用域中已声明x,因此会报错
        let x = "inner";
      }
    }
    

    更多详细内容参考ES 入门教程-unicode

    遍历/迭代器+for..of(iterators + for..of)

    一个数据结构只要部署了 Symbol.iterator 属性,就被视为具有 iterator 接口,就可以用 for...of 循环遍历它的成员。也就是说,for...of 循环内部调用的是数据结构的 Symbol.iterator 方法。

    for ... offor ... inforEach()的替代方法,它循环访问可迭代的数据结构,如数组,映射,集合和字符串。

    JavaScript 原有的 for...in 循环,只能获得对象的键名,不能直接获取键值。ES6 提供 for...of 循环,允许遍历获得键值。

    var arr = ["a", "b", "c", "d"];
    
    for (let a in arr) {
      console.log(a); // 0 1 2 3
    }
    
    for (let a of arr) {
      console.log(a); // a b c d
    }
    
    const str = "helloworld";
    for (let a of str) {
      console.log(a); // h e l l o w o r l d
    }
    

    更多详细内容参考ES 入门教程-iterators

    生成器(generators)

    Generators 使用function *yield简化了迭代器的创建。 声明为function *的函数一个遍历器对象,也就是说,Generator 函数是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

    生成器是迭代器的子类型,因此具有nextthrow方法。

    yield表达式是暂停执行的标记,而next方法可以恢复执行

    注意:ES7 出现后,推荐使用await

    function* foo() {
      yield 1;
      yield 2;
      yield 3;
      yield 4;
      yield 5;
      return 6;
    }
    
    for (let v of foo()) {
      console.log(v);
    }
    // 1 2 3 4 5
    

    下面是一个利用 Generator 函数和for...of循环,实现斐波那契数列的例子。

    var fibonacci = {
      [Symbol.iterator]: function* () {
        let [prev, curr] = [0, 1];
        for (;;) {
          yield curr;
          [prev, curr] = [curr, prev + curr];
        }
      },
    };
    
    for (var n of fibonacci) {
      //
      if (n > 1000) break;
      console.log(n);
    }
    

    从上面代码可见,使用for...of语句时不需要使用next方法。

    利用for...of循环,可以写出遍历任意对象(object)的方法。原生的 JavaScript 对象没有迭代器接口,无法使用for...of循环,通过 Generator 函数为它加上这个接口,就可以用了。

    生成器(Generator) 实质上继承了迭代器(Iterator)

    interface Generator extends Iterator {
      next(value?: any): IteratorResult;
      throw(exception: any);
    }
    

    更多详细内容参考ES 入门教程-iterators

    Unicode

    ES6 增强了 Unicode 的功能,包括

    • 支持字符的 Unicode 表示法

    举例来说,“中”的 Unicode 码点是 U+4e2d,你可以直接在字符串里面输入这个汉字,也可以输入它的转义形式\u4e2d,两者是等价的。

    "中" === "\u4e2d"; // true
    
    • 使用/u匹配码点的正则表达式
    // new RegExp behaviour, opt-in ‘u’
    "?".match(/./u)[0].length == 2;
    
    • 获取 32 位的 UTF-16 字符的码点-codePointAt
    "?".codePointAt(0) == 0x20bb7;
    
    let s = "?a";
    for (let ch of s) {
      console.log(ch.codePointAt(0).toString(16));
    }
    // 20bb7
    // 61
    

    更多详细内容参考ES 入门教程-unicode

    模块化(modules)

    ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

    使用 export defaultexport 进行导出

    // math.js
    export const pi = 3.141593;
    
    export default function sum(x, y) {
      return x + y;
    }
    

    使用 import 进行导入

    // app.js
    import sum, { pi } from "./math";
    
    alert("2π = " + sum(pi, pi));
    

    更多详细内容参考ES 入门教程-module

    模块加载器规则(module loaders)

    模块加载器支持:

    • 异步加载
    • 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。
    • 模块之中,顶层的 this 关键字返回 undefined,而不是指向 window。也就是说,在模块顶层使用 this 关键字,是无意义的
    //index.js
    const x = 1;
    
    console.log(x === window.x); //false
    console.log(this === undefined); // true
    

    利用顶层的 this 等于 undefined 这个语法点,可以侦测当前代码是否在 ES6 模块之中。

    const isNotModuleScript = this !== undefined;
    

    更多详细内容参考ES 入门教程-module-loader

    import and export

    Map + Set + Weakmap + Weakset

    ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

    // Sets
    var s = new Set();
    s.add("hello").add("goodbye").add("hello");
    s.size === 2;
    s.has("hello") === true;
    

    ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。

    // Maps
    var m = new Map();
    m.set("hello", 42);
    m.set(s, 34);
    m.get(s) == 34;
    

    WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合。

    WeakMapMap 的区别有两点。

    1. WeakMap 只接受对象作为键名(null 除外),不接受其他类型的值作为键名。
    2. WeakMap 的键名所指向的对象,不计入垃圾回收机制。
    // Weak Maps
    var wm = new WeakMap();
    wm.set(s, { extra: 42 });
    wm.size === undefined;
    

    WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

    1. WeakSet 的成员只能是对象,而不能是其他类型的值。
    2. WeakSet 中的对象都是弱引用
    // Weak Sets
    var ws = new WeakSet();
    ws.add({ data: 42 });
    // Because the added object has no other references, it will not be held in the set
    

    更多详细内容参考ES 入门教程-Set 和 Map

    代理(proxies)

    Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改。 可以用于操作拦截,日志记录/分析等。

    // 代理一个普通对象
    var target = {};
    var handler = {
      get: function (receiver, name) {
        return `Hello, ${name}!`;
      },
    };
    
    var p = new Proxy(target, handler);
    
    // true
    p.world === "Hello, world!";
    

    下面是 Proxy 所有可以代理的"元操作"

    var handler =
    {
      get:...,
      set:...,
      has:...,
      deleteProperty:...,
      apply:...,
      construct:...,
      getOwnPropertyDescriptor:...,
      defineProperty:...,
      getPrototypeOf:...,
      setPrototypeOf:...,
      enumerate:...,
      ownKeys:...,
      preventExtensions:...,
      isExtensible:...
    }
    

    MDN-handler.get()

    // 代理一个函数对象
    var target = function () {
      return "I am the target";
    };
    var handler = {
      apply: function (receiver, ...args) {
        return "I am the proxy";
      },
    };
    
    var p = new Proxy(target, handler);
    //true
    p() === "I am the proxy";
    

    更多详细内容参考ES 入门教程-proxy

    symbols

    ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值

    Symbol 值通过 Symbol 函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

    var MyClass = (function () {
      //
      var key = Symbol("key");
    
      function MyClass(privateData) {
        this[key] = privateData;
      }
    
      MyClass.prototype = {
        doStuff: function () {
          this[key];
        },
      };
    
      return MyClass;
    })();
    
    var c = new MyClass("hello");
    // true
    console.log(c["key"] === undefined);
    

    创建 Symbol 的时候,可以添加一个描述。

    const sym = Symbol("foo");
    

    上面代码中,sym 的描述就是字符串 foo

    Symbol 作为属性名,遍历对象的时候,该属性不会出现在 for...infor...of 循环中,也不会被 Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。

    但是,它也不是私有属性,有一个 Object.getOwnPropertySymbols()方法,可以获取指定对象的所有 Symbol 属性名。该方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。

    const obj = {};
    let a = Symbol("a");
    let b = Symbol("b");
    
    obj[a] = "Hello";
    obj[b] = "World";
    
    const objectSymbols = Object.getOwnPropertySymbols(obj);
    
    objectSymbols;
    // [Symbol(a), Symbol(b)]
    

    更多详细内容参考ES 入门教程-symbol

    期约(promises)

    Promise 是一个用于异步编程的库,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。 许多现有的 JavaScript 库已经使用了 Promise

    function timeout(duration = 0) {
      return new Promise((resolve, reject) => {
        setTimeout(resolve, duration);
      });
    }
    
    var p = timeout(1000)
      .then(() => {
        return timeout(2000);
      })
      .then(() => {
        throw new Error("hmm");
      })
      .catch((err) => {
        return Promise.all([timeout(100), timeout(200)]);
      });
    

    更多详细内容参考ES 入门教程-promise

    math + number + string + array + object APIs

    添加了许多类型的扩展方法,包括:Math ,Array ,String ,Object

    Number.EPSILON;
    Number.isInteger(Infinity); // false
    Number.isNaN("NaN"); // false
    
    Math.acosh(3); // 1.762747174039086
    Math.hypot(3, 4); // 5
    Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2); // 2
    
    "abcde".includes("cd"); // true
    "abc".repeat(3); // "abcabcabc"
    
    Array.from(document.querySelectorAll("*")); // Returns a real Array
    Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
      [(0, 0, 0)].fill(7, 1) // [0,7,7]
      [(1, 2, 3)].find((x) => x == 3) // 3
      [(1, 2, 3)].findIndex((x) => x == 2) // 1
      [(1, 2, 3, 4, 5)].copyWithin(3, 0) // [1, 2, 3, 1, 2]
      [("a", "b", "c")].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
      [("a", "b", "c")].keys() // iterator 0, 1, 2
      [("a", "b", "c")].values(); // iterator "a", "b", "c"
    
    Object.assign(Point, { origin: new Point(0, 0) });
    

    更多详细内容参考 ES 入门教程:

    • Number
    • Math,
    • Array.from
    • Array.of
    • Array.prototype.copyWithin
    • Object.assign

    二进制和八进制(binary and octal literals)

    两种新的数字表示形式。

    • 二进制: 0b 开头
    • 八进制: 0o 开头
    0b111110111 === 503; // true
    0o767 === 503; // true
    

    reflect api

    reflect API 公开对象上的运行时级别的元操作

    最重要的目的是配合 Proxy 使用,执行原生行为

    Object操作都变成函数行为。某些Object操作是命令式,比如name in objdelete obj[name],而Reflect.has(obj, name)Reflect.deleteProperty(obj, name)让它们变成了函数行为。

    // 老写法
    "assign" in Object; // true
    
    // 新写法
    Reflect.has(Object, "assign"); // true
    

    更多详细内容参考ES 入门教程-reflect

    尾调用(tail calls)

    • 尾调用:某个函数的最后一步是返回并调用另一个函数
    • 尾递归:函数调用自身,称为递归。如果尾调用自身,就称为尾递归。
    • 尾调用优化

    注意,目前只有 Safari 浏览器支持尾调用优化,Chrome 和 Firefox 都不支持。这里就不深入研究了 ?

    function factorial(n, acc = 1) {
      if (n <= 1) return acc;
      return factorial(n - 1, n * acc);
    }
    
    // 大多数浏览器中都会出现 堆栈溢出 的错误,
    // 但是在 ES6的Safari中是安全的
    factorial(100000);
    

    更多详细内容参考ES 入门教程-尾调用

    通过 Intl API 对字符串,数字和日期进行国际化

    Intl 对象是 ECMAScript 国际化 API 的命名空间,它提供对语言敏感的字符串比较、支持数字格式化以及日期和时间的格式化。

    Intl.Collator 对象

    collator 这个单词意思是排序器。Intl.Collator 对象是排序器的构造函数,可以支持对语言敏感的字符串比较。

    • 中文排序

    如果我们希望我们的中文按照首字母拼音排序,该怎么处理?

    此时,可以使用中文简体的 BCF 47 语言标记字符串 zh 进行排序,代码如下:

    var arrUsername = [
      "陈坤",
      "邓超",
      "杜淳",
      "冯绍峰",
      "韩庚",
      "胡歌",
      "黄晓明",
      "贾乃亮",
      "李晨",
      "李易峰",
      "鹿晗",
      "井柏然",
      "刘烨",
      "陆毅",
      "孙红雷",
    ];
    
    arrUsername.sort(new Intl.Collator("zh").compare);
    // 结果是:["陈坤", "邓超", "杜淳", "冯绍峰", "韩庚", "胡歌", "黄晓明", "贾乃亮", "井柏然", "李晨", "李易峰", "刘烨", "陆毅", "鹿晗", "孙红雷"]
    

    Intl API详细可以参考这篇文章JS Intl 对象完整简介及在中文中的应用


    ES2011-ES5

    相信大家已经对 ES5 都了然于胸,因此只做简单罗列,就不举例说明了

    'USE STRICT'

    JS 的早期版本允许使用未声明的变量。 但是当使用 es5“严格使用”功能时,会报告错误

    // index.js
    "use strict";
    
    // 报错:a is not defined
    a = 22;
    

    Array

    Array.isArray

    Array.forEach

    Array.map

    Array.filter

    Array.reduce

    Array.reduceRight

    Array.every

    Array.some

    Array.indexOf

    Array.lastIndexOf

    JSON

    JSON.parse

    JSON.stringify

    DATE

    Date.now()

    Date.now().valueOf()

    Object.defineProperty()


    参考文档

    1. ECMAScript 6 Features
    2. es6-features.org
    3. ES2021 Features with simple examples
    4. 4 个强大 JavaScript 运算符
    5. ES6 核心特性

    起源地下载网 » 你有一份 ECMAScript 特性速查表,请查收

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元