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;
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
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"(现代浏览器自动推断)
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.is
、assign
、entries
、create
、getPrototypeOf
等。
这些扩展让对象操作更灵活、更表达式化,也更易与 Map/Set 等数据结构联动使用。
5.你是怎么理解ES6中 Promise的?使⽤场景?
✅ Promise 是一个 构造函数 ,用来创建"表示未来才会结束的任务"的对象。
基本语法:
javascript
复制代码
js
复制编辑
const promise = new Promise((resolve, reject) => {
// 异步操作成功时调用 resolve(value)
// 异步操作失败时调用 reject(reason)
});
状态
说明
pending
初始状态,等待中
fulfilled
成功,调用了 resolve()
rejected
失败,调用了 reject()
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());
}
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...of
、forEach
✨ 常用 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 新增的 Set
和 Map
是对传统数组和对象的增强数据结构。
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 种拦截操作
❌ 支持少
性能
✅ 更优(现代浏览器优化)
❌