ES6 核心特性 [JavaScript]

Marimo_z
2025-04-19 / 0 评论 / 5 阅读 / 正在检测是否收录...

1. 块级作用域:letconst

  • let:声明块级作用域的变量,替代 var
  • const:声明常量(不可重新赋值)。

    let x = 10;
    if (true) {
      let x = 20; // 独立的块级作用域
      const PI = 3.14; // 常量
    }
    console.log(x); // 10

2. 箭头函数(Arrow Functions)

  • 简化函数语法。

    // 传统函数
    function getSum(a, b) { return a + b; }
    
    // 箭头函数
    const getSum1 = a => a + 3;
    
    const getSum2 = (a, b) => a + b;
    
    const getSum3 = (a, b, ...other) => console.log(a, b, other);
    
    const getResult = arr => {
      let sun = 0;
      arr.forEach(item => sum += item);
      return sun;
    };
  • 自动绑定外部 this(无自己的 this)。

    箭头函数没有自己的 this 绑定,它会继承外层作用域的 this。箭头函数的 this 在定义时确定,且不会因调用方式改变。

    // 普通函数(错误行为)
    const obj = {
      value: 42,
      getValue: function() {
        setTimeout(function() {
          console.log(this.value); // 输出 undefined(非严格模式)或报错(严格模式)
        }, 100);
      }
    };
    
    // 箭头函数
    const obj = {
      value: 42,
      getValue: function() {
        setTimeout(() => {
          console.log(this.value); // 42(箭头函数继承外层 this)
        }, 100);
      }
    };

3. 模板字符串(Template Literals)

  • 使用反引号 ` 包裹字符串,支持多行文本和变量插值(${expression})。

    const name = "Alice";
    const message = `
      Hello, ${name}!
      Today is ${new Date().toLocaleDateString()}.
    `;

4. 解构赋值(Destructuring Assignment)

  • 从数组或对象中提取值并赋值给变量。

    // 数组解构
    const [a, b, ...rest] = [1, 2, 3, 4];
    console.log(a); // 1
    console.log(rest); // [3, 4]
    
    // 对象解构
    const { name, age } = { name: "Bob", age: 25 };
    console.log(name); // "Bob"
    
    // 函数参数解构
    function getUser({ id, name }) {
      console.log(id, name);
    }

5. 默认参数(Default Parameters)

  • 为函数参数设置默认值。

    function greet(name = "Guest") {
      console.log(`Hello, ${name}!`);
    }
    greet(); // "Hello, Guest!"

6. 展开与剩余操作符(Spread & Rest)

  • 展开操作符(...:展开数组或对象。

    const arr1 = [1, 2];
    const arr2 = [...arr1, 3, 4]; // [1, 2, 3, 4]
    
    const obj1 = { a: 1, b: 2 };
    const obj2 = { ...obj1, c: 3 }; // { a:1, b:2, c:3 }
  • 剩余参数(...:收集剩余参数为数组。

    function sum(...numbers) {
      return numbers.reduce((acc, num) => acc + num, 0);
    }
    sum(1, 2, 3); // 6

7. 类(Class)

  • 语法糖,基于原型的面向对象编程更清晰。

    class Animal {
      #age; // 私有字段(ES2022特性)
    
      // 构造函数(实例化时调用)
      constructor(name, legs = 4, age = 1) {
        this.name = name;        // 实例属性
        this.legs = legs;        // 支持默认参数
        this.#age = age;         // 私有属性
      }
    
      // 实例方法(定义在原型上)
      speak() {
        console.log(`${this.name}发出声音。`);
      }
    
      // Getter
      get ageInfo() {
        return `${this.name}今年${this.#age}岁`;
      }
    
      // Setter
      set ageInfo(value) {
        if (value < 0) throw new Error('年龄不能为负');
        this.#age = value;
      }
    
      // 静态方法(直接通过类调用,不继承给实例)
      static createBaby(name) {
        return new Animal(name, 4, 0);
      }
    }
    
    // 通过 extends 实现继承,子类构造函数中必须调用 super()
    class Dog extends Animal {
      constructor(name, breed = '中华田园犬') {
        super(name);             // 调用父类构造函数
        this.breed = breed;      // 子类特有属性
      }
    
      // 方法重写
      speak() {
        super.speak();           // 调用父类方法
        console.log(`${this.name}(${this.breed})汪汪汪!`);
      }
    
      // 子类特有方法
      fetch(item) {
        console.log(`${this.name}叼回了${item}`);
      }
    }
    
    // 使用示例
    const puppy = Animal.createBaby('小奶狗'); // 静态方法调用
    console.log(puppy.ageInfo);  // "小奶狗今年0岁"
    
    const dog = new Dog('旺财', '金毛'); // 实例化
    dog.ageInfo = 2;             // 通过setter修改年龄
    dog.speak();                 // "旺财发出声音。" + "旺财(金毛)汪汪汪!"
    dog.fetch('飞盘');           // "旺财叼回了飞盘"
    console.log(dog.ageInfo);    // "旺财今年2岁"
    
    // 继承验证
    console.log(dog instanceof Animal);  // true
    console.log(dog instanceof Dog);     // true

8. 模块化(Modules)

  • 使用 exportimport 实现模块化,支持命名导出、默认导出和混合导出。
  • 可通过 as 重命名,用 * 导入整个模块的命名空间。

    // math.js
    // 命名导出:显式导出变量、函数或类。
    export const add = (a, b) => a + b;
    export const PI = 3.14;
    // 默认导出:一个模块只能有一个默认导出。
    export default function multiply(a, b) { return a * b; }
    
    // app.js
    import mult, { add as sum, PI } from './math.js'; // 默认导出需放在首位
    import * as MathUtil from './math.js';
    
    console.log(sum(2, 3)); // 5
    console.log(mult(2, 3)); // 6
    console.log(MathUtil.PI); // 3.14

9. Promise 和异步编程

  • 在 ES6 之前,JavaScript 使用 回调函数(Callback) 处理异步操作(如网络请求、定时器等)。但多层嵌套回调会导致 回调地狱(Callback Hell),代码难以阅读和维护:

    getData(function(a) {
      getMoreData(a, function(b) {
        getMoreData(b, function(c) {
          // 更多嵌套...
        });
      });
    });

    Promise 的出现解决了这一问题,提供了更清晰的链式调用和错误处理。

  • Promise 的基本概念:

    • Promise 是一个对象,表示一个异步操作的最终完成(或失败)及其结果值。
    • 三种状态

      • Pending(进行中):初始状态。
      • Fulfilled(已成功):操作成功完成。
      • Rejected(已失败):操作失败。
    • 状态不可逆:一旦状态从 Pending 变为 Fulfilled 或 Rejected,就不会再改变。
  • 使用示例:处理异步操作,避免回调地狱。

    1. 基础用法 + 链式调用 + 错误处理

      // 定义一个模拟异步获取数据的函数(返回 Promise)
      const fetchData = () => {
        return new Promise((resolve, reject) => {
          // 模拟异步操作(如网络请求)
          setTimeout(() => {
            const success = Math.random() > 0.3; // 70% 概率成功
            if (success) {
              resolve("Data received!"); // 成功时调用 resolve
            } else {
              reject("Network Error!");  // 失败时调用 reject
            }
          }, 1000);
        });
      };
      
      // 定义一个处理数据的函数(返回处理后的 Promise)
      const processData = (rawData) => {
        return new Promise((resolve) => {
          setTimeout(() => {
            resolve(`${rawData} => Processed`); // 模拟数据处理
          }, 500);
        });
      };
      
      // 执行异步操作链
      fetchData()
        .then((rawData) => {
          console.log("1. 原始数据:", rawData);
          return processData(rawData); // 返回新 Promise 以继续链式调用
        })
        .then((processedData) => {
          console.log("2. 处理后的数据:", processedData);
          return "Final Result"; // 返回普通值会自动包装为 Promise
        })
        .then((finalResult) => {
          console.log("3. 最终结果:", finalResult);
        })
        .catch((error) => {
          // 捕获链中任意位置的错误
          console.error("捕获到错误:", error);
        })
        .finally(() => {
          // 无论成功失败都会执行
          console.log("4. 请求流程结束");
        });
    2. 并行处理多个 Promise

      const fetchUser = () => {
        return new Promise((resolve) => {
          setTimeout(() => resolve({ id: 1, name: "Alice" }), 800);
        });
      };
      
      const fetchOrders = (userId) => {
        return new Promise((resolve) => {
          setTimeout(() => resolve([`Order1`, `Order2`]), 600);
        });
      };
      
      // 同时发起多个独立请求
      Promise.all([fetchUser(), fetchOrders(1)])
        .then(([user, orders]) => {
          console.log("\n并行请求结果:");
          console.log("用户:", user);
          console.log("订单:", orders);
        });
    3. 使用 async/await 语法糖

      async function fetchDataWithRetry(retryCount = 3) {
        for (let i = 0; i < retryCount; i++) {
          try {
            const data = await fetchData();
            const processed = await processData(data);
            console.log(`\n第 ${i + 1} 次尝试成功:`, processed);
            return processed; // 成功时提前返回
          } catch (error) {
            console.warn(`第 ${i + 1} 次尝试失败`);
            if (i === retryCount - 1) throw error; // 最后一次失败抛出错误
          }
        }
      }
      
      // 执行带重试机制的请求
      fetchDataWithRetry()
        .then(result => console.log("最终成功结果:", result))
        .catch(error => console.error("全部重试失败:", error));
    4. 复杂错误处理场景

      const validateData = (data) => {
        return new Promise((resolve, reject) => {
          if (data.includes("received")) {
            resolve("Validation passed");
          } else {
            reject(new Error("Invalid data format"));
          }
        });
      };
      
      fetchData()
        .then(data => {
          console.log("\n原始数据:", data);
          return validateData(data); // 可能在此处抛出错误
        })
        .then(validationResult => {
          console.log("验证结果:", validationResult);
        })
        .catch(error => {
          if (error.message === "Invalid data format") {
            console.error("数据格式错误:", error);
            return "Default Data"; // 提供默认值继续链式调用
          }
          throw error; // 其他错误继续抛出
        })
        .then(finalData => {
          console.log("最终使用的数据:", finalData);
        });
    5. 分层错误处理

      • 每个 .then 的第二个参数专门处理前一步的特定错误
      • 通过 return 替代值 实现错误恢复
      • 通过 throw new Error() 实现错误传播
      // 策略1:步骤专属处理(适合可恢复错误)
      .then(
        data => { /* 正常逻辑 */ },
        error => { 
          console.log("当前步骤错误处理");
          return recoveryValue; // 返回替代值继续流程
        }
      )
      
      // 策略2:中断流程(适合关键错误)
      .then(
        data => { /* 正常逻辑 */ },
        error => { 
          console.log("关键步骤失败,终止流程");
          throw error; // 重新抛出,由后续 catch 处理
        }
      )
      
      // 策略3:错误转换(统一错误类型)
      .then(
        data => { /* 正常逻辑 */ },
        error => { 
          throw new CustomError(error.message); 
        }
      )
      层级典型方法作用域最佳实践
      生成层throw new Error()原子函数抛具体类型错误,附加调试信息
      中间处理层.then() 二参 / .catch单个Promise局部恢复/转换/中断
      全局兜底层链尾 .catch()整个链最终日志/用户提示
      进程兜底unhandledrejection进程级防止静默崩溃,应急恢复

10. Symbol 类型

  • 唯一且不可变的值,用于对象属性的唯一标识。
  • 通过 Symbol.for() 创建全局共享的 Symbol,使用 description 获取描述。
  • 直接 Symbol() 每次都会创建全新的 Symbol(sym1 ≠ sym2)。
  • 相同 key 的 Symbol.for() 调用会返回同一个 Symbol 。

    const sym1 = Symbol("key");
    const sym2 = Symbol("key");
    const sym3 = Symbol.for("sharedKey");
    const sym4 = Symbol.for("sharedKey");
    console.log(sym1 === sym2); // false
    console.log(sym3 === sym4); // true
    console.log(sym1.description); // "key"
    
    // 内置 Symbol 应用
    // Symbol.iterator:定义对象的默认迭代器。
    // Symbol.toStringTag:定制对象的 toString() 行为。
    const obj = {
      [sym1]: "value"
    };
    console.log(obj[sym1]); // "value"
    
    const iterableObj = {
      [Symbol.iterator]: function* () { yield 1; yield 2; }
    };
    console.log([...iterableObj]); // [1, 2]

11. 迭代器和生成器(Iterators & Generators)

  • 迭代器

    • 对象需实现 Symbol.iterator 方法,返回一个包含 next() 方法的对象。
    • next() 返回 { value: any, done: boolean }
    const iterable = {
      [Symbol.iterator]() {
        let step = 0;
        return {
          next() {
            return { value: step++, done: step > 3 };
          }
        };
      }
    };
    for (const val of iterable) console.log(val); // 0, 1, 2
  • 生成器

    • function* 声明,通过 yield 暂停执行并返回值。
    • 生成器函数返回迭代器对象。
    function* idGenerator() {
      let id = 1;
      while (true) yield id++; // 无限ID序列
    }
    const gen = idGenerator();
    console.log(gen.next().value); // 1
  • 应用场景

    • 惰性求值:按需生成数据(如分页加载)。
    • 简化异步代码:与 async/await 结合使用。

12. Map 和 Set

  • Map:键值对集合,键可以是任意类型。

    const map = new Map();
    map.set("name", "Alice");
    map.set(1, "One");
    console.log(map.get("name")); // "Alice"
  • Set:唯一值的集合,自动去重。

    const set = new Set([1, 2, 2, 3]);
    console.log([...set]); // [1, 2, 3]

13. Proxy 和 Reflect

  • Proxy:创建对象的代理,拦截并自定义操作(如属性读取、设置)。

    const target = {};
    const handler = {
      get: (obj, prop) => {
        console.log(`Accessing ${prop}`);
        return obj[prop] || "Not found";
      }
    };
    const proxy = new Proxy(target, handler);
    console.log(proxy.name); // "Accessing name" → "Not found"
  • Reflect:提供操作对象的静态方法。

    Reflect.set(target, "age", 30);
    console.log(Reflect.get(target, "age")); // 30

14. 新的数据类型和结构

ArrayBufferTypedArray

  • 用途:处理二进制数据(如文件、图像、网络协议等)。
  • ArrayBuffer
    表示一段固定长度的原始二进制数据缓冲区,不能直接操作,需通过视图(TypedArrayDataView)。

    const buffer = new ArrayBuffer(16); // 创建一个16字节的缓冲区
  • TypedArray
    提供不同类型的视图来操作 ArrayBuffer,如:

    • Int8Array(8位有符号整数)
    • Uint8Array(8位无符号整数,常用于处理像素数据)
    • Float32Array(32位浮点数,适用于WebGL)
    const int32View = new Int32Array(buffer); // 用32位整数视图操作buffer
    int32View[0] = 42; // 写入数据
  • 应用场景

    • 文件读写、网络数据传输(如WebSocket二进制通信)。
    • WebGL图形处理、音视频编解码。

WeakMapWeakSet

  • 核心特性:键是弱引用(不阻止垃圾回收),避免内存泄漏。
  • WeakMap

    • 键必须是对象,值可以是任意类型。
    • 无法遍历、没有 size 属性。
    • 用途:存储对象的私有数据或元数据。
    const wm = new WeakMap();
    const obj = {};
    wm.set(obj, "私有数据");
    obj = null; // 当obj被回收时,wm中的键值对自动清除
  • WeakSet

    • 只能存储对象,无法遍历。
    • 用途:跟踪对象是否存在(如避免重复操作)。
    const ws = new WeakSet();
    ws.add(obj); // 添加对象
    console.log(ws.has(obj)); // true

15. 其他实用特性

字符串方法

  • includes()
    判断字符串是否包含子串:

    "hello".includes("ell"); // true
  • startsWith() / endsWith()
    判断字符串是否以特定子串开头/结尾:

    const url = "https://example.com";
    url.startsWith("https"); // true
    url.endsWith(".com");    // true

数值方法

  • Number.isNaN()
    更精准的 NaN 检测(全局 isNaN("abc") 返回 true,而此方法返回 false)。

    Number.isNaN(NaN);      // true
    Number.isNaN("NaN");    // false(全局isNaN("NaN")会返回true)
  • Number.isInteger()
    判断是否为整数:

    Number.isInteger(3.0);  // true
    Number.isInteger(3.1);  // false

数组方法

  • Array.from()
    将类数组对象(如 argumentsNodeList)转为数组:

    Array.from(document.querySelectorAll("div")); // [div, div, ...]
    function fn (){
        Array.from(arguments).forEach(function (item) {
            console.log(item) // 1 2 3
        })
    }
    
    fn(1, 2, 3)
  • Array.of()
    解决 new Array(3) 的歧义问题,创建明确元素的数组:

    Array.of(3);        // [3]
    new Array(3);       // [空属性 ×3]
  • 实例方法

    • find() / findIndex():查找符合条件的元素或索引。

      [1, 2, 3].find(x => x > 1); // 2
    • includes():判断数组是否包含某值(替代 indexOf)。

对象方法

  • Object.assign()
    合并对象(浅拷贝):

    const target = { a: 1 };
    Object.assign(target, { b: 2 }, { c: 3 }); // { a:1, b:2, c:3 }
  • Object.entries()
    返回对象的键值对数组(ES2017特性):

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

总结

ES6 的引入彻底改变了 JavaScript 的编程方式,核心改进包括:

  • 更清晰的语法(箭头函数、类、模板字符串)。
  • 更强的功能(模块化、Promise、解构赋值)。
  • 更好的性能与安全性(块级作用域、let/const)。
2

评论 (0)

取消