ES7-ES12特性

ES2016(ES7)特性

指数运算符

// ** 运算符
console.log(2 ** 3);   // 8
console.log(3 ** 2);   // 9
console.log(10 ** -1); // 0.1

// 右结合
console.log(2 ** 3 ** 2);  // 512 (= 2 ** (3 ** 2))

// 赋值运算符
let a = 2;
a **= 3;
console.log(a);  // 8

Array.prototype.includes

// includes方法
const arr = [1, 2, 3, NaN];

console.log(arr.includes(2));    // true
console.log(arr.includes(4));    // false
console.log(arr.includes(NaN));  // true

// 与indexOf对比
console.log(arr.indexOf(NaN));   // -1(indexOf不能正确处理NaN)

// 从指定位置开始
console.log([1, 2, 3].includes(1, 1));  // false
console.log([1, 2, 3].includes(1, 0));  // true

ES2017(ES8)特性

async/await

// async函数
async function fetchData() {
    const response = await fetch("/api/data");
    const data = await response.json();
    return data;
}

// 错误处理
async function safeFetch() {
    try {
        const data = await fetchData();
        return data;
    } catch (error) {
        console.error(error);
    }
}

Object.entries/values

const obj = { a: 1, b: 2, c: 3 };

// Object.entries
console.log(Object.entries(obj));
// [["a", 1], ["b", 2], ["c", 3]]

// Object.values
console.log(Object.values(obj));
// [1, 2, 3]

// 遍历对象
for (const [key, value] of Object.entries(obj)) {
    console.log(key, value);
}

// 转换回对象
const newObj = Object.fromEntries(Object.entries(obj));

字符串填充

// padStart
console.log("5".padStart(3, "0"));   // "005"
console.log("abc".padStart(6, "x")); // "xxxabc"

// padEnd
console.log("5".padEnd(3, "0"));     // "500"
console.log("abc".padEnd(6, "x"));   // "abcxxx"

// 实际应用
const date = new Date();
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
console.log(`${year}-${month}-${day}`);

Object.getOwnPropertyDescriptors

const obj = {
    name: "东巴文",
    get fullName() {
        return this.name;
    }
};

const descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors.name);
// {
//   value: "东巴文",
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

// 克隆对象(包括getter/setter)
const clone = Object.create(
    Object.getPrototypeOf(obj),
    Object.getOwnPropertyDescriptors(obj)
);

函数参数尾逗号

// 允许尾逗号
function foo(
    a,
    b,
    c,
) {
    return a + b + c;
}

const arr = [
    1,
    2,
    3,
];

const obj = {
    a: 1,
    b: 2,
};

ES2018(ES9)特性

对象Rest/Spread

// Rest属性
const { a, ...rest } = { a: 1, b: 2, c: 3 };
console.log(a, rest);  // 1 { b: 2, c: 3 }

// Spread属性
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, ...obj1 };
console.log(obj2);  // { c: 3, a: 1, b: 2 }

// 合并对象
const merged = { ...obj1, ...{ b: 3, d: 4 } };
console.log(merged);  // { a: 1, b: 3, d: 4 }

Promise.finally

// finally方法
fetch("/api/data")
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error(error))
    .finally(() => {
        console.log("请求完成");
        hideLoading();
    });

Array.prototype.flat

// flat扁平化
const arr = [1, [2, [3, [4]]]];

console.log(arr.flat());    // [1, 2, [3, [4]]]
console.log(arr.flat(2));   // [1, 2, 3, [4]]
console.log(arr.flat(Infinity));  // [1, 2, 3, 4]

// flatMap
const arr2 = [1, 2, 3];
const result = arr2.flatMap(x => [x, x * 2]);
console.log(result);  // [1, 2, 2, 4, 3, 6]

Object.fromEntries

// 从键值对创建对象
const entries = [["a", 1], ["b", 2]];
const obj = Object.fromEntries(entries);
console.log(obj);  // { a: 1, b: 2 }

// Map转对象
const map = new Map([["a", 1], ["b", 2]]);
const objFromMap = Object.fromEntries(map);
console.log(objFromMap);  // { a: 1, b: 2 }

// 过滤对象
const original = { a: 1, b: 2, c: 3 };
const filtered = Object.fromEntries(
    Object.entries(original).filter(([key, value]) => value > 1)
);
console.log(filtered);  // { b: 2, c: 3 }

正则表达式增强

// 命名捕获组
const regex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = regex.exec("2024-01-15");
console.log(match.groups.year);   // "2024"
console.log(match.groups.month);  // "01"
console.log(match.groups.day);    // "15"

// 后行断言
const str = "东巴文 db-w.cn";
console.log(str.match(/(?<=东巴文 )\S+/));  // ["db-w.cn"]

// 先行断言
console.log(str.match(/\S+(?= db-w.cn)/));  // ["东巴文"]

// dotAll模式
const regex2 = /foo.bar/s;
console.log(regex2.test("foo\nbar"));  // true

ES2019(ES9)特性

Array.prototype.flat/flatMap

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

// flatMap
const sentences = ["Hello World", "东巴文"];
const words = sentences.flatMap(s => s.split(" "));
console.log(words);  // ["Hello", "World", "东巴文"]

Object.fromEntries

// 已在上面介绍

String.prototype.trimStart/trimEnd

const str = "  hello world  ";

console.log(str.trimStart());  // "hello world  "
console.log(str.trimEnd());    // "  hello world"
console.log(str.trim());       // "hello world"

可选的catch绑定

// 旧写法
try {
    doSomething();
} catch (error) {
    console.error(error);
}

// 新写法(不需要error参数)
try {
    doSomething();
} catch {
    console.error("出错了");
}

Symbol.prototype.description

const sym = Symbol("东巴文");
console.log(sym.description);  // "东巴文"

const sym2 = Symbol();
console.log(sym2.description);  // undefined

ES2020(ES11)特性

可选链操作符

const user = {
    name: "东巴文",
    address: {
        city: "北京"
    }
};

// 可选链
console.log(user?.name);           // "东巴文"
console.log(user?.address?.city);  // "北京"
console.log(user?.phone?.number);  // undefined(不报错)

// 函数调用
const obj = {
    method: () => "Hello"
};
console.log(obj.method?.());       // "Hello"
console.log(obj.other?.());        // undefined

// 数组访问
const arr = [1, 2, 3];
console.log(arr?.[0]);             // 1
console.log(arr?.[10]);            // undefined

空值合并操作符

// ?? 只在null/undefined时使用默认值
const a = null ?? "default";      // "default"
const b = undefined ?? "default"; // "default"
const c = "" ?? "default";        // ""
const d = 0 ?? "default";         // 0
const e = false ?? "default";     // false

// 与||对比
const x = 0 || "default";   // "default"
const y = 0 ?? "default";   // 0

// 结合使用
const value = user?.name ?? "匿名";

BigInt

// BigInt类型
const big = 9007199254740993n;
console.log(big);  // 9007199254740993n

// BigInt函数
const big2 = BigInt(9007199254740993);
console.log(big2);  // 9007199254740993n

// 运算
const a = 10n;
const b = 5n;
console.log(a + b);   // 15n
console.log(a * b);   // 50n
console.log(a / b);   // 2n
console.log(a % b);   // 0n

// 不能与Number混合运算
// console.log(10n + 5);  // TypeError

// 比较
console.log(10n > 5);   // true
console.log(10n === 10);  // false
console.log(10n == 10);   // true

globalThis

// 统一的全局对象访问
console.log(globalThis);

// 浏览器: window
// Node.js: global
// Worker: self
// 统一使用 globalThis

String.prototype.matchAll

const str = "test1test2test3";
const regex = /test(\d)/g;

const matches = [...str.matchAll(regex)];
console.log(matches);
// [
//   ["test1", "1"],
//   ["test2", "2"],
//   ["test3", "3"]
// ]

for (const match of str.matchAll(regex)) {
    console.log(match[1]);
}

Promise.allSettled

const promises = [
    Promise.resolve(1),
    Promise.reject("error"),
    Promise.resolve(3)
];

Promise.allSettled(promises)
    .then(results => {
        console.log(results);
        // [
        //   { status: "fulfilled", value: 1 },
        //   { status: "rejected", reason: "error" },
        //   { status: "fulfilled", value: 3 }
        // ]
    });

ES2021(ES12)特性

逻辑赋值运算符

let a = null;
a ??= "default";  // a = a ?? "default"
console.log(a);   // "default"

let b = false;
b ||= true;       // b = b || true
console.log(b);   // true

let c = { x: 1 };
c &&= c.x;        // c = c && c.x
console.log(c);   // 1

数字分隔符

// 使用下划线分隔数字
const billion = 1_000_000_000;
const bytes = 0xFF_FF_FF_FF;
const bits = 0b1010_0001_1000_0101;
const hex = 0xA0_B0_C0;

console.log(billion);  // 1000000000

String.prototype.replaceAll

const str = "东巴文 东巴文 东巴文";

// replace只替换第一个
console.log(str.replace("东巴文", "db-w.cn"));
// "db-w.cn 东巴文 东巴文"

// replaceAll替换所有
console.log(str.replaceAll("东巴文", "db-w.cn"));
// "db-w.cn db-w.cn db-w.cn"

// 使用正则
console.log(str.replaceAll(/东巴文/g, "db-w.cn"));

Promise.any

const promises = [
    Promise.reject("error1"),
    Promise.resolve("success"),
    Promise.reject("error2")
];

Promise.any(promises)
    .then(value => console.log(value))  // "success"
    .catch(error => console.error(error));

// 全部失败
Promise.any([
    Promise.reject("error1"),
    Promise.reject("error2")
])
.catch(error => {
    console.log(error instanceof AggregateError);  // true
    console.log(error.errors);  // ["error1", "error2"]
});

WeakRef

// WeakRef弱引用
let obj = { name: "东巴文" };
const weakRef = new WeakRef(obj);

console.log(weakRef.deref());  // { name: "东巴文" }

obj = null;  // 可以被垃圾回收

// FinalizationRegistry
const registry = new FinalizationRegistry((value) => {
    console.log("对象被回收:", value);
});

let target = { data: "test" };
registry.register(target, "target标识");

target = null;  // 可能触发回调

ES2022(ES13)特性

类的私有字段

class Person {
    #name;  // 私有字段
    
    constructor(name) {
        this.#name = name;
    }
    
    get name() {
        return this.#name;
    }
    
    #privateMethod() {
        return "私有方法";
    }
}

const person = new Person("东巴文");
console.log(person.name);        // "东巴文"
// console.log(person.#name);    // SyntaxError
// console.log(person.#privateMethod());  // SyntaxError

类的静态块

class Config {
    static settings;
    
    static {
        this.settings = {
            apiUrl: "https://api.db-w.cn",
            timeout: 5000
        };
    }
}

console.log(Config.settings);

Array.prototype.at

const arr = [1, 2, 3, 4, 5];

console.log(arr.at(0));   // 1
console.log(arr.at(-1));  // 5(最后一个元素)
console.log(arr.at(-2));  // 4

// 等价于
console.log(arr[arr.length - 1]);  // 5

Object.hasOwn

const obj = { a: 1 };

console.log(Object.hasOwn(obj, "a"));      // true
console.log(Object.hasOwn(obj, "b"));      // false
console.log(Object.hasOwn(obj, "toString"));  // false

// 比 hasOwnProperty 更安全
const obj2 = Object.create(null);
obj2.a = 1;
// obj2.hasOwnProperty("a");  // TypeError
console.log(Object.hasOwn(obj2, "a"));  // true

ES2023(ES14)特性

数组从后查找

const arr = [1, 2, 3, 4, 3, 2, 1];

// findLast
console.log(arr.findLast(x => x > 2));     // 3
console.log(arr.findLastIndex(x => x > 2)); // 4

// toSorted/toReversed/toSpliced(不修改原数组)
const sorted = arr.toSorted();
console.log(sorted);  // [1, 1, 2, 2, 3, 3, 4]
console.log(arr);     // 原数组不变

const reversed = arr.toReversed();
console.log(reversed);  // [1, 2, 3, 4, 3, 2, 1]

const spliced = arr.toSpliced(2, 2, "a", "b");
console.log(spliced);  // [1, 2, "a", "b", 3, 2, 1]

// with(替换指定索引)
const replaced = arr.with(2, "新值");
console.log(replaced);  // [1, 2, "新值", 4, 3, 2, 1]

下一步

掌握了ES7-ES12特性后,让我们继续学习:

  1. 迭代器与生成器 - 学习迭代协议
  2. Proxy与Reflect - 学习元编程
  3. Map与Set - 学习集合类型

东巴文(db-w.cn) - 让编程学习更简单

🎯 东巴文寄语:JavaScript每年都会发布新特性,可选链、空值合并、BigInt等特性已经广泛使用。保持学习新特性是前端开发者的必备技能。在 db-w.cn,我们帮你紧跟JavaScript发展!