1. 说说var、let、const之间的区别
- 一句话:
- var 是函数作用域,可以变量提升
- let 和 const 是块级作用域,有暂时性死区,不能重复声明
- const 声明的变量不能修改,但是const 对象的属性可以修改
| 特性 |
var |
let |
const |
| 作用域 |
函数作用域 |
块级作用域 |
块级作用域 |
| 变量提升 |
✅ 是 |
❌ 否 |
❌ 否 |
| 暂时性死区 |
❌ 无 |
✅ 有 |
✅ 有 |
| 重复声明 |
✅ 可以 |
❌ 报错 |
❌ 报错 |
| 是否可修改 |
✅ 可修改 |
✅ 可修改 |
❌ 不可重新赋值(但对象属性可变) |
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
}
console.log(x); // undefined(var 提升)
var x = 5;
console.log(y); // ❌ 报错(let 不提升)
let y = 5;
console.log(a); // 报错 ReferenceError
let a = 10;
// 在声明a之前,a不能访问,这段事件就叫"暂时性死区"
-重复声明
var a = 1;
var a = 2; // ✅ 合法
let b = 1;
let b = 2; // ❌ 报错:Identifier 'b' has already been declared
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; } |
function greet(name = '匿名') {
return `你好,${name}`;
}
greet(); // "你好,匿名"
- 剩余参数...rest (代替arguments)
function sum(...nums) {
return nums.reduce((a, b) => a + b);
}
sum(1, 2, 3); // 6
- 箭头函数 ()=>{}
- 箭头函数没有自己的this,arguments,super,自动继承外层上下文
- 关于this指向:
谁定义就指向谁
function Timer() {
this.count = 0;
setInterval(() => {
this.count++; // this 指向 Timer 实例
}, 1000);
}
4. 函数名属性 function.name
js
复制编辑
function foo() {}
console.log(foo.name); // "foo"
const bar = function () {};
console.log(bar.name); // "bar"(现代浏览器自动推断)
function* gen(){
yield 1;
yield 2;
return 3;
}
const it = gen();
it.next(); // { value: 1, done: false }
补充:ES6+ 的 async/await(ES8)
虽然是 ES8 引入的,但属于函数的重要演进:
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 |
✅ 示例:
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
js
复制编辑
Object.is(NaN, NaN); // ✅ true
Object.is(+0, -0); // ❌ false
✅ 2. Object.assign(target, ...sources)
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] |
js
复制编辑
const obj = { a: 1, b: 2 };
Object.entries(obj); // [['a', 1], ['b', 2]]
✅ 4. Object.getOwnPropertyDescriptors()
- 获取对象所有属性(包括 getter/setter)的完整描述符
js
复制编辑
const obj = {
get name() {
return 'Tom';
}
};
Object.getOwnPropertyDescriptors(obj);
✅ 5. 原型操作方法
| 方法 |
作用 |
Object.create(proto) |
以某对象为原型创建新对象 |
Object.setPrototypeOf(obj, proto) |
设置对象原型 |
Object.getPrototypeOf(obj) |
获取对象原型 |
js
复制编辑
const proto = { greet() { console.log('Hi'); } };
const user = Object.create(proto);
user.greet(); // Hi
🎯 面试答题模板:
ES6 对象扩展主要包括两部分:
一是对象字面量增强语法 ,如属性简写、方法简写、动态属性名;
二是新增的 Object 方法,如 Object.is、assign、entries、create、getPrototypeOf 等。
这些扩展让对象操作更灵活、更表达式化,也更易与 Map/Set 等数据结构联动使用。
5.你是怎么理解ES6中 Promise的?使⽤场景?
✅ Promise 是一个 构造函数,用来创建"表示未来才会结束的任务"的对象。
基本语法:
js
复制编辑
const promise = new Promise((resolve, reject) => {
// 异步操作成功时调用 resolve(value)
// 异步操作失败时调用 reject(reason)
});
| 状态 |
说明 |
pending |
初始状态,等待中 |
fulfilled |
成功,调用了 resolve() |
rejected |
失败,调用了 reject() |
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());
}
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. 导出模块
js
复制编辑
// 方式一:命名导出
export const name = 'Tom';
export function greet() {}
// 方式二:默认导出
export default function () {
console.log('我是默认导出的函数');
}
✅ 2. 导入模块
js
复制编辑
// 命名导入(需要用原来的名字)
import { name, greet } from './module.js';
// 默认导入
import myFn from './module.js';
// 同时导入
import myFn, { name } from './module.js';
✅ 3. 重命名(防止变量名冲突)
js
复制编辑
import { name as userName } from './module.js';
✅ 4. 导出所有(重新导出)
js
复制编辑
export * from './user.js';
✅ 5. 动态导入(异步)
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 是可以暂停执行的函数,用于控制流程,异步处理和状态管理。非常适合处理复杂逻辑逐步执行
function* generatorFn() {
yield 1;
yield 2;
return 3;
}
// function* 声明一个生成器函数
// yield 语句表示"暂停",下一次调用.next()继续执行
// 返回的是一个 **迭代器对象**
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)
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(装饰器) 的?使⽤场景?
- 装饰器是一种函数,用于"增强"类,方法,属性,参数的功能,不改源代码就能添加逻辑,就像给对象贴标签
@log
class MyClass{}
function log(target : Function){
console.log('被装饰的类',target.name) // 输出:MyClass
}
// 意思就是把log函数应用到MyClass类上
- 本质就是一个函数,用来"拦截并增强"类,方法,属性,参数等
- 它是一个高级的元编程工具 ,让你可以:
- 在类定义时"动态注入"行为
- 类似于 AOP(面向切面编程)
- 编译阶段就可以插入逻辑
🧪 装饰器类型总结(TypeScript 中常见)
| 类型 |
作用对象 |
示例 |
| 类装饰器 |
装饰整个类 |
@Controller() |
| 属性装饰器 |
装饰类的某个属性 |
@Inject() |
| 方法装饰器 |
装饰类的方法 |
@Get() |
| 参数装饰器 |
装饰方法的参数 |
@Body() |
1. 类装饰器(用于增强类本身)
// 应用装饰器,给UseController添加一个Controller方法
@Controller
class UseController{}
function Controller(target : Function){
target.property.isController = true
}
// 测试
const uc = new UserController();
console.log(uc.isController); // true ✅
2. 属性装饰器(通常用于依赖注入)
// 👇 属性装饰器:为属性打标签(可结合元编程)
function Inject(target: any, key: string) {
console.log(`💉 正在注入属性: ${key} 到类 ${target.constructor.name}`);
// 这里通常用于框架自动注入依赖
}
class OrderService {
@Inject
dbService: any; // 会被装饰器处理
}
3. 方法饰器(拦截函数调用,添加日志等)
// 👇 方法装饰器:打印调用日志
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. 参数装饰器
// 👇 参数装饰器:记录第几个参数是 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...of、forEach |
✨ 常用 API:
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:
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 新增的 Set 和 Map 是对传统数组和对象的增强数据结构。
Set 用于存储唯一值,可用于数组去重、状态缓存等;Map 是更灵活的键值对集合,支持任意类型作为键,常用于结构化数据存储、缓存、对象映射等。
相比传统的 Object/Array,它们提供了更强的 API、更高的性能和更准确的数据语义。
10. 你是怎么理解ES6中Proxy的?使⽤场景?
- Proxy 是"代理"对象的工具,可以拦截和自定义对象的基本操作行为(读写、函数调用、属性判断等) ,像是在对象前面加了一层"中间人"。
- 例子:拦截属性读写
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)
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. 设置限制器(防止非法属性被添加)
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. 函数代理(统计调用次数)
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 种拦截操作 |
❌ 支持少 |
| 性能 |
✅ 更优(现代浏览器优化) |
❌ |