ES6 新特性全面总结

ES6 新特性全面总结

ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多强大的新特性,极大地提升了JavaScript的开发体验和能力。以下是ES6主要新增知识点的详细总结:

(一)、ES6变量声明:let 和 const 详解

一、let 和 const 的基本概念

ES6引入了两种新的变量声明方式:letconst,它们与传统的var声明有显著区别。

1. let 声明

let用于声明块级作用域的变量:

javascript 复制代码
let x = 10;
if (true) {
  let x = 20; // 不同的变量
  console.log(x); // 20
}
console.log(x); // 10
2. const 声明

const用于声明常量,声明后不能重新赋值:

javascript 复制代码
const PI = 3.1415;
// PI = 3; // TypeError: Assignment to constant variable

二、与 var 的关键区别

特性 var let const
作用域 函数作用域或全局作用域 块级作用域 块级作用域
变量提升 是(但存在TDZ) 是(但存在TDZ)
重复声明 允许 不允许 不允许
初始值 可不初始化 可不初始化 必须初始化
重新赋值 允许 允许 不允许

三、块级作用域详解

1. 什么是块级作用域

块级作用域是指由{}包围的代码块形成的作用域:

javascript 复制代码
{
  let a = 1;
  var b = 2;
}
console.log(a); // ReferenceError: a is not defined
console.log(b); // 2
2. 常见块级作用域场景

• if语句

• for循环

• while循环

• switch语句

• 单独的{}

四、暂时性死区(TDZ)

1. 概念

在声明前访问letconst变量会触发暂时性死区错误:

javascript 复制代码
console.log(a); // undefined
var a = 1;

console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 2;
2. 原理

虽然letconst也会提升,但在声明前处于"暂时性死区",访问会报错。

五、const 的特殊说明

1. 必须初始化
javascript 复制代码
const a; // SyntaxError: Missing initializer in const declaration
2. 对象和数组的特殊性

const只保证变量名绑定的内存地址不变,不保证内部数据不变:

javascript 复制代码
const obj = {a: 1};
obj.a = 2; // 允许
// obj = {}; // 不允许

const arr = [1, 2];
arr.push(3); // 允许
// arr = []; // 不允许

六、最佳实践建议

  1. 默认使用const :除非需要重新赋值,否则优先使用const
  2. 需要重新赋值时用let :当变量需要改变时使用let
  3. 避免使用var :除非有特殊需求,否则不使用var
  4. 声明位置:尽量在作用域顶部声明变量
  5. 命名规范const常量可以使用全大写命名(如MAX_SIZE

七、常见使用场景

1. for循环中的let
javascript 复制代码
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 0, 1, 2
}
2. 块级作用域变量
javascript 复制代码
function processData(data) {
  {
    let temp = transformData(data);
    // 处理temp...
  }
  // temp在这里不可访问
}
3. 模块中的常量
javascript 复制代码
// config.js
export const API_URL = 'https://api.example.com';
export const MAX_RETRIES = 3;

八、常见问题解答

Q1: 什么时候用let,什么时候用const?

A: 优先使用const,只有确定变量需要重新赋值时才使用let。

Q2: const声明的对象属性可以修改吗?

A: 可以修改对象属性,但不能重新赋值整个对象。

Q3: let和const能替代var吗?

A: 在大多数情况下可以完全替代,但要注意作用域差异。

Q4: 为什么会有暂时性死区?

A: 这是为了更早发现编程错误,避免变量提升带来的混淆。

Q5: 全局作用域下let和var有什么区别?

A: 全局作用域下,var声明的变量会成为window对象的属性,而let不会。

(二)、ES6箭头函数(Arrow Functions)全面解析

一、基本语法

箭头函数是ES6引入的一种更简洁的函数写法,使用=>符号定义。

1. 基础语法形式
javascript 复制代码
// 传统函数写法
function sum(a, b) {
  return a + b;
}

// 箭头函数写法
const sum = (a, b) => a + b;
2. 不同形式的箭头函数
情况 示例 等价传统函数
单个参数 x => x * 2 function(x) { return x * 2; }
多个参数 (x, y) => x + y function(x, y) { return x + y; }
无参数 () => 42 function() { return 42; }
多行函数体 (a, b) => {<br> const c = a + b;<br> return c * 2;<br>} function(a, b) {<br> const c = a + b;<br> return c * 2;<br>}
返回对象 () => ({ a: 1 }) function() { return { a: 1 }; }

二、箭头函数的特性

1. 没有自己的this

箭头函数没有自己的this,它会捕获所在上下文的this值。

javascript 复制代码
const obj = {
  name: 'Alice', // 定义一个对象属性 name,值为 'Alice'
  sayName: function() {
    console.log(this.name); // 输出当前对象的 name 属性值,即 'Alice'

    // 使用箭头函数作为 setTimeout 的回调函数
    setTimeout(() => {
      console.log(this.name); // 输出当前对象的 name 属性值,即 'Alice'
      // 箭头函数不会创建自己的 this 上下文,而是捕获外层函数的 this
      // 因此这里的 this 指向 obj 对象
    }, 100);
  },
  sayNameError: function() {
    // 使用普通函数作为 setTimeout 的回调函数
    setTimeout(function() {
      console.log(this.name); // 输出 undefined
      // 普通函数有自己的 this 上下文,默认指向全局对象(浏览器中是 window)
      // 因此这里的 this 不指向 obj 对象,而是 window 对象
      // 由于 window 对象中没有 name 属性,所以输出 undefined
    }, 100);
  }
};
2. 没有arguments对象

箭头函数没有自己的arguments对象,但可以访问外围函数的arguments

javascript 复制代码
function outer(a, b) {
  const inner = () => {
    // arguments 是一个特殊的类数组对象,它包含了函数调用时传入的所有参数。
    console.log(arguments); // 访问outer的arguments
  };
  inner();
}
outer(1, 2); // [1, 2]
关键点
  • arguments 的作用域
    • 在普通函数中,arguments 是一个类数组对象,包含了函数调用时传入的所有参数。
    • 在箭头函数中,arguments 不会被自动绑定,而是继承自外层函数的 arguments
  • 箭头函数的特性
    • 箭头函数不会创建自己的 thisarguments,而是继承自外层函数的上下文。
3. 不能作为构造函数

箭头函数不能使用new调用,没有prototype属性。

javascript 复制代码
const Foo = () => {};
// new Foo(); // TypeError: Foo is not a constructor
4. 没有super和new.target

箭头函数没有supernew.target绑定。

三、箭头函数的适用场景

1. 回调函数
javascript 复制代码
// 数组方法
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);

// 事件处理
button.addEventListener('click', () => {
  console.log('Button clicked');
});
2. 需要保持this上下文的场景
javascript 复制代码
class Counter {
  constructor() {
    this.count = 0;
    // 使用箭头函数保持this指向
    this.increment = () => {
      this.count++;
    };
  }
}
3. 简洁的单行函数
javascript 复制代码
const isEven = n => n % 2 === 0;
const greet = name => `Hello, ${name}!`;

四、箭头函数的不适用场景

1. 对象方法
javascript 复制代码
const obj = {
  value: 0,
  // 错误:箭头函数不会绑定this到obj
  increment: () => {
    this.value++; // this指向window/undefined
  }
};
2. 需要动态this的场景
javascript 复制代码
// 错误:无法通过call/apply/bind改变this
const greet = () => console.log(this.name);
const alice = { name: 'Alice' };
greet.call(alice); // 无效
3. 需要arguments对象的函数
javascript 复制代码
// 错误:无法访问自己的arguments
const sum = () => {
  console.log(arguments); // 引用外层arguments或报错
};

五、箭头函数与普通函数的对比

特性 箭头函数 普通函数
this绑定 词法作用域 动态绑定
arguments
构造函数 不能
prototype
yield 不能用作生成器 可以
简洁性
适用场景 回调、需要固定this 方法、构造函数

六、常见问题解答

Q1: 什么时候应该使用箭头函数?

A: 适合需要保持this一致性的回调函数、简单的单行函数、不需要arguments的场景。

Q2: 箭头函数能替代所有普通函数吗?

A: 不能。对象方法、构造函数、需要动态thisarguments的场景仍需使用普通函数。

Q3: 箭头函数有prototype属性吗?

A: 没有,这也是它不能用作构造函数的原因之一。

Q4: 如何给箭头函数添加默认参数?

A: 和普通函数一样,直接在参数中指定:

javascript 复制代码
const greet = (name = 'Guest') => `Hello, ${name}!`;

Q5: 箭头函数可以有name属性吗?

A: 可以,当箭头函数被赋值给变量时,会使用变量名作为函数名:

javascript 复制代码
const foo = () => {};
console.log(foo.name); // "foo"

七、高级用法

1. 立即执行箭头函数(IIFE)
javascript 复制代码
((name) => {
  console.log(`Hello, ${name}!`);
})('Alice');
2. 链式调用
javascript 复制代码
const operations = {
  value: 1,
  add: (n) => {
    operations.value += n;
    return operations;
  },
  multiply: (n) => {
    operations.value *= n;
    return operations;
  }
};

operations.add(2).multiply(3).add(1); // value = 10
3. 配合解构使用
javascript 复制代码
const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
];

const names = users.map(({ name }) => name); // ['Alice', 'Bob']

箭头函数是ES6中最受欢迎的特性之一,正确理解和使用它可以使代码更简洁、更可读,同时避免许多this绑定的陷阱。

(三)、ES6模板字符串(Template Literals)深度解析

一、基本概念与语法

模板字符串是ES6引入的一种新型字符串表示法,使用反引号(`````)包裹内容,相比传统字符串具有更强大的功能。

1. 基础语法对比
javascript 复制代码
// 传统字符串
const name = 'Alice';
const greeting = 'Hello, ' + name + '!';

// 模板字符串
const greeting = `Hello, ${name}!`;
2. 核心特性

多行字符串 :直接支持换行

字符串插值 :使用${expression}嵌入变量和表达式

标签模板:可以自定义字符串处理函数

二、多行字符串处理

1. 传统方式的痛点
javascript 复制代码
// ES5实现多行字符串
var message = '第一行\n' +
              '第二行\n' +
              '第三行';
2. 模板字符串解决方案
javascript 复制代码
const message = `第一行
第二行
第三行`;
3. 实际应用场景
javascript 复制代码
// HTML模板
const html = `
<div class="container">
  <h1>${title}</h1>
  <p>${content}</p>
</div>
`;

// SQL查询
const query = `
SELECT * FROM users
WHERE id = ${userId}
ORDER BY name DESC
`;

三、字符串插值详解

1. 基本插值
javascript 复制代码
const name = 'Alice';
const age = 25;
console.log(`Name: ${name}, Age: ${age}`); // "Name: Alice, Age: 25"
2. 表达式计算
javascript 复制代码
const a = 10;
const b = 20;
console.log(`Sum: ${a + b}`); // "Sum: 30"
3. 函数调用
javascript 复制代码
function getAge() {
  return 25;
}
console.log(`Age: ${getAge()}`); // "Age: 25"
4. 嵌套模板
javascript 复制代码
const isMember = true;
console.log(`Status: ${
  isMember ? `Member since ${2020}` : 'Not a member'
}`); // "Status: Member since 2020"

四、标签模板(Tagged Templates)

1. 基本概念

标签模板允许使用函数解析模板字符串,第一个参数是字符串数组,后续参数是插值表达式。

javascript 复制代码
function tag(strings, ...values) {
  console.log(strings); // ["Hello ", "!"]
  console.log(values); // ["Alice"]
  return 'Processed string';
}

const name = 'Alice';
const result = tag`Hello ${name}!`;
// 当调用 tag 函数时:
// strings 参数接收模板字符串中的文本部分。
// values 参数接收模板字符串中的嵌入值。
2. 实际应用案例
a) 安全HTML转义
javascript 复制代码
// 定义一个标签函数 safeHtml,用于处理模板字符串并防止 XSS 攻击
function safeHtml(strings, ...values) {
  let result = ''; // 初始化一个空字符串,用于存储最终生成的 HTML 内容

  // 遍历 strings 数组,它包含了模板字符串中的所有文本部分
  for (let i = 0; i < strings.length; i++) {
    result += strings[i]; // 将当前的文本部分添加到 result 中

    // 检查是否存在对应的嵌入值
    if (i < values.length) {
      // 将嵌入值转换为字符串,并进行 HTML 转义
      result += String(values[i])
        .replace(/&/g, '&amp;')    // 将 & 替换为 &amp;
        .replace(/</g, '&lt;')     // 将 < 替换为 &lt;
        .replace(/>/g, '&gt;')     // 将 > 替换为 &gt;
        .replace(/"/g, '&quot;')   // 将 " 替换为 &quot;
        .replace(/'/g, '&#39;');   // 将 ' 替换为 &#39;
    }
  }

  return result; // 返回最终生成的 HTML 内容
}

// 定义一个可能包含恶意脚本的用户输入
const userInput = '<script>alert("XSS")</script>';

// 使用 safeHtml 标签函数处理模板字符串
const safeOutput = safeHtml`<div>${userInput}</div>`;

// 输出结果
console.log(safeOutput); // <div>&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;</div>
b) 国际化处理
javascript 复制代码
function i18n(strings, ...values) {
  const translations = {
    'Hello': '你好',
    'Welcome': '欢迎'
  };
  let translated = '';
  strings.forEach((str, i) => {
    translated += translations[str.trim()] || str;
    if (values[i]) translated += values[i];
  });
  return translated;
}

const name = 'Alice';
console.log(i18n`Hello ${name}!`); // "你好 Alice!"
c) SQL查询构建
javascript 复制代码
function sqlQuery(strings, ...values) {
  // 实际应用中应该使用数据库驱动提供的参数化查询
  let query = strings[0];
  for (let i = 0; i < values.length; i++) {
    query += `$${i + 1}` + strings[i + 1];
  }
  return {
    text: query,
    values: values
  };
}

const userId = 123;
const query = sqlQuery`SELECT * FROM users WHERE id = ${userId}`;
// {
//   text: "SELECT * FROM users WHERE id = $1",
//   values: [123]
// }

五、特殊字符处理

1. 转义字符
javascript 复制代码
console.log(`反引号: \` 美元符号: \${`); // "反引号: ` 美元符号: ${"
2. 原始字符串

使用String.raw标签获取原始字符串(不处理转义字符):

javascript 复制代码
const path = String.raw`C:\Development\project\files`;
console.log(path); // "C:\Development\project\files"

// 等同于
function raw(strings, ...values) {
  let result = strings.raw[0];
  for (let i = 0; i < values.length; i++) {
    result += values[i] + strings.raw[i + 1];
  }
  return result;
}

六、性能考量

  1. 静态字符串:对于纯静态字符串,模板字符串与普通字符串性能相当
  2. 动态插值:频繁变化的插值内容可能影响性能,在极端性能敏感场景需测试
  3. 标签模板:自定义处理会增加开销,但通常可忽略不计

七、最佳实践

  1. 优先使用模板字符串:替代所有字符串拼接场景

  2. 复杂逻辑处理:对于复杂插值逻辑,考虑提前计算表达式

    javascript 复制代码
    // 不推荐
    console.log(`Result: ${calculateA() + calculateB() * complexCalculation()}`);
    
    // 推荐
    const result = calculateA() + calculateB() * complexCalculation();
    console.log(`Result: ${result}`);
  3. 多行缩进处理 :使用.trim()消除不必要的缩进

    javascript 复制代码
    function getHtml() {
      return `
        <div>
          <p>Content</p>
        </div>
      `.trim();
    }
  4. 安全注意事项

    • 不要直接将用户输入插入HTML/URL/SQL

    • 使用专用转义库或标签模板处理危险内容

八、浏览器兼容性

现代浏览器均支持模板字符串特性,对于旧版浏览器需要通过Babel等工具转译:

• ES6转ES5:将模板字符串转换为普通字符串拼接

• 标签模板:转换为函数调用形式

九、扩展应用

1. 配合React等框架
jsx 复制代码
const name = 'Alice';
const element = <div>Hello, {name}!</div>;

// JSX本质上也是一种模板字符串的扩展应用
2. 生成动态CSS
javascript 复制代码
const primaryColor = '#3498db';
const css = `
  .button {
    background: ${primaryColor};
    padding: 10px 20px;
  }
`;
3. 创建DSL(领域特定语言)
javascript 复制代码
function createRoute(strings, ...values) {
  return {
    path: strings.join('').replace(/\s+/g, ''),
    params: values
  };
}

const id = 123;
const route = createRoute`/users/ ${id} /profile`;
// { path: "/users/123/profile", params: [123] }

模板字符串彻底改变了JavaScript处理字符串的方式,使代码更简洁、更可读,同时通过标签模板提供了强大的扩展能力。正确使用这一特性可以显著提升开发效率和代码质量。

(四)、ES6解构赋值(Destructuring Assignment)全面解析

一、基本概念

解构赋值是ES6引入的一种语法,允许按照一定模式从数组或对象中提取值,然后对变量进行赋值。这种语法可以极大简化数据提取的代码。

二、数组解构

1. 基本用法
javascript 复制代码
const arr = [1, 2, 3];

// 传统方式
const a = arr[0];
const b = arr[1];
const c = arr[2];

// 解构赋值
const [a, b, c] = arr; // a=1, b=2, c=3
2. 嵌套解构
javascript 复制代码
const arr = [1, [2, 3], 4];
const [a, [b, c], d] = arr; // a=1, b=2, c=3, d=4
3. 默认值
javascript 复制代码
const [a=1, b=2] = []; // a=1, b=2
const [a, b=2] = [5]; // a=5, b=2
4. 跳过元素
javascript 复制代码
const [a, , b] = [1, 2, 3]; // a=1, b=3 (跳过第二个元素)
5. 剩余模式
javascript 复制代码
const [a, ...rest] = [1, 2, 3]; // a=1, rest=[2, 3]

三、对象解构

1. 基本用法
javascript 复制代码
const obj = { x: 1, y: 2 };

// 传统方式
const x = obj.x;
const y = obj.y;

// 解构赋值
const { x, y } = obj; // x=1, y=2
2. 别名赋值
javascript 复制代码
const { x: a, y: b } = { x: 1, y: 2 }; // a=1, b=2
3. 默认值
javascript 复制代码
const { a=1, b=2 } = {}; // a=1, b=2
const { a: x=1, b: y=2 } = { b: 3 }; // x=1, y=3
4. 嵌套解构
javascript 复制代码
const obj = { a: { b: 1, c: 2 }, d: 3 };
const { a: { b, c }, d } = obj; // b=1, c=2, d=3
5. 剩余模式
javascript 复制代码
const { a, ...rest } = { a: 1, b: 2, c: 3 }; // a=1, rest={b:2, c:3}

四、混合解构

可以混合使用数组和对象解构:

javascript 复制代码
const props = {
  arr: [1, { b: 2, c: 3 }]
};
const { arr: [a, { b, c }] } = props; // a=1, b=2, c=3

五、函数参数解构

1. 对象参数解构
javascript 复制代码
function draw({ x=0, y=0, radius=1 }) {
  console.log(x, y, radius);
}
draw({ x: 10, y: 20 }); // 10 20 1
2. 数组参数解构
javascript 复制代码
function sum([a=0, b=0]) {
  return a + b;
}
sum([1, 2]); // 3
3. 复杂参数解构
javascript 复制代码
function process({ 
  id, 
  name: firstName, 
  address: { city } = {} 
}) {
  console.log(id, firstName, city);
}
process({ id: 1, name: 'Alice', address: { city: 'Beijing' } });

六、特殊应用场景

1. 交换变量值
javascript 复制代码
let a = 1, b = 2;
[a, b] = [b, a]; // a=2, b=1
2. 函数返回多个值
javascript 复制代码
function getData() {
  return [1, 2, 3];
}
const [a, b, c] = getData();
3. 正则表达式匹配
javascript 复制代码
const url = 'https://example.com/path';
const { 1: protocol, 2: host } = url.match(/(\w+):\/\/([^/]+)/);
4. 模块导入
javascript 复制代码
import { Component, useState } from 'react';
5. 配置对象处理
javascript 复制代码
function init({ 
  width = 100, 
  height = 200, 
  color = 'red' 
} = {}) {
  // 使用解构参数并设置默认值
}

七、注意事项

  1. 解构失败 :如果解构不成功,变量的值等于undefined

    javascript 复制代码
    const [a] = []; // a=undefined
    const { b } = {}; // b=undefined
  2. 模式匹配:解构赋值的左边是模式,不是变量

    javascript 复制代码
    const { a: b } = { a: 1 }; // 模式是a,变量是b
  3. 不可迭代值:对非迭代值使用数组解构会报错

    javascript 复制代码
    const [a] = null; // TypeError
  4. 已声明变量:已声明变量解构需要用括号包裹

    javascript 复制代码
    let a;
    ({ a } = { a: 1 }); // 必须加括号
  5. 默认值生效条件 :只有当解构的值严格等于undefined时,默认值才会生效

    javascript 复制代码
    const { a = 1 } = { a: null }; // a=null

八、最佳实践

  1. 合理使用默认值:为可能不存在的属性设置默认值
  2. 避免过度嵌套:过深的解构会降低代码可读性
  3. 明确变量名:使用别名时选择有意义的名称
  4. 处理错误情况:考虑解构失败时的处理方式
  5. 文档注释:对复杂解构添加注释说明结构

九、浏览器兼容性

现代浏览器均支持解构赋值,对于旧版浏览器需要通过Babel等工具转译:

• 对象解构转换为Object.assign()或逐个属性赋值

• 数组解构转换为下标访问

解构赋值是ES6中最实用的特性之一,合理使用可以显著提高代码的简洁性和可读性,特别是在处理复杂数据结构时。

(五)、函数参数默认值

javascript 复制代码
function sayHello(name = 'Guest') {
  console.log(`Hello, ${name}!`);
}
sayHello(); // Hello, Guest!

(六)、ES6 扩展运算符(Spread Operator)深度解析

扩展运算符(...)是 ES6 引入的一个重要特性,它允许将可迭代对象(如数组、字符串、Map、Set 等)"展开"为单独的元素。

一、基本语法与概念

扩展运算符使用三个点(...)表示,主要功能是将一个可迭代对象展开为多个元素。

javascript 复制代码
const arr = [1, 2, 3];
console.log(...arr); // 1 2 3

二、数组中的应用

1. 数组复制(浅拷贝)
javascript 复制代码
const original = [1, 2, 3];
const copy = [...original]; // 创建新数组
2. 数组合并
javascript 复制代码
const arr1 = [1, 2];
const arr2 = [3, 4];
const merged = [...arr1, ...arr2]; // [1, 2, 3, 4]
3. 数组解构
javascript 复制代码
const [first, ...rest] = [1, 2, 3, 4];
console.log(first); // 1
console.log(rest); // [2, 3, 4]
4. 插入元素
javascript 复制代码
const numbers = [1, 2, 3];
const newNumbers = [0, ...numbers, 4]; // [0, 1, 2, 3, 4]
5. 替代 apply 方法
javascript 复制代码
// ES5
Math.max.apply(null, [1, 2, 3]);

// ES6
Math.max(...[1, 2, 3]);

三、对象中的应用(ES2018+)

1. 对象复制(浅拷贝)
javascript 复制代码
const obj = { a: 1, b: 2 };
const copy = { ...obj }; // { a: 1, b: 2 }
2. 对象合并
javascript 复制代码
const obj1 = { a: 1 };
const obj2 = { b: 2 };
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 2 }
3. 属性覆盖
javascript 复制代码
const defaults = { color: 'red', size: 'medium' };
const settings = { ...defaults, color: 'blue' };
// { color: 'blue', size: 'medium' }
4. 添加新属性
javascript 复制代码
const person = { name: 'Alice' };
const withAge = { ...person, age: 25 }; // { name: 'Alice', age: 25 }

四、函数参数中的应用

1. 收集剩余参数
javascript 复制代码
function sum(...numbers) {
  return numbers.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6
2. 传递数组参数
javascript 复制代码
const numbers = [1, 2, 3];
sum(...numbers); // 等同于 sum(1, 2, 3)

五、其他可迭代对象中的应用

1. 字符串展开
javascript 复制代码
const str = 'hello';
const chars = [...str]; // ['h', 'e', 'l', 'l', 'o']
2. Set 展开
javascript 复制代码
const set = new Set([1, 2, 3]);
const arr = [...set]; // [1, 2, 3]
3. Map 展开
javascript 复制代码
const map = new Map([['a', 1], ['b', 2]]);
const entries = [...map]; // [['a', 1], ['b', 2]]

六、高级用法

1. 实现数组扁平化
javascript 复制代码
function flatten(arr) {
  return [].concat(...arr);
}
flatten([[1], [2, 3], [4]]); // [1, 2, 3, 4]
2. 实现深度克隆(仅适用于特定情况)
javascript 复制代码
const deepClone = obj => JSON.parse(JSON.stringify(obj));
const cloned = deepClone({ a: [1, 2], b: { c: 3 } });
3. 条件展开
javascript 复制代码
const condition = true;
const obj = {
  ...(condition && { a: 1 }),
  b: 2
}; // { a: 1, b: 2 } 或 { b: 2 }

七、注意事项

  1. 浅拷贝问题:扩展运算符只进行浅拷贝

    javascript 复制代码
    const obj = { a: { b: 1 } };
    const copy = { ...obj };
    copy.a.b = 2;
    console.log(obj.a.b); // 2 (被修改)
  2. 性能考虑:对于大型数组/对象,扩展运算符可能不是最高效的选择

  3. 浏览器兼容性:对象展开是 ES2018 特性,旧环境可能需要 Babel 转译

  4. 不可迭代对象:不能展开普通对象(在数组上下文中)

    javascript 复制代码
    const obj = { a: 1, b: 2 };
    // [...obj]; // TypeError: obj is not iterable

八、最佳实践

  1. 优先使用扩展运算符 :替代 concatapply 等传统方法

  2. 合理使用解构:结合解构赋值处理复杂数据结构

  3. 注意不可变性:在 React/Redux 等需要不可变数据的场景中特别有用

  4. 命名清晰:使用有意义的变量名提高可读性

  5. 文档注释:对复杂展开逻辑添加注释说明

九、常见问题解答

Q1: 扩展运算符和剩余参数有什么区别?

A: 语法相同但使用场景不同:

• 扩展运算符用于展开元素

• 剩余参数用于收集元素

Q2: 如何实现深度克隆?

A: 扩展运算符只能浅拷贝,深度克隆需要递归或使用 JSON.parse(JSON.stringify())(有局限性)

Q3: 可以展开 Generator 吗?

A: 可以,Generator 是可迭代对象:

javascript 复制代码
function* gen() { yield 1; yield 2; }
[...gen()]; // [1, 2]

Q4: 为什么对象展开是 ES2018 特性?

A: 数组展开在 ES6 引入,对象展开稍晚标准化

Q5: 扩展运算符会影响原对象吗?

A: 不会,但浅拷贝的属性引用相同

扩展运算符极大简化了 JavaScript 中对数组和对象的操作,是现代 JavaScript 开发中不可或缺的特性。合理使用可以使代码更加简洁、可读性更强。

(七)、ES6 Promise 深度解析

一、Promise 基本概念

Promise 是 ES6 引入的异步编程解决方案,用于处理异步操作。它代表一个尚未完成但预期将来会完成的操作及其结果值。

1. 三种状态

pending(待定) :初始状态

fulfilled(已兑现) :操作成功完成

rejected(已拒绝):操作失败

状态转换是不可逆的:pending → fulfilled 或 pending → rejected

2. 基本语法
javascript 复制代码
const promise = new Promise((resolve, reject) => {
  // 异步操作
  if (/* 成功 */) {
    resolve(value); // 状态变为fulfilled
  } else {
    reject(error); // 状态变为rejected
  }
});

二、Promise 实例方法

1. then() 方法
javascript 复制代码
promise.then(
  value => { /* 成功处理 */ },
  error => { /* 失败处理 */ }
);
2. catch() 方法
javascript 复制代码
promise.catch(
  error => { /* 失败处理 */ }
);
3. finally() 方法
javascript 复制代码
promise.finally(
  () => { /* 无论成功失败都会执行 */ }
);

三、Promise 静态方法

1. Promise.resolve()
javascript 复制代码
Promise.resolve('success')
  .then(val => console.log(val)); // 'success'
2. Promise.reject()
javascript 复制代码
Promise.reject('error')
  .catch(err => console.log(err)); // 'error'
3. Promise.all()
javascript 复制代码
Promise.all([promise1, promise2])
  .then(values => { /* 所有promise都成功 */ })
  .catch(error => { /* 任一promise失败 */ });
4. Promise.race()
javascript 复制代码
Promise.race([promise1, promise2])
  .then(value => { /* 第一个完成的promise */ });
5. Promise.allSettled()
javascript 复制代码
Promise.allSettled([promise1, promise2])
  .then(results => { /* 所有promise都完成 */ });
6. Promise.any()
javascript 复制代码
Promise.any([promise1, promise2])
  .then(value => { /* 第一个成功的promise */ })
  .catch(errors => { /* 所有promise都失败 */ });

四、Promise 链式调用

Promise 的 then() 方法返回一个新的 Promise,可以实现链式调用:

javascript 复制代码
doSomething()
  .then(result => doSomethingElse(result))
  .then(newResult => doThirdThing(newResult))
  .then(finalResult => console.log(finalResult))
  .catch(failureCallback);

五、Promise 错误处理

1. 使用 catch()
javascript 复制代码
promise
  .then(handleSuccess)
  .catch(handleError);
2. then() 的第二个参数
javascript 复制代码
promise
  .then(handleSuccess, handleError);
3. 区别

.then(success, error):只能捕获当前 then 之前的错误

.catch(error):可以捕获整个链中的错误

六、Promise 最佳实践

  1. 总是返回 Promise:在 then() 回调中返回 Promise 或值
  2. 避免嵌套:使用链式调用而非嵌套
  3. 总是捕获错误:使用 catch() 处理错误
  4. 命名 Promise:给 Promise 变量有意义的名称
  5. 避免冗余代码:合理使用 Promise 静态方法

七、Promise 实现示例

1. 封装 setTimeout
javascript 复制代码
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

delay(1000).then(() => console.log('1秒后执行'));

2. 封装 XMLHttpRequest

javascript 复制代码
function getJSON(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
}

3. 封装 fetch

javascript 复制代码
function fetchData(url) {
  return fetch(url)
    .then(response => {
      if (!response.ok) {
        throw new Error('Network response was not ok');
      }
      return response.json();
    });
}

八、Promise 与 async/await

async/await 是建立在 Promise 之上的语法糖:

javascript 复制代码
async function fetchData() {
  try {
    const response = await fetch(url);
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Error:', error);
  }
}

九、常见问题解答

Q1: Promise 和回调函数有什么区别?

A: Promise 提供了更清晰的链式调用和错误处理,避免了回调地狱。

Q2: 如何取消一个 Promise?

A: 原生 Promise 无法取消,但可以使用 AbortController 或第三方库实现类似功能。

Q3: Promise 是微任务吗?

A: 是的,Promise 的回调会作为微任务执行,比 setTimeout 等宏任务优先级高。

Q4: 如何实现 Promise 重试机制?

A: 可以封装一个重试函数:

javascript 复制代码
function retry(fn, times) {
  return new Promise((resolve, reject) => {
    function attempt() {
      fn().then(resolve).catch(err => {
        if (times-- > 0) attempt();
        else reject(err);
      });
    }
    attempt();
  });
}

Q5: Promise.all 和 Promise.allSettled 有什么区别?

A: all 在任一 promise 失败时立即拒绝,allSettled 会等待所有 promise 完成。

Promise 是现代 JavaScript 异步编程的核心,理解其原理和用法对于编写高质量的异步代码至关重要。

(八)、ES6 Class 类全面解析

一、Class 基本概念

ES6 引入的 class 关键字实质上是 JavaScript 基于原型的继承的语法糖,它提供了更接近传统面向对象语言的写法。

1. 基本语法
javascript 复制代码
class Person {
  constructor(name) {
    this.name = name;
  }
  
  sayHello() {
    console.log(`Hello, ${this.name}!`);
  }
}

const alice = new Person('Alice');
alice.sayHello(); // "Hello, Alice!"

二、Class 核心特性

1. 构造方法 (constructor)
javascript 复制代码
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}
2. 实例方法
javascript 复制代码
class Person {
  // ...
  
  greet() {
    console.log(`Hi, I'm ${this.name}`);
  }
}
3. 静态方法 (static)
javascript 复制代码
class MathUtils {
  static sum(a, b) {
    return a + b;
  }
}

MathUtils.sum(1, 2); // 3
4. 静态属性
javascript 复制代码
class Config {
  static apiUrl = 'https://api.example.com';
}

console.log(Config.apiUrl);
5. 私有字段 (ES2022)
javascript 复制代码
class Counter {
  #count = 0; // 私有字段
  
  increment() {
    this.#count++;
  }
  
  getCount() {
    return this.#count;
  }
}

三、Class 继承

1. extends 继承
javascript 复制代码
class Student extends Person {
  constructor(name, grade) {
    super(name); // 调用父类构造函数
    this.grade = grade;
  }
  
  study() {
    console.log(`${this.name} is studying`);
  }
}
2. super 关键字

super() 调用父类构造函数

super.method() 调用父类方法

3. 方法重写
javascript 复制代码
class Student extends Person {
  sayHello() {
    super.sayHello(); // 调用父类方法
    console.log("I'm a student");
  }
}
4. 继承内置类
javascript 复制代码
class MyArray extends Array {
  first() {
    return this[0];
  }
  
  last() {
    return this[this.length - 1];
  }
}

四、Getter 和 Setter

javascript 复制代码
class Temperature {
  constructor(celsius) {
    this.celsius = celsius;
  }
  
  get fahrenheit() {
    return this.celsius * 1.8 + 32;
  }
  
  set fahrenheit(value) {
    this.celsius = (value - 32) / 1.8;
  }
}

const temp = new Temperature(25);
console.log(temp.fahrenheit); // 77
temp.fahrenheit = 86;
console.log(temp.celsius); // 30

五、Class 表达式

1. 命名类表达式
javascript 复制代码
const Person = class NamedPerson {
  constructor(name) {
    this.name = name;
  }
  
  sayName() {
    console.log(this.name);
  }
};
2. 匿名类表达式
javascript 复制代码
const Person = class {
  // ...
};

六、Class 与原型的关系

Class 本质上是构造函数的语法糖:

javascript 复制代码
typeof Person; // "function"
Person.prototype.constructor === Person; // true

七、Class 与函数声明的重要区别

  1. 提升(hoisting):类声明不会被提升
  2. 严格模式:类声明和类表达式默认在严格模式下执行
  3. 调用方式 :类必须使用 new 调用
  4. 方法枚举:类方法不可枚举

八、高级用法

1. Mixin 模式
javascript 复制代码
function mixin(...mixins) {
  class Mix {
    constructor() {
      for (let mixin of mixins) {
        copyProperties(this, new mixin());
      }
    }
  }
  
  for (let mixin of mixins) {
    copyProperties(Mix, mixin);
    copyProperties(Mix.prototype, mixin.prototype);
  }
  
  return Mix;
}

class DistributedEdit extends mixin(Loggable, Serializable) {
  // ...
}
2. 抽象基类
javascript 复制代码
class Abstract {
  constructor() {
    if (new.target === Abstract) {
      throw new Error('Cannot instantiate abstract class');
    }
  }
}

class Concrete extends Abstract {}
3. Symbol.iterator 实现
javascript 复制代码
class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }
  
  *[Symbol.iterator]() {
    for (let i = this.start; i <= this.end; i++) {
      yield i;
    }
  }
}

const range = new Range(1, 5);
[...range]; // [1, 2, 3, 4, 5]

九、最佳实践

  1. 单一职责原则:每个类应该只有一个职责
  2. 开放封闭原则:对扩展开放,对修改封闭
  3. 优先组合而非继承:减少复杂的继承层次
  4. 合理使用私有字段:保护内部状态
  5. 文档注释:使用 JSDoc 注释类和方法

十、常见问题解答

Q1: Class 与构造函数有什么区别?

A: Class 本质上是构造函数的语法糖,但提供了更清晰的语法和更好的错误检查。

Q2: 如何实现接口?

A: JavaScript 没有接口,可以通过抽象类或 TypeScript 实现。

Q3: 如何实现多重继承?

A: JavaScript 不支持多重继承,可以通过 Mixin 模式模拟。

Q4: 私有方法和属性有哪些实现方式?

A:

• 使用 ES2022 私有字段 #field

• 使用 WeakMap 或 Symbol 模拟

• 命名约定(如 _privateMethod

Q5: 如何检测一个类是否是另一个类的子类?

A: 使用 instanceofObject.prototype.isPrototypeOf()

javascript 复制代码
class Parent {}
class Child extends Parent {}

console.log(Child.prototype instanceof Parent); // true
console.log(Parent.prototype.isPrototypeOf(Child.prototype)); // true

ES6 Class 为 JavaScript 提供了更优雅的面向对象编程方式,虽然它本质上是基于原型的继承的语法糖,但这种语法更符合大多数程序员的习惯,也使代码更易于理解和维护。

(九)、模块系统

1. 导出

javascript 复制代码
// math.js
export const PI = 3.1415;
export function square(x) {
  return x * x;
}

2. 导入

javascript 复制代码
import { PI, square } from './math.js';
console.log(square(PI)); // 9.86902225

(十)、Symbol

javascript 复制代码
const sym1 = Symbol('description');
const sym2 = Symbol('description');
console.log(sym1 === sym2); // false

const obj = {
  [sym1]: 'value'
};
console.log(obj[sym1]); // value

(十一)、迭代器和生成器

1. 迭代器

javascript 复制代码
const iterable = {
  [Symbol.iterator]() {
    let step = 0;
    return {
      next() {
        step++;
        if (step <= 3) {
          return { value: step, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

for (const value of iterable) {
  console.log(value); // 1, 2, 3
}

2. 生成器

javascript 复制代码
function* idGenerator() {
  let id = 1;
  while (true) {
    yield id++;
  }
}

const gen = idGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2

(十二)、Set和Map

1. Set

javascript 复制代码
const set = new Set([1, 2, 3, 3, 4]);
console.log(set.size); // 4
console.log([...set]); // [1, 2, 3, 4]

2. Map

javascript 复制代码
const map = new Map();
map.set('name', 'Alice');
map.set('age', 25);
console.log(map.get('name')); // Alice

(十三)、Proxy和Reflect

1. Proxy

javascript 复制代码
const target = {};
const handler = {
  get(target, prop) {
    return prop in target ? target[prop] : 37;
  }
};

const proxy = new Proxy(target, handler);
proxy.a = 1;
console.log(proxy.a); // 1
console.log(proxy.b); // 37

2. Reflect

javascript 复制代码
const obj = { a: 1 };
console.log(Reflect.get(obj, 'a')); // 1
Reflect.set(obj, 'b', 2);
console.log(obj.b); // 2

(十四)、新的数据类型

1. TypedArray

javascript 复制代码
const buffer = new ArrayBuffer(16);
const int32View = new Int32Array(buffer);

2. DataView

javascript 复制代码
const view = new DataView(buffer);
view.setInt32(0, 42);
console.log(view.getInt32(0)); // 42

(十五)、字符串和数组新增方法

1. 字符串方法

javascript 复制代码
'hello'.startsWith('he'); // true
'hello'.endsWith('lo'); // true
'hello'.includes('ell'); // true
'abc'.repeat(3); // 'abcabcabc'

2. 数组方法

javascript 复制代码
[1, 2, 3].find(x => x > 1); // 2
[1, 2, 3].findIndex(x => x > 1); // 1
[1, 2, 3].fill(4); // [4, 4, 4]
Array.from('hello'); // ['h', 'e', 'l', 'l', 'o']
Array.of(1, 2, 3); // [1, 2, 3]

(十六)、尾调用优化

javascript 复制代码
function factorial(n, total = 1) {
  if (n === 1) return total;
  return factorial(n - 1, n * total); // 尾调用优化
}

(十七)、二进制和八进制字面量

javascript 复制代码
const binary = 0b1010; // 10
const octal = 0o12; // 10

(十八_、Object新增方法

javascript 复制代码
Object.assign({}, {a: 1}, {b: 2}); // {a: 1, b: 2}
Object.is(NaN, NaN); // true
Object.setPrototypeOf(obj, prototype);
Object.getOwnPropertySymbols(obj);

总结

ES6的这些新特性极大地丰富了JavaScript的功能,使代码更加简洁、可读性更强,同时也提高了开发效率。掌握这些特性对于现代JavaScript开发至关重要。随着JavaScript的不断发展,这些特性已经成为现代Web开发的基石。

相关推荐
旭久25 分钟前
react+antd封装一个可回车自定义option的select并且与某些内容相互禁用
前端·javascript·react.js
是纽扣也是烤奶30 分钟前
关于React Redux
前端
阿丽塔~32 分钟前
React 函数组件间怎么进行通信?
前端·javascript·react.js
冴羽1 小时前
SvelteKit 最新中文文档教程(17)—— 仅服务端模块和快照
前端·javascript·svelte
uhakadotcom1 小时前
Langflow:打造AI应用的强大工具
前端·面试·github
前端小张同学1 小时前
AI编程-cursor无限使用, 还有谁不会🎁🎁🎁??
前端·cursor
yanxy5121 小时前
【TS学习】(15)分布式条件特性
前端·学习·typescript
uhakadotcom2 小时前
Caddy Web服务器初体验:简洁高效的现代选择
前端·面试·github
前端菜鸟来报道2 小时前
前端react 实现分段进度条
前端·javascript·react.js·进度条