积累:03-ES6

1. 说说var、let、const之间的区别

  • 一句话:
    • var 是函数作用域,可以变量提升
    • let 和 const 是块级作用域,有暂时性死区,不能重复声明
    • const 声明的变量不能修改,但是const 对象的属性可以修改
特性 var let const
作用域 函数作用域 块级作用域 块级作用域
变量提升 ✅ 是 ❌ 否 ❌ 否
暂时性死区 ❌ 无 ✅ 有 ✅ 有
重复声明 ✅ 可以 ❌ 报错 ❌ 报错
是否可修改 ✅ 可修改 ✅ 可修改 ❌ 不可重新赋值(但对象属性可变)
  • 作用域差异
js 复制代码
function test() {
  if (true) {
    var a = 1;
    let b = 2;
    const c = 3;
  }
  console.log(a); // ✅ 1
  console.log(b); // ❌ 报错:b is not defined
  console.log(c); // ❌ 报错:c is not defined
}
  • 变量提升
js 复制代码
console.log(x); // undefined(var 提升)
var x = 5;

console.log(y); // ❌ 报错(let 不提升)
let y = 5;
  • 暂时性死区(TDZ)
js 复制代码
console.log(a); // 报错 ReferenceError
let a = 10;
//  在声明a之前,a不能访问,这段事件就叫"暂时性死区"

-重复声明

js 复制代码
var a = 1;
var a = 2; // ✅ 合法

let b = 1;
let b = 2; // ❌ 报错:Identifier 'b' has already been declared
  • const 不可修改(引用不可变)
js 复制代码
const obj = { name: 'Tom' };
obj.name = 'Jerry'; // ✅ 可以改属性

obj = {}; // ❌ 报错:Assignment to constant variable

2. ES6中数组新增了哪些扩展?

  • 一句话:
    • from、of 新建数组
    • find、includes 查元素
    • fill、copyWithin 改数组
    • entries/keys/values 遍历数组
方法名 分类 用途简介 示例
Array.from() 创建 把类数组/可迭代对象转成真正数组 Array.from('abc') → ['a','b','c']
Array.of() 创建 用参数生成数组(区别于 Array 构造器) Array.of(1, 2) → [1, 2]
find() 查找 找出符合条件的第一个元素 [1,2,3].find(x => x > 1) → 2
findIndex() 查索引 找出符合条件的第一个索引 [1,2,3].findIndex(x => x > 1) → 1
includes() 判断 是否包含某个值(类似字符串) [1, 2, 3].includes(2) → true
fill() 替换 用某个值填满整个或部分数组 [1,2,3].fill(0) → [0,0,0]
copyWithin() 拷贝 把数组一部分复制到另一部分 [1,2,3,4].copyWithin(1, 2) → [1,3,4,4]
entries() 遍历器 返回键值对迭代器 for-of 可用:[...arr.entries()]
keys() 遍历器 返回键名迭代器 [...arr.keys()] → [0,1,2]
values() 遍历器 返回值迭代器 [...arr.values()] → [a,b,c](某些浏览器需兼容)

3. 函数新增了哪些扩展?

  • 一句话:默认值、箭头函数、省参数、...收集、name提升、this自动绑!
特性 说明 示例
✅ 默认参数 函数参数可设置默认值 function fn(a = 1) {}
✅ 剩余参数 ...args 获取不定数量参数(替代 arguments function sum(...nums) {}
✅ 箭头函数 => 更简洁的函数写法,自动绑定 this const add = (a, b) => a + b
函数名 name属性 函数自动拥有 name 属性 (function hello() {}).name // "hello"
function* 生成器函数 暂停执行、生成迭代器 function* gen() { yield 1; }
  • 默认参数
js 复制代码
    function greet(name = '匿名') {
      return `你好,${name}`;
    }
    greet(); // "你好,匿名"
  • 剩余参数...rest (代替arguments)
js 复制代码
function sum(...nums) {
  return nums.reduce((a, b) => a + b);
}
sum(1, 2, 3); // 6
  • 箭头函数 ()=>{}
    • 箭头函数没有自己的this,arguments,super,自动继承外层上下文
    • 关于this指向:谁定义就指向谁
js 复制代码
function Timer() {
  this.count = 0;
  setInterval(() => {
    this.count++; // this 指向 Timer 实例
  }, 1000);
}

4. 函数名属性 function.name

javascript 复制代码
js
复制编辑
function foo() {}
console.log(foo.name); // "foo"

const bar = function () {};
console.log(bar.name); // "bar"(现代浏览器自动推断)
  • 函数生成器 function * (高级)
js 复制代码
function* gen(){
  yield 1;
  yield 2;
  return 3;
}

const it = gen();
it.next(); // { value: 1, done: false }

补充:ES6+ 的 async/await(ES8)

虽然是 ES8 引入的,但属于函数的重要演进:

csharp 复制代码
js
复制编辑
async function fetchData() {
  const res = await fetch('/api');
  const data = await res.json();
  return data;
}

4. 对象新增了哪些扩展?

✅ 一句话速记口诀:

简洁写法、动态属性、Object 新方法、原型操作全掌握!


🧠 ES6 对象的主要扩展(分为两类):

🔹 一、对象字面量增强语法(简洁写法)

特性 说明 示例
属性简写 变量名与属性名相同,可省略 { name } 相当于 { name: name }
方法简写 可省略 function 关键字 say() {}
动态属性名 属性名可用变量计算 [propName]: value
✅ 示例:
ini 复制代码
js
复制编辑
const name = 'Tom';
const age = 18;
const prop = 'score';

const person = {
  name,          // 属性简写
  age,
  say() {        // 方法简写
    console.log(`I am ${this.name}`);
  },
  [prop]: 100    // 动态属性名
};

🔹 二、Object 新增的 API 方法

✅ 1. Object.is(a, b)
  • 类似于 ===,但可以区分 +0-0,也能判断 NaN === NaN
vbnet 复制代码
js
复制编辑
Object.is(NaN, NaN); // ✅ true
Object.is(+0, -0);   // ❌ false
✅ 2. Object.assign(target, ...sources)
  • 对象浅拷贝(从后往前合并属性)
css 复制代码
js
复制编辑
const a = { x: 1 };
const b = { y: 2 };
Object.assign(a, b); // a => { x: 1, y: 2 }
✅ 3. Object.keys() / Object.values() / Object.entries()
方法 返回值
Object.keys(obj) 所有可枚举数组
Object.values(obj) 所有可枚举数组
Object.entries(obj) 键值对数组:[key, value]
css 复制代码
js
复制编辑
const obj = { a: 1, b: 2 };
Object.entries(obj); // [['a', 1], ['b', 2]]
✅ 4. Object.getOwnPropertyDescriptors()
  • 获取对象所有属性(包括 getter/setter)的完整描述符
csharp 复制代码
js
复制编辑
const obj = {
  get name() {
    return 'Tom';
  }
};
Object.getOwnPropertyDescriptors(obj);
✅ 5. 原型操作方法
方法 作用
Object.create(proto) 以某对象为原型创建新对象
Object.setPrototypeOf(obj, proto) 设置对象原型
Object.getPrototypeOf(obj) 获取对象原型
ini 复制代码
js
复制编辑
const proto = { greet() { console.log('Hi'); } };
const user = Object.create(proto);
user.greet(); // Hi

🎯 面试答题模板:

ES6 对象扩展主要包括两部分:

一是对象字面量增强语法 ,如属性简写、方法简写、动态属性名;

二是新增的 Object 方法,如 Object.isassignentriescreategetPrototypeOf 等。

这些扩展让对象操作更灵活、更表达式化,也更易与 Map/Set 等数据结构联动使用。

5.你是怎么理解ES6中 Promise的?使⽤场景?

✅ Promise 是一个 构造函数,用来创建"表示未来才会结束的任务"的对象。

基本语法:

javascript 复制代码
js
复制编辑
const promise = new Promise((resolve, reject) => {
  // 异步操作成功时调用 resolve(value)
  // 异步操作失败时调用 reject(reason)
});
状态 说明
pending 初始状态,等待中
fulfilled 成功,调用了 resolve()
rejected 失败,调用了 reject()
  • async/await 的本质和语法糖实现
js 复制代码
function run(generatorFunc) {
  const gen = generatorFunc(); // 拿到生成器对象

  function step(nextF) {
    let next;
    try {
      next = nextF(); // 执行 generator 的 next()
    } catch (e) {
      return Promise.reject(e);
    }

    if (next.done) return Promise.resolve(next.value);
    return Promise.resolve(next.value).then(
      v => step(() => gen.next(v)), // 成功后继续下一步
      e => step(() => gen.throw(e)) // 失败则抛出
    );
  }

  return step(() => gen.next());
}
  • 手写Promise
js 复制代码
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  constructor(executor) {
    this.state = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (this.state === PENDING) {
        this.state = FULFILLED;
        this.value = value;
        this.onFulfilledCallbacks.forEach(fn => fn());
      }
    };

    const reject = (reason) => {
      if (this.state === PENDING) {
        this.state = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };

    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };

    return new MyPromise((resolve, reject) => {
      const handle = () => {
        queueMicrotask(() => {
          try {
            if (this.state === FULFILLED) {
              const x = onFulfilled(this.value);
              resolve(x);
            } else if (this.state === REJECTED) {
              const x = onRejected(this.reason);
              resolve(x);
            }
          } catch (err) {
            reject(err);
          }
        });
      };

      if (this.state === PENDING) {
        this.onFulfilledCallbacks.push(handle);
        this.onRejectedCallbacks.push(handle);
      } else {
        handle();
      }
    });
  }
}
  • Promise.all 等全部成功,Promise.race 谁快听谁的,Promise.allSettled 全部等结果,失败也不怕!
方法 成功条件 失败条件 返回值 常用场景
Promise.all 所有都成功才成功 只要有一个失败就立即失败 成功返回数组结果 并发请求并全部依赖
Promise.race 任意一个先结束就返回(成功或失败) 第一个失败即失败 返回第一个结果 超时控制、竞速请求
Promise.allSettled 都执行完才返回 不会中断失败 每个结果都有状态 展示每个结果是否成功

6. 你是怎么理解ES6中Module的?使⽤场景?

特性 说明
静态加载 编译阶段就确定模块依赖,提高编译效率
按需引入 只加载用到的部分,支持 tree-shaking
默认严格模式 模块默认是 use strict
模块作用域隔离 不会污染全局变量
支持异步动态加载 可通过 import() 动态导入

🧩 常用语法:

✅ 1. 导出模块

javascript 复制代码
js
复制编辑
// 方式一:命名导出
export const name = 'Tom';
export function greet() {}

// 方式二:默认导出
export default function () {
  console.log('我是默认导出的函数');
}

✅ 2. 导入模块

javascript 复制代码
js
复制编辑
// 命名导入(需要用原来的名字)
import { name, greet } from './module.js';

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

// 同时导入
import myFn, { name } from './module.js';

✅ 3. 重命名(防止变量名冲突)

javascript 复制代码
js
复制编辑
import { name as userName } from './module.js';

✅ 4. 导出所有(重新导出)

javascript 复制代码
js
复制编辑
export * from './user.js';

✅ 5. 动态导入(异步)

ini 复制代码
js
复制编辑
import('./module.js').then(mod => {
  mod.doSomething();
});

📌 可用于懒加载路由按需加载


🎯 使用场景:

场景 示例
分模块组织代码 utils.js / api.js / config.js
前端构建工具 Tree-shaking Webpack/Vite 自动去除没用的代码
SSR 服务端渲染项目 Next.js 使用 ES Module
动态导入组件 Vue/React 的异步组件加载
浏览器原生使用 Module <script type="module"> 支持

7. 你是怎么理解ES6中 Generator的?使⽤场景?

  • Generator 是可以暂停执行的函数,用于控制流程,异步处理和状态管理。非常适合处理复杂逻辑逐步执行
js 复制代码
function* generatorFn() {
  yield 1;
  yield 2;
  return 3;
}
// function* 声明一个生成器函数
// yield 语句表示"暂停",下一次调用.next()继续执行
// 返回的是一个 **迭代器对象**
  • 基本用法
js 复制代码
function* gen() {
  console.log('开始');
  yield 'A';
  console.log('中间');
  yield 'B';
  return '结束';
}

const g = gen();
console.log(g.next()); // { value: 'A', done: false }
console.log(g.next()); // { value: 'B', done: false }
console.log(g.next()); // { value: '结束', done: true }

🎯 实际使用场景(超实用):

场景 说明
✅ 异步流程控制 比如连续的请求处理(早期 async/await 替代前)
✅ 按需生成数据 无限序列生成器、分页等
✅ 状态机实现 多状态流程控制(如表单流程)
✅ 自定义遍历器 对象、树结构自定义迭代逻辑
✅ 中间件机制 Koa2 源码中的核心机制就是 Generator

🔁 示例:异步流程控制(模拟 async/await)

scss 复制代码
js
复制编辑
function* asyncFlow() {
  const res1 = yield fetch('/api/user');
  const res2 = yield fetch('/api/order');
  console.log(res1, res2);
}

function run(generator) {
  const it = generator();

  function step(data) {
    const { value, done } = it.next(data);
    if (done) return;
    value.then(step); // value 是 Promise
  }

  step();
}

8. 你是怎么理解ES6中 Decorator(装饰器) 的?使⽤场景?

  • 装饰器是一种函数,用于"增强"类,方法,属性,参数的功能,不改源代码就能添加逻辑,就像给对象贴标签
js 复制代码
@log
class MyClass{}
function log(target : Function){
    console.log('被装饰的类',target.name) // 输出:MyClass
}
// 意思就是把log函数应用到MyClass类上
  • 本质就是一个函数,用来"拦截并增强"类,方法,属性,参数等
  • 它是一个高级的元编程工具 ,让你可以:
    • 在类定义时"动态注入"行为
    • 类似于 AOP(面向切面编程)
    • 编译阶段就可以插入逻辑

🧪 装饰器类型总结(TypeScript 中常见)

类型 作用对象 示例
类装饰器 装饰整个类 @Controller()
属性装饰器 装饰类的某个属性 @Inject()
方法装饰器 装饰类的方法 @Get()
参数装饰器 装饰方法的参数 @Body()
1. 类装饰器(用于增强类本身)
js 复制代码
// 应用装饰器,给UseController添加一个Controller方法
@Controller
class UseController{}

function Controller(target : Function){
    target.property.isController = true
}

// 测试
const uc = new UserController();
console.log(uc.isController); // true ✅
2. 属性装饰器(通常用于依赖注入)
js 复制代码
// 👇 属性装饰器:为属性打标签(可结合元编程)
function Inject(target: any, key: string) {
  console.log(`💉 正在注入属性: ${key} 到类 ${target.constructor.name}`);
  // 这里通常用于框架自动注入依赖
}

class OrderService {
  @Inject
  dbService: any; // 会被装饰器处理
}
3. 方法饰器(拦截函数调用,添加日志等)
js 复制代码
// 👇 方法装饰器:打印调用日志
function Log(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value; // 保存原始方法
  descriptor.value = function (...args: any[]) {
    console.log(`🔍 调用了方法: ${key},参数:`, args);
    return original.apply(this, args); // 执行原方法
  };
  return descriptor;
}

class UserService {
  @Log
  login(username: string, password: string) {
    console.log('🔐 正在执行登录逻辑...');
    return true;
  }
}

// 测试
new UserService().login('admin', '123456');
4. 参数装饰器
js 复制代码
// 👇 参数装饰器:记录第几个参数是 body
function Body(target: any, methodName: string, paramIndex: number) {
  console.log(`📦 方法 ${methodName} 的第 ${paramIndex} 个参数标记为 @Body`);
}

class ApiController {
  create(@Body data: any) {
    console.log('📝 创建数据:', data);
  }
}

// 调用方式
new ApiController().create({ name: 'Tom' });

9. 你是怎么理解ES6新增Set、Map两种数据结构的?

  • Set 是 "无重复数组",Map 是 "键值对的加强版对象",都支持更强的 API 和迭代能力。

📦 一、Set:值唯一的集合

✅ 特点:

特性 说明
✅ 值唯一 自动去重,不能重复
✅ 类型支持 可以存对象、NaN、undefined
✅ 可遍历 支持 for...offorEach

✨ 常用 API:

scss 复制代码
js
复制编辑
const s = new Set([1, 2, 2, 3]);
s.add(4);         // 添加元素
s.has(2);         // 判断是否存在
s.delete(3);      // 删除元素
s.size;           // 获取长度
s.clear();        // 清空集合

// 遍历
for (let item of s) console.log(item);

🎯 使用场景:

场景 说明
✅ 数组去重 Array.from(new Set(arr))
✅ 判断是否访问过 比如 visited URL
✅ 多值唯一集合操作 处理标签、关键字

📦 二、Map:更强大的键值对容器

✅ 特点:

特性 说明
✅ 任意类型作 key 可以是对象、函数等
✅ 有序 遍历顺序就是插入顺序
✅ 不存在原型链干扰 不会和 hasOwnProperty 冲突

✨ 常用 API:

arduino 复制代码
js
复制编辑
const map = new Map();
map.set('name', 'Tom');       // 添加键值对
map.set({ id: 1 }, '对象键'); // 对象作为 key
map.get('name');              // 获取值
map.has('name');              // 是否存在
map.delete('name');           // 删除
map.clear();                  // 清空
map.size;                     // 大小

// 遍历
for (let [key, val] of map) {
  console.log(key, val);
}

🎯 使用场景:

场景 说明
✅ 复杂键值缓存 如对象缓存、LRU
✅ 数据结构优化 避免对象键名只能是字符串
✅ 高性能查找表 比如映射状态码、UI 控件缓存

📊 Set vs Map vs Object 对比:

特性 Object Map Set
字符串/符号 任意类型 无键,只存值
是否有序 无序 有序 有序
是否可迭代 ❌(需额外处理) ✅(直接 for...of)
常用操作 obj[key] .get()/.set() .add()/.has()
重复值 支持 支持 key 唯一 自动去重

🧠 面试答题模板:

ES6 新增的 SetMap 是对传统数组和对象的增强数据结构。

Set 用于存储唯一值,可用于数组去重、状态缓存等;Map 是更灵活的键值对集合,支持任意类型作为键,常用于结构化数据存储、缓存、对象映射等。

相比传统的 Object/Array,它们提供了更强的 API、更高的性能和更准确的数据语义。

10. 你是怎么理解ES6中Proxy的?使⽤场景?

  • Proxy 是"代理"对象的工具,可以拦截和自定义对象的基本操作行为(读写、函数调用、属性判断等) ,像是在对象前面加了一层"中间人"。
  • 例子:拦截属性读写
js 复制代码
class User {
    name:'Ton',
    age:25
}

const proxyUser = new Proxy(user, {
  get(target, key) {
    console.log(`读取属性:${key}`);
    return target[key];
  },
  set(target, key, value) {
    console.log(`设置属性:${key} = ${value}`);
    target[key] = value;
    return true;
  }
});

console.log(proxyUser.name); // 打印读取日志
proxyUser.age = 30;          // 打印设置日志

🧠 Proxy 能拦截哪些操作?(常用 traps)

操作行为 trap 名称 说明
读取属性 get 拦截 obj.prop
设置属性 set 拦截 obj.prop = val
是否存在 has 拦截 'key' in obj
删除属性 deleteProperty 拦截 delete obj.key
枚举属性 ownKeys 拦截 Object.keys()for...in
获取原型 getPrototypeOf 拦截 Object.getPrototypeOf()
调用函数 apply 拦截函数调用(用于函数代理)

🎯 使用场景总结:

场景 应用说明
✅ Vue3 响应式 reactive() 内部用 Proxy 监听对象变化
✅ 表单校验 拦截非法属性设置
✅ 数据保护 防止访问或修改敏感属性
✅ 自动默认值 get 时自动返回默认内容
✅ Mock 数据 拦截访问,返回模拟结果
✅ API 代理 调用远程方法,如 RPC 封装

🛠 示例场景代码

✅ 1. Vue3 响应式核心原理(简化版 reactive)

javascript 复制代码
js
复制编辑
function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      console.log('读取:', key);
      return target[key];
    },
    set(target, key, val) {
      console.log('设置:', key, '=', val);
      target[key] = val;
      // 这里本可以触发视图更新
      return true;
    }
  });
}

const state = reactive({ count: 0 });
state.count++; // 自动追踪、触发更新

✅ 2. 设置限制器(防止非法属性被添加)

javascript 复制代码
js
复制编辑
const safeUser = new Proxy({}, {
  set(target, key, value) {
    if (['name', 'age'].includes(key)) {
      target[key] = value;
      return true;
    } else {
      throw new Error(`❌ 不允许添加属性:${key}`);
    }
  }
});

safeUser.name = 'Alice'; // ✅
safeUser.role = 'admin'; // ❌ 抛出异常

✅ 3. 函数代理(统计调用次数)

javascript 复制代码
js
复制编辑
function sayHi(name) {
  console.log(`Hi, ${name}`);
}

const trackedSayHi = new Proxy(sayHi, {
  apply(target, thisArg, args) {
    console.log('📞 被调用了');
    return target.apply(thisArg, args);
  }
});

trackedSayHi('Tom'); // 输出调用次数 + 原函数逻辑

🧠 面试答题模板:

Proxy 是 ES6 引入的元编程工具,它可以拦截对对象的各种操作(如读写、函数调用、in 判断等),并自定义行为。

它被广泛用于响应式系统(Vue3)、安全控制、Mock 接口、日志追踪等。相比 Object.defineProperty,Proxy 支持更广泛的操作拦截,能完美代理整个对象。

✅ Proxy vs Object.defineProperty 对比:

对比点 Proxy defineProperty
监听属性 ✅ 可监听新增/删除 ❌ 只能监听已有属性
监听数组 ✅ 完整支持 ❌ 不支持索引变化
API 简洁性 ✅ 一个代理搞定所有 ❌ 每个属性都要手动定义
支持能力 ✅ 支持 13 种拦截操作 ❌ 支持少
性能 ✅ 更优(现代浏览器优化)
相关推荐
LaoZhangAI32 分钟前
Kiro vs Cursor:2025年AI编程IDE深度对比
前端·后端
止观止35 分钟前
CSS3 粘性定位解析:position sticky
前端·css·css3
爱编程的喵1 小时前
深入理解JavaScript单例模式:从Storage封装到Modal弹窗的实战应用
前端·javascript
lemon_sjdk1 小时前
Java飞机大战小游戏(升级版)
java·前端·python
G等你下课1 小时前
如何用 useReducer + useContext 构建全局状态管理
前端·react.js
欧阳天羲1 小时前
AI 增强大前端数据加密与隐私保护:技术实现与合规遵
前端·人工智能·状态模式
慧一居士1 小时前
Axios 和Express 区别对比
前端
I'mxx1 小时前
【html常见页面布局】
前端·css·html
万少1 小时前
云测试提前定位和解决问题 萤火故事屋 上架流程
前端·harmonyos·客户端