Skip to content

ES6+ 新特性

1. 变量声明 (let / const)

1.1 块级作用域

javascript
// var - 函数作用域
if (true) {
    var a = 1;
}
console.log(a);  // 1 (泄漏到外部)

// let/const - 块级作用域
if (true) {
    let b = 2;
    const c = 3;
}
console.log(b);  // ReferenceError

1.2 暂时性死区 (TDZ)

javascript
console.log(x);  // undefined (var 提升)
var x = 1;

console.log(y);  // ReferenceError (TDZ)
let y = 2;

// TDZ: 从块作用域开始到变量声明之间的区域
{
    // --- TDZ 开始 ---
    console.log(z);  // ReferenceError
    // --- TDZ 结束 ---
    let z = 3;
}

1.3 const 特性

javascript
const PI = 3.14159;
PI = 3;  // TypeError: 不能重新赋值

// const 只保证引用不变, 对象内容可变
const obj = { name: 'Alice' };
obj.name = 'Bob';  // OK
obj = {};  // TypeError

// 冻结对象
const frozen = Object.freeze({ name: 'Alice' });
frozen.name = 'Bob';  // 静默失败 (严格模式报错)

1.4 循环中的 let

javascript
// var: 共享同一变量
for (var i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100);
}
// 输出: 3, 3, 3

// let: 每次迭代创建新绑定
for (let i = 0; i < 3; i++) {
    setTimeout(() => console.log(i), 100);
}
// 输出: 0, 1, 2

2. 箭头函数 🔥

2.1 语法简化

javascript
// 传统函数
const add = function(a, b) {
    return a + b;
};

// 箭头函数
const add = (a, b) => a + b;

// 单参数可省略括号
const double = x => x * 2;

// 返回对象需要括号
const getObj = () => ({ name: 'Alice' });

// 多行需要大括号和 return
const complex = (a, b) => {
    const result = a + b;
    return result * 2;
};

2.2 this 绑定

javascript
// 箭头函数没有自己的 this, 继承外层作用域
const obj = {
    name: 'Alice',
    
    // 传统: this 取决于调用方式
    sayHi: function() {
        setTimeout(function() {
            console.log(this.name);  // undefined (this 指向 window)
        }, 100);
    },
    
    // 箭头函数: 继承 sayHello 的 this
    sayHello: function() {
        setTimeout(() => {
            console.log(this.name);  // 'Alice'
        }, 100);
    }
};

2.3 不适用场景

javascript
// ❌ 对象方法
const obj = {
    name: 'Alice',
    sayHi: () => console.log(this.name)  // undefined
};

// ❌ 构造函数
const Person = (name) => { this.name = name; };
new Person('Alice');  // TypeError

// ❌ arguments 对象
const fn = () => console.log(arguments);  // ReferenceError

// ❌ 原型方法
function Person(name) { this.name = name; }
Person.prototype.sayHi = () => console.log(this.name);  // undefined

3. 解构赋值 🔥

3.1 对象解构

javascript
const user = { name: 'Alice', age: 25, city: 'NYC' };

// 基础解构
const { name, age } = user;

// 重命名
const { name: userName } = user;

// 默认值
const { country = 'USA' } = user;

// 嵌套解构
const { address: { street } } = { address: { street: '123 Main' } };

// Rest 收集
const { name, ...rest } = user;  // rest = { age: 25, city: 'NYC' }

3.2 数组解构

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

// 基础解构
const [first, second] = arr;

// 跳过元素
const [, , third] = arr;

// 默认值
const [a = 0, b = 0] = [];

// Rest 收集
const [head, ...tail] = arr;  // tail = [2, 3, 4, 5]

// 交换变量
let x = 1, y = 2;
[x, y] = [y, x];

3.3 函数参数解构

javascript
// 对象参数
function greet({ name, age = 18 }) {
    console.log(`${name}, ${age}`);
}
greet({ name: 'Alice' });

// 数组参数
function first([x]) {
    return x;
}
first([1, 2, 3]);  // 1

4. 模板字符串

4.1 基本用法

javascript
const name = 'Alice';
const age = 25;

// 多行字符串
const html = `
<div>
    <h1>Hello, ${name}</h1>
    <p>Age: ${age}</p>
</div>
`;

// 表达式
const result = `Sum: ${1 + 2}`;
const message = `Status: ${age >= 18 ? 'Adult' : 'Minor'}`;

4.2 标签模板

javascript
function highlight(strings, ...values) {
    return strings.reduce((result, str, i) => {
        const value = values[i] ? `<mark>${values[i]}</mark>` : '';
        return result + str + value;
    }, '');
}

const name = 'Alice';
const age = 25;
highlight`Name: ${name}, Age: ${age}`;
// "Name: <mark>Alice</mark>, Age: <mark>25</mark>"

// 实际应用: styled-components
const Button = styled.button`
    color: ${props => props.primary ? 'blue' : 'gray'};
`;

5. 展开运算符 (...) 🔥

5.1 数组展开

javascript
// 合并数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const merged = [...arr1, ...arr2];  // [1, 2, 3, 4]

// 复制数组 (浅拷贝)
const copy = [...arr1];

// 函数调用
Math.max(...[1, 2, 3]);  // 3

// 字符串转数组
[...'hello'];  // ['h', 'e', 'l', 'l', 'o']

5.2 对象展开

javascript
// 合并对象
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3 };
const merged = { ...obj1, ...obj2 };  // { a: 1, b: 2, c: 3 }

// 覆盖属性
const updated = { ...obj1, b: 10 };  // { a: 1, b: 10 }

// 浅拷贝
const copy = { ...obj1 };

5.3 Rest 参数

javascript
// 收集剩余参数
function sum(...numbers) {
    return numbers.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3, 4);  // 10

// 解构中的 Rest
const [first, ...rest] = [1, 2, 3];
const { a, ...others } = { a: 1, b: 2, c: 3 };

6. 默认参数与增强对象字面量

6.1 默认参数

javascript
// 基础默认值
function greet(name = 'Guest', greeting = 'Hello') {
    return `${greeting}, ${name}!`;
}

// 表达式作为默认值
function getId(prefix = Date.now()) {
    return `${prefix}-${Math.random()}`;
}

// 解构默认值
function config({ timeout = 1000, retries = 3 } = {}) {
    console.log(timeout, retries);
}
config();  // 1000, 3

6.2 增强对象字面量

javascript
const name = 'Alice';
const age = 25;

// 属性简写
const user = { name, age };  // { name: 'Alice', age: 25 }

// 方法简写
const obj = {
    greet() {  // 不用 function 关键字
        return 'Hello';
    }
};

// 计算属性名
const key = 'dynamicKey';
const obj = {
    [key]: 'value',
    [`${key}_2`]: 'value2'
};

7. Class (类)

7.1 基本语法

javascript
class Person {
    // 构造函数
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    // 实例方法
    greet() {
        return `Hello, I'm ${this.name}`;
    }

    // 静态方法
    static create(name) {
        return new Person(name, 0);
    }

    // Getter
    get info() {
        return `${this.name}, ${this.age}`;
    }

    // Setter
    set info(value) {
        [this.name, this.age] = value.split(',');
    }
}

const alice = new Person('Alice', 25);
alice.greet();  // "Hello, I'm Alice"
Person.create('Bob');  // Person { name: 'Bob', age: 0 }

7.2 继承

javascript
class Animal {
    constructor(name) {
        this.name = name;
    }

    speak() {
        console.log(`${this.name} makes a sound`);
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name);  // 必须先调用 super()
        this.breed = breed;
    }

    speak() {
        super.speak();  // 调用父类方法
        console.log(`${this.name} barks`);
    }
}

const dog = new Dog('Buddy', 'Golden');
dog.speak();
// Buddy makes a sound
// Buddy barks

7.3 私有字段 (ES2022) 🔥

javascript
class Counter {
    #count = 0;  // 私有字段
    static #total = 0;  // 静态私有字段

    increment() {
        this.#count++;
        Counter.#total++;
    }

    get count() {
        return this.#count;
    }

    #privateMethod() {  // 私有方法
        console.log('Private');
    }
}

const counter = new Counter();
counter.#count;  // SyntaxError: 无法访问

8. Proxy & Reflect 🔥

8.1 Proxy 基础

javascript
const target = { name: 'Alice', age: 25 };

const proxy = new Proxy(target, {
    // 拦截属性读取
    get(target, prop, receiver) {
        console.log(`Getting ${prop}`);
        return Reflect.get(target, prop, receiver);
    },
    
    // 拦截属性设置
    set(target, prop, value, receiver) {
        console.log(`Setting ${prop} = ${value}`);
        return Reflect.set(target, prop, value, receiver);
    },
    
    // 拦截 in 操作符
    has(target, prop) {
        return prop in target;
    },
    
    // 拦截属性删除
    deleteProperty(target, prop) {
        delete target[prop];
        return true;
    }
});

proxy.name;  // Getting name → 'Alice'
proxy.age = 30;  // Setting age = 30

8.2 常用 Handler

Handler拦截操作
get属性读取
set属性设置
hasin 操作符
deletePropertydelete 操作
apply函数调用
constructnew 操作
ownKeysObject.keys()
getPrototypeOfObject.getPrototypeOf()
setPrototypeOfObject.setPrototypeOf()
definePropertyObject.defineProperty()
getOwnPropertyDescriptorObject.getOwnPropertyDescriptor()

8.3 Vue 3 响应式原理

javascript
function reactive(target) {
    return new Proxy(target, {
        get(target, key, receiver) {
            track(target, key);  // 收集依赖
            const result = Reflect.get(target, key, receiver);
            // 嵌套对象也要代理
            return typeof result === 'object' ? reactive(result) : result;
        },
        set(target, key, value, receiver) {
            const result = Reflect.set(target, key, value, receiver);
            trigger(target, key);  // 触发更新
            return result;
        }
    });
}

8.4 Reflect

javascript
// Reflect 方法与 Proxy handler 一一对应
Reflect.get(obj, 'name');
Reflect.set(obj, 'name', 'Alice');
Reflect.has(obj, 'name');
Reflect.deleteProperty(obj, 'name');
Reflect.ownKeys(obj);

// 为什么用 Reflect?
// 1. 返回值语义化 (Reflect.set 返回 boolean)
// 2. 正确处理 receiver (继承场景)
// 3. 与 Proxy 配合使用

9. Map & Set

9.1 Map

javascript
const map = new Map();

// 设置/获取
map.set('key', 'value');
map.set({ id: 1 }, 'object key');  // 对象可作为键
map.get('key');  // 'value'

// 检查/删除
map.has('key');  // true
map.delete('key');
map.clear();

// 遍历
map.forEach((value, key) => console.log(key, value));
for (const [key, value] of map) {
    console.log(key, value);
}

// 大小
map.size;

// 与数组互转
const arr = [['a', 1], ['b', 2]];
const map = new Map(arr);
const backToArr = [...map];

9.2 Set

javascript
const set = new Set([1, 2, 3, 3]);  // 自动去重

// 添加/检查/删除
set.add(4);
set.has(3);  // true
set.delete(1);
set.clear();

// 遍历
set.forEach(value => console.log(value));
for (const value of set) {
    console.log(value);
}

// 数组去重
const unique = [...new Set([1, 2, 2, 3])];

// 集合运算
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);

// 并集
const union = new Set([...a, ...b]);

// 交集
const intersection = new Set([...a].filter(x => b.has(x)));

// 差集
const difference = new Set([...a].filter(x => !b.has(x)));

9.3 WeakMap & WeakSet

javascript
// 键必须是对象, 弱引用 (不阻止垃圾回收)
const weakMap = new WeakMap();
let obj = { data: 'important' };
weakMap.set(obj, 'metadata');

obj = null;  // obj 可被垃圾回收, weakMap 中的条目自动移除

// 使用场景: 存储 DOM 节点关联数据
const nodeData = new WeakMap();
const div = document.createElement('div');
nodeData.set(div, { clicks: 0 });
// div 被移除时, 关联数据自动释放

10. Symbol

10.1 基本用法

javascript
// 创建唯一标识
const sym1 = Symbol('description');
const sym2 = Symbol('description');
sym1 === sym2;  // false

// 作为对象键
const obj = {
    [sym1]: 'value'
};
obj[sym1];  // 'value'

// 不会被常规遍历发现
Object.keys(obj);  // []
Object.getOwnPropertySymbols(obj);  // [Symbol(description)]

10.2 内置 Symbol

javascript
// Symbol.iterator - 定义迭代器
const obj = {
    data: [1, 2, 3],
    [Symbol.iterator]() {
        let index = 0;
        return {
            next: () => ({
                value: this.data[index],
                done: index++ >= this.data.length
            })
        };
    }
};
[...obj];  // [1, 2, 3]

// Symbol.toStringTag - 自定义 toString 标签
class MyClass {
    get [Symbol.toStringTag]() {
        return 'MyClass';
    }
}
Object.prototype.toString.call(new MyClass());  // '[object MyClass]'

// Symbol.toPrimitive - 类型转换
const obj = {
    [Symbol.toPrimitive](hint) {
        if (hint === 'number') return 42;
        if (hint === 'string') return 'hello';
        return true;
    }
};

10.3 Symbol.for 全局注册

javascript
// 全局共享 Symbol
const sym1 = Symbol.for('shared');
const sym2 = Symbol.for('shared');
sym1 === sym2;  // true

// 获取 key
Symbol.keyFor(sym1);  // 'shared'

11. Iterator & Generator

11.1 迭代器协议

javascript
// 可迭代对象需实现 [Symbol.iterator]
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
}

11.2 Generator 函数

javascript
function* generator() {
    yield 1;
    yield 2;
    yield 3;
}

const gen = generator();
gen.next();  // { value: 1, done: false }
gen.next();  // { value: 2, done: false }
gen.next();  // { value: 3, done: false }
gen.next();  // { value: undefined, done: true }

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

11.3 Generator 高级用法

javascript
function* gen() {
    const a = yield 1;
    console.log('a:', a);
    const b = yield 2;
    console.log('b:', b);
    return 3;
}

const g = gen();
g.next();      // { value: 1, done: false }
g.next('A');   // a: A → { value: 2, done: false }
g.next('B');   // b: B → { value: 3, done: true }

// yield* 委托
function* outer() {
    yield 1;
    yield* [2, 3];  // 委托给数组迭代器
    yield* inner();  // 委托给另一个 generator
}

function* inner() {
    yield 4;
    yield 5;
}

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

12. 模块 (Module)

12.1 导出

javascript
// 命名导出
export const name = 'Alice';
export function greet() {}
export class Person {}

// 统一导出
const a = 1;
const b = 2;
export { a, b };

// 重命名导出
export { a as aliasA };

// 默认导出
export default function() {}
export default class {}
export default { name: 'Alice' };

12.2 导入

javascript
// 命名导入
import { name, greet } from './module.js';

// 重命名导入
import { name as userName } from './module.js';

// 导入全部
import * as module from './module.js';

// 默认导入
import MyDefault from './module.js';

// 混合导入
import MyDefault, { name } from './module.js';

// 动态导入 (返回 Promise)
const module = await import('./module.js');

12.3 模块特性

javascript
// 严格模式: 模块自动启用 'use strict'

// 顶层 this: undefined
console.log(this);  // undefined

// 顶层 await (ES2022)
const data = await fetch('/api/data');

// 模块只执行一次, 后续导入是缓存
import './init.js';  // 执行
import './init.js';  // 不再执行

13. ES2020+ 新特性

13.1 可选链 (?.) 🔥

javascript
const user = { profile: { name: 'Alice' } };

// 传统
const name = user && user.profile && user.profile.name;

// 可选链
const name = user?.profile?.name;

// 方法调用
obj.method?.();

// 数组索引
arr?.[0];

13.2 空值合并 (??) 🔥

javascript
// || 的问题: 0, '', false 被视为假值
const a = 0 || 'default';  // 'default' (错误)

// ?? 只在 null/undefined 时使用默认值
const a = 0 ?? 'default';  // 0 (正确)
const b = null ?? 'default';  // 'default'
const c = undefined ?? 'default';  // 'default'

13.3 逻辑赋值运算符 (ES2021)

javascript
// ||= : 左侧为假值时赋值
a ||= b;  // a = a || b

// &&= : 左侧为真值时赋值
a &&= b;  // a = a && b

// ??= : 左侧为 null/undefined 时赋值
a ??= b;  // a = a ?? b

// 实际应用
user.name ??= 'Anonymous';
options.timeout ??= 5000;

13.4 数字分隔符 (ES2021)

javascript
const billion = 1_000_000_000;
const bytes = 0xFF_FF_FF;
const binary = 0b1010_0001;

13.5 Promise.any (ES2021)

javascript
// 返回第一个成功的 Promise
const fastest = await Promise.any([
    fetch('/api/server1'),
    fetch('/api/server2')
]);

// 全部失败时抛出 AggregateError
try {
    await Promise.any([
        Promise.reject('Error 1'),
        Promise.reject('Error 2')
    ]);
} catch (error) {
    console.log(error.errors);  // ['Error 1', 'Error 2']
}

13.6 数组方法 (ES2022-2023)

javascript
// Array.at() - 支持负索引
[1, 2, 3].at(-1);  // 3

// Array.findLast() / findLastIndex()
[1, 2, 3, 2].findLast(x => x === 2);  // 2 (最后一个)
[1, 2, 3, 2].findLastIndex(x => x === 2);  // 3

// Array.toSorted() / toReversed() / toSpliced() - 不改变原数组
const arr = [3, 1, 2];
arr.toSorted();  // [1, 2, 3], arr 不变
arr.toReversed();  // [2, 1, 3]
arr.toSpliced(1, 1, 'a');  // [3, 'a', 2]

// Array.with() - 替换指定位置元素
[1, 2, 3].with(1, 'a');  // [1, 'a', 3]

13.7 Object.groupBy (ES2024)

javascript
const people = [
    { name: 'Alice', age: 25 },
    { name: 'Bob', age: 30 },
    { name: 'Charlie', age: 25 }
];

Object.groupBy(people, p => p.age);
// {
//   25: [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 }],
//   30: [{ name: 'Bob', age: 30 }]
// }

14. 面试高频问题

Q1: var、let、const 的区别?

特性varletconst
作用域函数块级块级
变量提升✅ (undefined)❌ (TDZ)❌ (TDZ)
重复声明
重新赋值

Q2: 箭头函数和普通函数的区别?

  1. 没有自己的 this, 继承外层作用域
  2. 没有 arguments 对象
  3. 不能用作构造函数 (new)
  4. 没有 prototype 属性
  5. 不能用作 Generator

Q3: Map 和 Object 的区别?

特性MapObject
键类型任意类型字符串/Symbol
顺序插入顺序部分有序
大小map.size手动计算
迭代直接可迭代需要转换
性能频繁增删更优静态键更优

Q4: WeakMap 的使用场景?

  1. 存储 DOM 节点关联数据 (节点删除后自动清理)
  2. 缓存计算结果 (对象释放后缓存自动清理)
  3. 私有数据存储

Q5: Proxy 相比 Object.defineProperty 的优势?

特性ProxyObject.defineProperty
拦截操作数13 种只有 get/set
数组索引✅ 原生支持❌ 需要特殊处理
新增属性✅ 自动拦截❌ 需要 Vue.$set
性能略低略高

Q6: 可选链和空值合并的区别?

  • ?.: 解决链式访问中 null/undefined 的问题
  • ??: 解决默认值赋值中 0/'' 被误判的问题

前端面试知识库