ES6 新特性

ES6 新特性详细讲解:常见使用方式、场景与坑

ES6(ES2015)是 JavaScript 历史上的一个巨大飞跃,引入了大量现代开发中不可或缺的语法与 API。下面按类别逐一剖析核心特性,重点说明日常怎么用、何时用、以及有哪些容易踩的坑


1. letconst

基本讲解

  • 块级作用域 :变量只在当前 {} 内有效,不再像 var 那样函数级作用域。
  • let:声明可变变量。
  • const:声明常量引用,对于对象/数组,内部属性可以改变,只是不能重新赋值。

常见使用方式与场景

js 复制代码
// 循环中使用 let 获得每次迭代独立的绑定
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i)); // 0, 1, 2
}
// 若使用 var 会全部输出 3

// 用 const 声明不会被重新赋值的引用,增强代码可读性
const config = { api: '/v1' };
config.api = '/v2'; // OK,属性可以改
// config = {};    // 报错:Assignment to constant variable

场景 :全面替代 var,优先使用 const,只在明确需要重新赋值时用 let

⚠️ 常见坑

  • 暂时性死区(TDZ) :声明前访问变量会报 ReferenceError,而不是 undefined

    js 复制代码
    console.log(a); // ReferenceError
    let a = 1;
  • const 声明的对象仍可被修改 ,要完全冻结需用 Object.freeze()

  • 重复声明 :同一作用域内 let/const 不允许重复声明同名变量(var 可以)。


2. 箭头函数

基本讲解

箭头函数 ()=>{} 没有自己的 thisargumentssupernew.target,它会捕获外层词法作用域的 this

常见使用方式与场景

js 复制代码
// 简短回调
[1, 2, 3].map(x => x * 2);

// 保持 this 指向
class Fetcher {
  constructor() { this.data = null; }
  fetch() {
    fetch('/api')
      .then(res => res.json())
      .then(json => { this.data = json; }); // this 指向 Fetcher 实例
  }
}

// 立即返回对象字面量要加小括号
const fn = name => ({ name });

场景 :几乎所有的短回调、需要绑定外层 this 的异步处理、数组方法回调。

⚠️ 常见坑

  • 没有自己的 this :在对象方法中直接使用箭头函数会导致 this 指向外层(可能为 windowundefined)。

    js 复制代码
    const obj = {
      name: 'test',
      say: () => console.log(this.name) // this 指向全局,无法获取 obj.name
    };
    obj.say(); // undefined
  • 不能用作构造函数 ,用 new 会抛出 TypeError

  • 没有 arguments 对象 ,需用剩余参数 ...args 替代。

  • 不适用于需要动态 this 的场景 ,如 Vue 组件的 methods 或事件处理函数绑定。


3. 模板字符串

基本讲解

使用反引号 ````` 包裹,支持多行字符串和 ${} 插值,还支持标签模板

常见使用方式与场景

js 复制代码
const name = 'World';
const str = `Hello ${name}!`; 

// 多行字符串
const html = `
  <div>
    <p>多行文字</p>
  </div>`;

// 标签模板(如 styled-components, GraphQL)
function highlight(strings, ...values) {
  return strings.reduce((acc, str, i) => 
    `${acc}${str}<mark>${values[i] || ''}</mark>`, '');
}
highlight`Hello ${name}, welcome!`; // Hello <mark>World</mark>, welcome!

场景:拼接 HTML、URL、SQL 查询、国际化文本、任何需要插入变量或多行文本的地方。

⚠️ 常见坑

  • 模板字符串内的空白和换行都会被保留,可能导致意料之外的空格。
  • 标签模板的 values 数量与 strings 差 1,需要小心处理边界。
  • ${} 里放复杂表达式,注意可读性。

4. 解构赋值

基本讲解

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

  • 数组解构let [a, b] = [1, 2];
  • 对象解构let { x, y } = { x: 1, y: 2 }; 支持重命名 { x: newX } 和默认值 { x = 10 }

常见使用方式与场景

js 复制代码
// 函数参数解构,直观获取配置
function ajax({ url, method = 'GET', data = {} }) { /*...*/ }

// 交换变量
[a, b] = [b, a];

// 提取深层属性
const { user: { profile: { email } } } = response;

场景 :提取接口返回数据、处理函数多个返回值、导入模块特定成员(import { readFile } from 'fs')。

⚠️ 常见坑

  • 解构 nullundefined 会报错

    js 复制代码
    const { a } = null; // TypeError: Cannot destructure property 'a' of 'null'

    建议对可能为空的数据设置默认空对象 const { a } = obj || {};

  • 默认值只在值为 undefined 时生效nullfalse0 不会触发默认值。

    js 复制代码
    const { x = 10 } = { x: null }; // x 是 null,不是 10
  • 重命名与默认值同时使用时,注意顺序:const { a: newA = 5 } = obj;


5. 展开运算符(Spread)与剩余参数(Rest)

基本讲解

  • 展开...iterable 将数组/字符串等可迭代对象展开为元素序列。
  • 剩余参数...args 将多个实参收集成一个数组,代替 arguments

常见使用方式与场景

js 复制代码
// 数组合并、浅拷贝
const arr = [1, 2, 3];
const newArr = [0, ...arr, 4]; // [0,1,2,3,4]
const copy = [...arr];

// 函数调用传参
Math.max(...arr);

// 剩余参数,收集多余实参
function sum(...nums) { return nums.reduce((a,b) => a+b, 0); }

// 浅拷贝对象(ES2018,此处作为扩展)
const objCopy = { ...originalObj };

场景:不可变数据操作、合并配置、函数参数灵活处理。

⚠️ 常见坑

  • 展开只做浅拷贝 ,嵌套对象/数组仍然是引用共享,修改会互相影响。

    js 复制代码
    const orig = { a: 1, b: { c: 2 } };
    const copy = { ...orig };
    copy.b.c = 3;
    console.log(orig.b.c); // 3,被修改了!
  • 剩余参数必须是最后一个形参:function(a, ...rest, b){} 会报错。

  • ... 用于函数调用时,若展开一个超大数据量数组可能导致参数过多(引擎有参数数量限制)。


6. 函数默认参数

基本讲解

形参可以直接赋予默认值,且默认表达式会在调用时惰性求值。

常见使用方式与场景

js 复制代码
function fetchData(url, method = 'GET', timeout = 5000) {
  // ...
}

// 结合解构
function createElement({ type = 'div', content = '' } = {}) {
  // 防止传入 undefined 导致解构报错
}

场景 :提供函数可选参数的默认行为,取代手动 options = options || {} 那种假值覆盖问题。

⚠️ 常见坑

  • 默认值只在参数为 undefined 时激活nullfalse0 不会触发。

  • 默认表达式作用域 :可以引用前面的参数,但遵循 TDZ。

    js 复制代码
    function f(x = y, y = 1) {} // 调用无参时 y 尚未初始化,报 ReferenceError
  • 注意参数默认值会让函数产生一个独立的 arguments 行为:在严格模式下,arguments 不会反映默认值参数的变化,非严格模式有些差异,建议直接用剩余参数。


7. 类(Class)

基本讲解

语法糖,基于原型继承实现,提供了更清晰的构造函数、方法和 extendssuper

常见使用方式与场景

js 复制代码
class Animal {
  constructor(name) { this.name = name; }
  speak() { console.log(`${this.name} makes a noise.`); }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 必须在使用 this 前调用
    this.breed = breed;
  }
  speak() { console.log(`${this.name} barks.`); }
}

场景:面向对象编程、React 类组件、封装复杂逻辑。

⚠️ 常见坑

  • 必须使用 new 调用 ,否则抛出 TypeError
  • 子类构造函数里 super() 必须且在使用 this 之前调用
  • 类方法不可枚举enumerable: false),与对象字面量方法不同。
  • 没有真正的私有属性 (ES6 标准中),后续通过 # 语法或 TypeScript private 实现。ES6 中一般用 _ 约定或 Symbol/WeakMap 模拟。
  • 箭头函数在类字段中(类字段提案)能绑定 this,但那是后续提案,不是 ES6 的 class 本身。

8. 模块(ES Modules)

基本讲解

通过 importexport 实现静态模块系统,支持命名导出、默认导出以及重命名。

常见使用方式与场景

js 复制代码
// math.js
export const PI = 3.14;
export function add(a, b) { return a + b; }
export default function multiply(a, b) { return a * b; }

// app.js
import multiply, { PI, add as sum } from './math.js';

场景:现代前端工程化、Node.js (type: module)、Tree-shaking 依赖分析。

⚠️ 常见坑

  • 静态结构importexport 只能在顶层作用域,无法在条件语句中使用(不能动态导入,需用 import() 函数,属 ES2020)。
  • 只读绑定:导入的变量是只读的,尝试修改会报错(但对象属性可修改)。
  • 循环依赖 :如果 a 导入 b,b 导入 a,可能会导致拿到的导出是未初始化的,需要重新设计模块或善用动态 import()
  • 路径必须完整 :浏览器环境下要加扩展名 .js,Node 中根据配置。

9. Promise

基本讲解

处理异步操作的状态机,有三种状态:pending、fulfilled、rejected。通过 .then() / .catch() 链式调用。

常见使用方式与场景

js 复制代码
function fetchUser(id) {
  return fetch(`/users/${id}`).then(res => res.json());
}

fetchUser(1)
  .then(user => console.log(user))
  .catch(err => console.error('失败', err))
  .finally(() => console.log('请求结束')); // finally ES2018

// Promise 静态方法
Promise.all([p1, p2])        // 全部成功
Promise.race([p1, p2])       // 谁先完成
Promise.resolve(value)
Promise.reject(reason)

场景:网络请求、定时器、文件读取等所有异步操作,且是 async/await 的基础。

⚠️ 常见坑

  • 忘记 return 导致链断裂

    js 复制代码
    fetchUser(1).then(user => {
      updateUI(user);
      // 没有 return 下一个 then 会收到 undefined
    }).then(data => ...);
  • 未处理拒绝 :未加 .catch() 的 Promise 一旦 reject,可能产生 UnhandledPromiseRejection 错误,需要全局兜底或必须 catch。

  • then 的回调返回一个 Promise 会自动展开,但同样要 return

  • Promise.all 一个失败全部失败,若需部分容错可用 Promise.allSettled(ES2020)或用 .catch 包裹单个请求。


10. Set 与 Map

基本讲解

  • Set :值唯一的集合,支持 add, has, delete, clear,可迭代。
  • Map :键值对集合,键可以是任意类型,而对象键只能是字符串或 Symbol。支持 set, get, has, delete,可迭代。

常见使用方式与场景

js 复制代码
// 数组去重
const arr = [1, 2, 2, 3];
const unique = [...new Set(arr)];

// Map 存储元数据,用对象做键
const cache = new Map();
const objKey = { id: 1 };
cache.set(objKey, 'data');
cache.get(objKey); // 'data'

场景:去重、缓存对象映射、记录访问状态、需要非字符串键的场景。

⚠️ 常见坑

  • Set 的比较是 SameValueZeroNaN 视为等于自身,+0-0 相等。

    js 复制代码
    new Set([NaN, NaN]).size; // 1
  • Map 的键基于引用比较,对象作为键时,即使内容相同但引用不同也会被视为不同键。若想用内容匹配,需自行处理。

  • WeakSet/WeakMap 只能存放对象,且对键是弱引用,不可迭代,常用于 DOM 节点关联数据避免内存泄漏,不能用于需要遍历的场景。


11. Symbol

基本讲解

创建唯一且不可变的值,常用作对象属性的标识符,防止属性名冲突。Symbol() 每次返回全新的值。

常见使用方式与场景

js 复制代码
const LOG_LEVEL = Symbol('logLevel');
const obj = {
  [LOG_LEVEL]: 'debug',
  normalProp: 'hi'
};
// for...in, Object.keys() 都不会枚举 Symbol 属性
console.log(obj[LOG_LEVEL]); // 'debug'

场景 :定义内部状态、自定义迭代器(Symbol.iterator)、框架中避免属性冲突。

⚠️ 常见坑

  • Symbol 不能隐式转换成字符串

    js 复制代码
    const s = Symbol('desc');
    '' + s; // TypeError: Cannot convert a Symbol value to a string

    需使用 String(s)s.description

  • JSON.stringify 会忽略 Symbol 键和 Symbol 值。

  • 全局 Symbol 注册用 Symbol.for('key'),但可能造成全局污染。


12. 迭代器(Iterator)与生成器(Generator)

基本讲解

  • 迭代器协议 :对象实现 next() 方法返回 { value, done }
  • 可迭代协议 :实现 Symbol.iterator 方法,用于 for...of... 展开等。
  • 生成器function* 函数,可通过 yield 暂停与恢复,返回迭代器。

常见使用方式与场景

js 复制代码
// 自定义迭代器
const range = {
  from: 1, to: 5,
  [Symbol.iterator]() {
    let current = this.from;
    return { next: () => current <= this.to ? { value: current++ } : { done: true } };
  }
};
for (let n of range) { console.log(n); }

// 生成器做 ID 生成
function* idMaker() {
  let id = 0;
  while (true) yield id++;
}
const gen = idMaker();
gen.next().value; // 0

场景:处理惰性求值序列、实现异步流程控制(redux-saga)、自定义数据结构遍历。

⚠️ 常见坑

  • 生成器返回的迭代器只能遍历一次。
  • 注意 yield 的返回值是下一个 next(arg) 传入的参数,容易在双向通信时混淆。
  • 在生成器中未捕获的错误会使生成器终止。
  • for...of 不能直接遍历普通对象(除非实现迭代器),必须迭代 Object.keys/values/entries

13. Proxy 和 Reflect

基本讲解

  • Proxy:创建对象的代理,拦截并自定义对象的基本操作(get、set、has、deleteProperty 等 13 种)。
  • Reflect:提供操作对象的默认方法(与 Proxy 陷阱一一对应),使行为更可预测。

常见使用方式与场景

js 复制代码
const handler = {
  get(target, key, receiver) {
    console.log(`Getting ${key}`);
    return Reflect.get(target, key, receiver);
  },
  set(target, key, value, receiver) {
    if (typeof value !== 'number') throw new Error('必须为数字');
    return Reflect.set(target, key, value, receiver);
  }
};
const obj = new Proxy({}, handler);
obj.a = 10; // 成功
obj.b = 'x'; // 抛出错误

场景:数据验证、日志记录、响应式系统(如 Vue 3 的 reactivity)、属性访问控制、实现数组负索引等。

⚠️ 常见坑

  • 代理会带来性能开销,在热点代码中需谨慎。
  • this 陷阱 :代理中的方法若依赖 this,可能指向代理而非原始对象,需要绑定或使用 receiver
  • 无法拦截某些操作:比如严格模式下某些操作、原型链上的 property 查找。
  • 应使用 Reflect 执行默认行为,否则可能破坏内部原理。

14. 新增数组与字符串方法

常见新增

  • Array : find, findIndex, fill, copyWithin, Array.from, Array.of, includes (ES7 但常与 ES6 共提)。
  • String : startsWith, endsWith, includes, repeat, padStart/padEnd (后俩 ES2017)。

使用方式与场景

js 复制代码
// 查找第一个符合条件的元素
const users = [{id:1}, {id:2}];
const user = users.find(u => u.id === 2);

// 填充数组
new Array(3).fill(0); // [0,0,0]

// 将类数组/迭代器转为真数组
Array.from(document.querySelectorAll('li'));

// 字符串检测
'/api/users'.startsWith('/api');

场景 :替代 indexOf !== -1 的写法,填充默认数据,操作类数组。

⚠️ 常见坑

  • find 返回第一个元素,未找到返回 undefinedfindIndex 未找到返回 -1

  • fill 使用同一个引用填充对象数组,导致所有元素共享同一个对象,修改一个会影响全部:

    js 复制代码
    const arr = new Array(3).fill({});
    arr[0].a = 1;
    console.log(arr[1].a); // 1,全指向同一个对象
  • Array.from 对包含 Symbol.iterator 的对象转换更可靠,普通有 length 属性的也能转,但会缺失真正迭代。


15. for...of 循环

基本讲解

遍历可迭代对象(数组、字符串、Set、Map、arguments、NodeList 等),获取的是for...in 获取键)。

使用与场景

js 复制代码
// 遍历 Map
const map = new Map([['a', 1], ['b', 2]]);
for (let [key, value] of map) { /* ... */ }

// 遍历字符串
for (let char of 'hello') {}

场景 :无需索引的数组遍历,支持 break/continue,比 forEach 灵活。

⚠️ 常见坑

  • 不能直接遍历纯对象,因为普通对象没有实现迭代器协议,会报错。可通过 Object.entries(obj) 等处理。
  • 在遍历过程中修改遍历集合会导致行为不一致,建议先复制一份。

总结与最佳实践

  • 变量声明 :默认 const,需要改则 let,忘记 var
  • 函数 :回调用箭头函数,但对象方法和动态 this 场景用普通函数。
  • 异步 :Promise 链式调用记得 returncatch,更推荐 async/await
  • 解构/展开:善用但注意浅拷贝问题,深层修改用深拷贝库或手动解构。
  • :组织复杂逻辑,别忘 super 规则,私有性可结合 TypeScript 或 # 语法。
  • 模块:静态导入为主,动态导入用于按需加载;留意循环依赖。
  • 新数据结构:用 Map/Set 处理键非字符串、去重等;注意引用比较。

掌握这些特性及其边界情况,能大大提升代码简洁性、可读性和健壮性,同时避开常见的开发陷阱。

相关推荐
KaMeidebaby1 小时前
卡梅德生物技术快报|抗原如何自己检测?FAdV-4 重组抗原制备与 ELISA 体系技术调试指南
前端·人工智能·物联网·算法·百度
一拳不是超人1 小时前
AI 辅助研发时代,如何用“规范 Skill”缩短测试周期
前端·人工智能·代码规范
夜郎king3 小时前
湖南高考天气查询:基于 HTML5 与百度天气 API 实现页面展示
前端·html5·百度天气实践·天气信息可视化
云水一下10 小时前
TypeScript 从零基础到精通(五):高级类型与泛型
前端·javascript·typescript
counterxing10 小时前
vibe coding 之后,我更不想打字了
前端·agent·ai编程
copyer_xyf11 小时前
Python 模块与包的导入导出
前端·后端·python
研☆香11 小时前
es6新特性功能介绍(四)
前端·ecmascript·es6
微扬嘴角11 小时前
React篇1--JSX语法规则、组件、组件实例的3大特性
前端·react.js·前端框架
copyer_xyf11 小时前
Python venv 虚拟环境
前端·后端·python