2026-03-05
测试
00

目录

1. 修改原数组的方法 (Mutator Methods)
2. 返回新数组的方法 (Accessor Methods)
3. 查找与校验 (Search & Check)
4. 遍历与累积 (Iteration & Accumulation)
forEach(fn)
reduce(fn, init)
💡 开发小贴士
🎨 CSS 核心面试题 50 条
一、 基础与选择器 (1-10)
二、 布局与定位 (11-25)
三、 响应式与进阶 (26-35)
四、 动画与视觉效果 (36-45)
五、 渲染原理与工程化 (46-50)
🟢 核心简明答案(精选)
🟡 JavaScript 核心面试题 50 条(精简干货版)
一、 基础类型与内存 (1-10)
二、 函数、作用域与闭包 (11-20)
三、 面向对象与原型链 (21-30)
四、 异步编程 (31-40)
五、 ES6+ 与现代特性 (41-50)
💡 复习建议
🟢 Vue 2 面试经典(50题)
核心原理与响应式 (1-15)
组件与通信 (16-30)
生命周期与周边 (31-50)
🔵 Vue 3 进阶实战(50题)
核心新特性 (1-15)
深入 Composition API (16-30)
生态与工具链 (31-50)
🔵 微信小程序面试题 50 条
一、 核心概念与环境 (1-10)
二、 生命周期 (11-20)
三、 数据传递与通信 (21-30)
四、 性能优化与打包 (31-40)
五、 API 与高级进阶 (41-50)
🟢 核心简明答案(精选)
方案一:重写对象的 toString 或 valueOf
方案二:使用 Object.defineProperty (支持 ===)
方案三:数组的 shift 方法(针对 ==)
总结原理

JavaScript 的数组方法非常强大,可以大致分为 修改原数组返回新数组遍历/查找其他辅助 四类。


1. 修改原数组的方法 (Mutator Methods)

这些方法会直接改变调用它们的数组。

  • push(): 在末尾添加元素,返回新长度。
  • pop(): 删除末尾元素,返回该元素。
  • shift(): 删除首位元素,返回该元素。
  • unshift(): 在首位添加元素,返回新长度。
  • splice(start, count, ...items): 最万能的方法,可以实现删除、插入和替换。
  • sort(): 对数组排序(默认按字符串 Unicode 码点)。
  • reverse(): 反转数组。

2. 返回新数组的方法 (Accessor Methods)

这些方法不会改变原数组,而是返回一个处理后的新数组或结果。

  • map(fn): 对每个元素执行函数,返回由结果组成的新数组。
  • filter(fn): 过滤掉不符合条件的元素,返回新数组。
  • concat(): 合并两个或多个数组。
  • slice(start, end): 浅拷贝数组的一部分。
  • flat(): 将嵌套数组拉平(扁平化)。

3. 查找与校验 (Search & Check)

用于定位元素或判断数组状态。

方法描述
indexOf()返回元素第一次出现的索引,找不到返回 -1。
includes()判断数组是否包含某个值,返回布尔值。
find(fn)返回第一个符合条件的元素
findIndex(fn)返回第一个符合条件的索引
every(fn)是否所有元素都符合条件?
some(fn)是否至少有一个元素符合条件?

4. 遍历与累积 (Iteration & Accumulation)

forEach(fn)

单纯的遍历,没有返回值。适合用来执行一些副作用(如打印、修改外部变量)。

reduce(fn, init)

这是最强大的方法之一,用于将数组减少(归纳)为单一的值(如求和、构建对象)。

注意:map, filter, reduce 这种不会改动原数组的方法,在现代前端开发(尤其是 React/Vue)中非常受欢迎,因为它们遵循函数式编程不可变性的原则。


💡 开发小贴士

  • 想要浅拷贝一个数组? 可以用 [...arr]arr.slice()
  • 想判断一个变量是不是数组? 请使用 Array.isArray(obj),而不是 typeof(后者会返回 "object")。

明白,一次性列出 100 个详细答案会变成“长篇小说”,为了让你看得轻松且能快速记住,我将这些题目精简为核心干货版

你可以把这看作是一份“速查小抄”。


既然你已经有了 Vue 的 100 条题目,JavaScript 作为前端开发的底层基石,这 50 条题目将涵盖从基础语法、异步编程、内存模型到 ES6+ 新特性的完整链路。


既然你已经集齐了 JS 和 Vue 的大招,CSS 作为前端的“门面”,自然也不能落下。CSS 面试题现在的趋势是:不再单纯考属性记忆,而是考布局方案、渲染性能和现代新特性。


🎨 CSS 核心面试题 50 条

一、 基础与选择器 (1-10)

  1. CSS 有哪些基本数据单位?(px, em, rem, vh/vw, %)。
  2. 选择器的优先级是如何计算的?(!important > 内联 > ID > 类/伪类 > 标签 > 通配符)。
  3. 有哪些方式可以隐藏一个元素?display:none, visibility:hidden, opacity:0, clip-path)。
  4. display
    和 visibility
    的区别?
    (前者不占位且触发回流,后者占位仅触发重绘)。
  5. 什么是 CSS 盒模型?(标准盒模型 content-box vs 怪异盒模型 border-box)。
  6. 如何转换盒模型?box-sizing: border-box)。
  7. 什么是块级元素、行内元素、行内块元素?
  8. 为什么会有样式重置(Reset CSS vs Normalize.css)?
  9. 伪类(
    )与 伪元素(::before)的区别?
  10. 如何实现一个三角形?(利用 border 的透明边框)。

二、 布局与定位 (11-25)

  1. CSS 常见的布局方式有哪些?(Float, Flex, Grid, Table, Position)。
  2. Flex 布局中 flex: 1 是哪三个属性的简写?flex-grow, flex-shrink, flex-basis)。
  3. 如何实现元素的水平垂直居中?(Flex, Grid, 绝对定位 + transform)。
  4. 什么是 BFC(块级格式化上下文)?
  5. BFC 的触发条件有哪些?(overflow 不为 visible, position 为 absolute/fixed, flex 等)。
  6. BFC 可以解决什么问题?(外边距塌陷、清除浮动、自适应两栏布局)。
  7. 如何清除浮动(Clearfix)?
  8. 什么是外边距重叠(Margin Collapsing)?
  9. position 的属性值有哪些?(static, relative, absolute, fixed, sticky)。
  10. relative 和 absolute 定位是相对于谁?
  11. sticky(粘性定位)的生效条件是什么?
  12. Flex 布局中 justify-contentalign-items 的区别?
  13. 如何实现两栏布局(左侧固定,右侧自适应)?
  14. 圣杯布局和双飞翼布局的实现原理?
  15. Grid 布局相比 Flex 布局的优势在哪里?

三、 响应式与进阶 (26-35)

  1. 什么是响应式设计?(一套代码适配不同屏幕)。
  2. 媒体查询(Media Queries)的使用方法?
  3. 如何实现移动端 1px 边框?(transform: scale, box-shadow)。
  4. 什么是 rem 布局?原理是什么?(相对于 html 根元素的 font-size)。
  5. vw/vh 布局是什么?(相对于视口宽高)。
  6. 如何处理图片在不同分辨率下的模糊问题?(srcset, image-set)。
  7. z-index 失效的常见原因?(层叠上下文、父元素 position 设置)。
  8. 什么是层叠上下文(Stacking Context)?
  9. 如何实现单行/多行文本溢出省略号?
  10. CSS 变量(CSS Variables/Custom Properties)怎么用?

四、 动画与视觉效果 (36-45)

  1. transition(过渡)和 animation(动画)的区别?
  2. CSS 动画卡顿的原因及优化手段?(使用 transform/opacity,开启 GPU 加速)。
  3. 什么是“硬件加速”(GPU Acceleration)?
  4. transform 常见的变换有哪些?(translate, scale, rotate, skew)。
  5. will-change 属性的作用?
  6. 如何实现一个 0.5px 的线条?
  7. CSS 实现背景毛玻璃效果?backdrop-filter: blur())。
  8. 如何更改 input 输入框光标的颜色?caret-color)。
  9. CSS 绘制扇形或圆环的思路?
  10. 什么是响应式图片中的 object-fit

五、 渲染原理与工程化 (46-50)

  1. 浏览器的渲染流程(关键渲染路径)?
  2. 什么是回流(Reflow)与重绘(Repaint)?
  3. 如何减少回流和重绘?
  4. 什么是 CSS 模块化(CSS Modules, Scoped CSS)?
  5. 常用的 CSS 预处理器(Sass/Less)解决了什么问题?

🟢 核心简明答案(精选)

  • BFC 原理:它是一个独立的渲染区域,内部的子元素不会在布局上影响到外部的元素。可以看作是“装在盒子里的布局”。
  • 1px 边框:最常用的方法是 ::after 伪元素 + transform: scaleY(0.5)
  • 回流 vs 重绘:改变几何属性(宽、高、位置)触发回流;仅改变外观属性(颜色、背景)触发重绘。回流必引发重绘
  • flex: 1:代表 flex-grow: 1(剩余空间撑开)、flex-shrink: 1(空间不足缩放)、flex-basis: 0%(项目基础大小)。

🟡 JavaScript 核心面试题 50 条(精简干货版)

一、 基础类型与内存 (1-10)

  1. 8 种数据类型:Number, String, Boolean, Null, Undefined, Symbol, BigInt 和 Object。
  2. 堆栈内存:基本类型存在“栈”(Stack),引用类型存在“堆”(Heap),栈中只存堆的地址。
  3. typeof 结果typeof nullobject(历史 Bug);typeof functionfunction
  4. 显式类型转换Number(), String(), Boolean()
  5. 隐式类型转换:常见于 + 运算符(字符串拼接)和 == 比较(转为数字)。
  6. NaN 的特性typeof NaN === 'number',且 NaN !== NaN
  7. Object.is():与 === 类似,但处理了 NaN === NaN 为 true 和 +0 === -0 为 false。
  8. null vs undefined:null 表示“空对象”,undefined 表示“变量未定义”。
  9. 0.1 + 0.2 !== 0.3:IEEE 754 浮点数精度问题,解决办法是 toFixed() 或按比例放大成整数。
  10. 深拷贝 vs 浅拷贝:浅拷贝只拷一层引用;深拷贝通过递归或 JSON.parse(JSON.stringify()) 实现。

二、 函数、作用域与闭包 (11-20)

  1. 作用域 (Scope):全局作用域、函数作用域、块级作用域(ES6)。
  2. 作用域链:函数查找变量时,会逐层向上级作用域查找,直到全局。
  3. 闭包 (Closure):函数可以记住并访问其词法作用域,即使函数在作用域之外执行。
  4. 变量提升 (Hoisting)varfunction 声明会提升至作用域顶部,let/const 不会(TDZ)。
  5. 暂时性死区 (TDZ):在 let/const 声明前访问该变量会报错。
  6. this 指向:普通函数看调用者,new 指向新对象,箭头函数看外层作用域。
  7. call / apply / bind:手动改变 this。apply 传数组,call 传参数列表,bind 返回新函数。
  8. 箭头函数特性:没有自己的 this、没有 arguments、不能作为构造函数(无 prototype)。
  9. 高阶函数:接收函数作为参数或返回一个函数的函数(如 map, reduce)。
  10. 函数柯里化 (Currying):将多参数函数转化为一系列单参数函数的过程。

三、 面向对象与原型链 (21-30)

  1. prototype:每个函数都有 prototype 属性,指向其原型对象。
  2. proto:每个对象都有 __proto__ 属性,指向其构造函数的 prototype
  3. 原型链终点Object.prototype.__proto__null
  4. new 操作符步骤:1.创对象 2.连原型 3.绑 this 4.返对象。
  5. instanceof 原理:在原型链上查找是否存在构造函数的 prototype
  6. ES6 Class:本质是构造函数的语法糖,存在变量提升(但不可访问)。
  7. constructor:原型对象上的属性,指向关联的构造函数。
  8. 继承方式:原型链继承、借用构造函数、组合继承、寄生组合继承(最优)。
  9. super 关键字:在子类中调用父类的构造函数或方法。
  10. 静态方法 (static):只能通过类本身访问,不能通过实例访问。

四、 异步编程 (31-40)

  1. 同步 vs 异步:同步阻塞后续任务,异步(回调/Promise)不阻塞。
  2. 事件循环 (Event Loop):JS 引擎不断从任务队列中提取任务到主线程执行的机制。
  3. 宏任务 (Macrotask):setTimeout, setInterval, I/O, UI 渲染。
  4. 微任务 (Microtask):Promise.then, MutationObserver, process.nextTick。
  5. 执行顺序:同步代码 -> 微任务队列 -> 宏任务队列。
  6. Promise 状态:Pending, Fulfilled, Rejected。状态不可逆。
  7. Promise.all:所有成功才成功,一个失败就失败。
  8. Promise.race:谁跑得快就用谁的结果(无论成功失败)。
  9. async / await:Generator 的语法糖,让异步代码看起来像同步。
  10. Generatorfunction* 定义,通过 yield 暂停执行。

五、 ES6+ 与现代特性 (41-50)

  1. 解构赋值:快速从数组或对象中提取值。
  2. 展开运算符 (...):用于数组合并、对象克隆或函数传参。
  3. Set & Map:Set 是类数组去重集合;Map 是键值对集合(键可以是任何类型)。
  4. Proxy:代理对象,用于拦截底层操作(Vue 3 响应式核心)。
  5. Reflect:提供操作对象的统一 API。
  6. Symbol:唯一标识符,常用于对象私有属性,防止属性名冲突。
  7. ES Module (import/export):编译时加载(静态),输出值的引用。
  8. CommonJS (require/exports):运行时加载,输出值的拷贝。
  9. 防抖 (Debounce):n 秒后执行,若期间再次触发则重新计时(如搜索框)。
  10. 节流 (Throttle):n 秒内只执行一次(如页面滚动监听)。

💡 复习建议

  1. 手写代码:特别是 防抖、节流、深拷贝、Promise、new,这些在面试中极大概率要求手写。
  2. 理解 Event Loop:找几个复杂的 setTimeout + Promise 嵌套题练一下。
  3. 对比记忆:比如 var/let/constcall/apply/bindMap/Object 的异同。

如果你需要对其中任何一条(例如第 28 条的继承或第 32 条的 Event Loop)进行代码演示或深度解析,随时告诉我!

🟢 Vue 2 面试经典(50题)

核心原理与响应式 (1-15)

  1. 响应式原理:通过 Object.defineProperty 拦截 getter/setter。
  2. 依赖收集:Getter 中收集 Dep,Setter 中触发 Watcher 更新。
  3. 数组更新:Vue 2 重写了数组的 7 个方法(push, pop 等)来实现响应式。
  4. 对象检测限制:无法检测对象属性的添加或删除(需使用 this.$set)。
  5. 虚拟 DOM:JS 对象层,减少对真实 DOM 的操作频率。
  6. Diff 算法:采用双端比较算法。
  7. v-model 原理:value@input 事件的语法糖。
  8. computed 缓存:只有依赖项改变时才会重新计算。
  9. watch 深度监听deep: true 用于监听对象内部变化。
  10. nextTick 原理:利用微任务(Promise, MutationObserver)或宏任务。
  11. data 必须是函数:防止多个组件实例共用同一个数据对象。
  12. template 编译过程:parse (转 AST) -> optimize (标记静态) -> generate (生成 render)。
  13. **el 与 mountel优先级高于手动mount**:`el` 优先级高于手动 `mount`。
  14. 异步更新队列:Vue 会缓冲同一事件循环中的所有数据变更。
  15. key 的作用:Diff 时的唯一标识,避免节点错误复用。

组件与通信 (16-30)

  1. 父子通信props / $emit
  2. 兄弟通信:Event Bus (事件总线)。
  3. 跨层级通信provide / inject
  4. parent/parent / children:直接访问父子实例。
  5. $refs:获取 DOM 或组件实例。
  6. attrs/attrs / listeners:处理非 props 属性和事件。
  7. 插槽 (Slot):普通插槽、具名插槽、作用域插槽。
  8. keep-alive:缓存组件,有 include/exclude 属性。
  9. mixin:分发组件的可复用功能,但易造成命名冲突。
  10. 动态组件<component :is="xxx">
  11. 递归组件:组件内部调用自己,必须有 name 属性和结束条件。
  12. .sync 修饰符:实现双向绑定的简写。
  13. functional 函数式组件:无状态、无实例,渲染开销低。
  14. extends:组件继承。
  15. Vue.use():安装插件,调用插件的 install 方法。

生命周期与周边 (31-50)

  1. beforeCreate:实例初始化后,数据观测前。
  2. created:实例创建完成,可访问 data,不可操作 DOM。
  3. beforeMount:挂载前,render 函数首次调用。
  4. mounted:挂载完成,可操作 DOM。
  5. beforeUpdate:数据更新,DOM 重新渲染前。
  6. updated:DOM 更新完成。
  7. beforeDestroy:实例销毁前,清理定时器。
  8. destroyed:销毁完成。
  9. v-if vs v-show:前者是条件渲染(销毁/重建),后者是显示隐藏。
  10. v-for 为何不推荐和 v-if 连用:v-for 优先级更高,浪费性能。
  11. 路由 Hash 模式:监听 hashchange 事件。
  12. 路由 History 模式:利用 pushState,需后端配合避免 404。
  13. 导航守卫beforeEach, beforeRouteEnter 等。
  14. Vuex 核心:State, Getter, Mutation, Action, Module。
  15. Mutation 为何必须同步:为了让 devtools 追踪状态快照。
  16. Action 作用:处理异步逻辑,提交 Mutation。
  17. scoped CSS 原理:添加 Data 属性实现样式隔离。
  18. 单向数据流:父传子的 props 不应在子组件修改。
  19. SSR 优势:SEO 友好,首屏渲染快。
  20. 项目优化:路由懒加载、CDN 引入、图片懒加载。

🔵 Vue 3 进阶实战(50题)

核心新特性 (1-15)

  1. Proxy 响应式:支持监听属性新增、删除及数组下标修改。
  2. Composition API:解决逻辑碎片化问题。
  3. setup 函数:组件执行的起点,在 beforeCreate 之前。
  4. ref vs reactiveref 处理基础值/对象,reactive 只处理对象。
  5. toRefs:解构响应式对象而不丢失响应性。
  6. watch vs watchEffect:前者需指定来源,后者自动收集依赖。
  7. Teleport:将 DOM 挂载到外部节点(如 Body)。
  8. Fragment:支持多个根节点。
  9. Suspense:异步组件加载时的占位状态。
  10. v-model 改变:支持多个 v-model,value 改为 modelValue
  11. Tree-shaking:按需引入功能,打包体积更小。
  12. 性能提升:静态提升 (Hoisting) 和 补丁标记 (Patch Flag)。
  13. defineProps / defineEmits<script setup> 模式下的 API。
  14. Custom Renderer API:支持自定义渲染器。
  15. LifeCycle HooksonMounted, onUpdated 等需从 vue 引入。

深入 Composition API (16-30)

  1. shallowRef / shallowReactive:浅层响应式。
  2. triggerRef:手动触发 shallowRef 的更新。
  3. readonly:创建只读的响应式副本。
  4. isRef / isReactive:判断变量类型。
  5. customRef:自定义响应式逻辑(如防抖 ref)。
  6. provide / inject 在 setup 中使用:实现跨层级通信。
  7. useSlots / useAttrs:在 setup 中获取插槽和属性。
  8. CSS 中的 v-bind:在 <style> 中使用 JS 变量。
  9. expose:显式暴露组件内部属性。
  10. Lifecycle 顺序:setup 在所有钩子之前。
  11. Hooks (组合式函数):逻辑封装的最佳实践。
  12. Computed Getter/Setter:计算属性的双向绑定。
  13. watch 后置刷新flush: 'post'
  14. EffectScope:批量收集和清理响应式副作用。
  15. markRaw:标记对象使其永远不转为响应式。

生态与工具链 (31-50)

  1. Vite 原理:利用浏览器原生 ESM,开发环境无需打包。
  2. Pinia 优势:无 Mutation、完善的 TS 支持、更轻量。
  3. Vue Router 4createRouter API,取消了 onReady
  4. createApp:Vue 3 全局配置不再影响所有实例。
  5. 全局 API 移至实例:如 Vue.nextTick 变为 import { nextTick }
  6. Slots 统一:普通插槽和作用域插槽在 Vue 3 中统一。
  7. Data 选项改变:必须始终返回一个函数。
  8. Emits 选项:强制声明组件触发的事件。
  9. 指令生命周期改变:与组件生命周期对齐(如 inserted -> mounted)。
  10. Transition 类名变化v-enter 改为 v-enter-from
  11. 移除 filter:建议使用计算属性或方法代替。
  12. 移除 on/on / off:不再支持内置事件总线。
  13. TypeScript 集成:Vue 3 源码完全由 TS 编写。
  14. Volar:Vue 3 官方推荐的 VS Code 插件。
  15. Devtools 6.0:对 Composition API 的深度支持。
  16. 按需加载组件defineAsyncComponent
  17. SSR 水合 (Hydration):客户端接管服务器渲染的 DOM。
  18. 性能监控performance API 的集成。
  19. 错误处理onErrorCapturedapp.config.errorHandler
  20. 组合式 API 的代码组织:按逻辑功能模块(Feature-based)划分代码。

微信小程序面试题通常围绕着宿主环境、生命周期、双向绑定机制、性能优化、以及与普通 H5 的区别


🔵 微信小程序面试题 50 条

一、 核心概念与环境 (1-10)

  1. 小程序与普通 H5 的区别?(双线程模型、渲染层与逻辑层分离、无法操作 DOM/BOM)。
  2. 小程序的双线程模型是什么?(渲染层 WebView + 逻辑层 JSCore)。
  3. AppID 的作用是什么?(唯一标识、权限控制、云开发绑定)。
  4. 小程序的配置文件有哪些?(app.json, project.config.json, sitemap.json)。
  5. wxml 与 html 的区别?(标签名不同、多了一些指令如 wx:if、自带封装组件)。
  6. wxss 与 css 的区别?(支持 rpx 单位、支持全局/局部样式、仅支持部分 CSS 选择器)。
  7. rpx 单位的原理?(以 750rpx 为屏幕宽度,根据不同设备宽度进行自适应)。
  8. 小程序如何实现数据双向绑定?(通过 this.setData(),而非 Vue 的 Proxy 拦截)。
  9. 为什么不能在逻辑层直接操作 DOM?(因为逻辑层运行在 JSCore 中,没有 DOM API)。
  10. 小程序的运行机制?(冷启动 vs 热启动)。

二、 生命周期 (11-20)

  1. 应用的生命周期有哪些?onLaunch, onShow, onHide, onError)。
  2. 页面的生命周期有哪些?onLoad, onShow, onReady, onHide, onUnload)。
  3. onLoadonShow 的区别?onLoad 只加载一次,onShow 每次切换页面都会触发)。
  4. 组件的生命周期有哪些?created, attached, ready, moved, detached)。
  5. 组件的新生命周期 lifetimes 字段是什么?(推荐的组件生命周期定义方式)。
  6. 父子组件生命周期的触发顺序?
  7. 如何监听页面的下拉刷新?onPullDownRefresh)。
  8. 如何监听页面的触底加载?onReachBottom)。
  9. 页面栈(Page Stack)的最大深度是多少?(10层)。
  10. reLaunchredirectTo 的区别?

三、 数据传递与通信 (21-30)

  1. 页面间跳转如何传递参数?(URL 拼接 query,onLoad 中接收)。
  2. 父子组件之间如何通信?properties 传值,triggerEvent 发射事件)。
  3. 如何获取父/子组件的实例?selectComponent)。
  4. 什么是“事件冒泡”与“事件捕获”?bind vs catch)。
  5. bindtapcatchtap 的区别?catch 会阻止事件冒泡)。
  6. 全局状态管理怎么实现?(App 实例的 globalData 或使用 MobX/Pinia 插件)。
  7. 如何实现组件间的样式隔离?options 中的 addGlobalClassstyleIsolation)。
  8. 什么是模板(template)与插件(plugin)?
  9. Behavior 的作用是什么?(类似 Vue 的 mixin,用于代码复用)。
  10. 如何给事件传参?(通过 data-xxx 属性,在 e.currentTarget.dataset 中获取)。

四、 性能优化与打包 (31-40)

  1. setData 的工作原理及其性能瓶颈?(跨线程通信、频繁调用或大数据量会导致卡顿)。
  2. 小程序包体积限制是多少?(主包 + 分包不超过 20MB,单个包不超过 2MB)。
  3. 什么是分包加载(Subpackaging)?(解决首屏加载慢、包体积受限问题)。
  4. 什么是独立分包?(不依赖主包即可独立运行的分包)。
  5. 分包预下载的策略是什么?
  6. 如何优化长列表渲染?(使用 recycle-view 虚拟列表)。
  7. 小程序图片加载优化的方法?(lazy-load、云开发自动缩略图)。
  8. 什么是小程序的“骨架屏”?
  9. 如何减少渲染层和逻辑层的通信频率?
  10. 如何检查小程序的性能?(开发者工具内置的“体验评分”)。

五、 API 与高级进阶 (41-50)

  1. 小程序如何发起网络请求?wx.request,且必须是 HTTPS)。
  2. 如何处理登录流程?wx.login 获取 code -> 后端换取 openid/session_key)。
  3. openid 和 unionid 的区别?(openid 是应用级唯一,unionid 是开发者账号跨应用唯一)。
  4. 小程序如何实现支付?wx.requestPayment)。
  5. 什么是云开发(Cloud Development)?(无需后端服务器,直接操作云数据库、云函数)。
  6. 小程序如何跳转到另一个小程序?wx.navigateToMiniProgram)。
  7. Web-view 组件的作用及限制?(展示 H5 页面,JSSDK 通信限制)。
  8. 小程序的分享功能如何实现?onShareAppMessage)。
  9. 如何通过扫描二维码进入特定页面并携带参数?
  10. 什么是自定义 Tabbar?

🟢 核心简明答案(精选)

  • 双线程原理:JS 逻辑跑在 A 线程,CSS/WXML 渲染在 B 线程(WebView)。两者通过 native(微信客户端)中转,用 JSON 进行通信。这也是为什么频繁 setData 会卡,因为“搬运数据”很累。
  • setData 优化:不要把不需要渲染的变量放进 data;不要频繁调用;更新对象时建议只更新变化的路径(如 this.setData({ 'user.age': 18 }))。
  • 登录闭环:前端拿 code -> 后端拿 code 去微信服务器换 openid -> 后端签发自己的 token 给前端 -> 前端存 storage

方案一:重写对象的 toStringvalueOf

在 JS 中,当一个对象与一个原始值进行比较或运算时,会触发隐式类型转换。它会尝试调用对象的 valueOf()toString() 方法。

javascript
let a = { value: 1, // 每次被调用时,返回当前值并自增 toString() { return this.value++; } }; if (a == 1 && a == 2) { console.log("成功了!"); }
  • 原理:当执行 a == 1 时,JS 发现 a 是对象,于是调用 a.toString(),得到 1,此时 value 变成 2。下一次执行 a == 2 时,再次调用,得到 2
  • 注意:这种方案在 ===(严格相等)下会失效,因为 === 不会触发隐式转换。

方案二:使用 Object.defineProperty (支持 ===)

如果你面试时考官明确要求必须使用 ===,那么你需要利用 数据劫持,在全局对象(浏览器里是 window)上定义一个属性。

javascript
let _value = 1; Object.defineProperty(window, 'a', { get() { return _value++; } }); if (a === 1 && a === 2) { console.log("严格相等也成功了!"); }
  • 原理:每次访问变量 a 时,都会触发 get 拦截器。第一次读取 a 返回 1 并让内部变量加 1,第二次读取自然就变成了 2
  • 适用场景:这是目前最标准、能绕过严格相等检查的解法。

方案三:数组的 shift 方法(针对 ==

数组也是对象,我们可以巧妙利用数组的 toString 默认会调用 join 的特性。

javascript
let a = [1, 2]; a.join = a.shift; // 将 join 指向 shift if (a == 1 && a == 2) { console.log("数组解法成功!"); }

总结原理

这类题目的核心原理有两个:

  1. 隐式转换控制:利用对象在参与运算时会顺序调用 Symbol.toPrimitive -> valueOf -> toString 的特性。
  2. 属性拦截(Getter):利用 ES6 的 Proxy 或 ES5 的 Object.defineProperty 劫持成员访问,实现每次读取返回不同值。

这类题目虽然在实际开发中是“毒瘤”代码,但面试官喜欢用它来试探你对 JS 底层机制的掌握程度。你还想看看关于 Proxy 如何实现这个效果的代码吗?

这就是 100 条 Vue 重点题! 建议你先通读一遍,把不理解的概念标出来。 需要我针对其中某一条(比如 Vue 3 的 Patch Flag 到底是什么)给你做一个详细的图解或代码示例吗?

一、基础概念与数据类型

  1. JavaScript 有哪些数据类型? 答案:

基本类型:String、Number、Boolean、null、undefined、Symbol(ES6)、BigInt(ES2020)

引用类型:Object(包括 Array、Function、Date、RegExp 等)

  1. null 和 undefined 的区别? 答案:

undefined:变量已声明但未赋值

null:表示空值,是一个可以赋给变量的特殊值

typeof null 返回 “object”,这是历史遗留 bug

  1. == 和 === 的区别? 答案:

==:宽松相等,会进行类型转换

===:严格相等,不会进行类型转换

1 == '1' // true 1 === '1' // false AI写代码 javascript 运行 1 2

  1. 什么是变量提升? 答案:

var 声明的变量会提升到作用域顶部

只有声明会提升,赋值不会提升

let 和 const 存在暂时性死区,不会提升

  1. let、const、var 的区别? JavaScript 面试常见50题及答案

一、基础概念与数据类型

  1. JavaScript 有哪些数据类型? 答案:

基本类型:String、Number、Boolean、null、undefined、Symbol(ES6)、BigInt(ES2020)

引用类型:Object(包括 Array、Function、Date、RegExp 等)

  1. null 和 undefined 的区别? 答案:

undefined:变量已声明但未赋值

null:表示空值,是一个可以赋给变量的特殊值

typeof null 返回 “object”,这是历史遗留 bug

  1. == 和 === 的区别? 答案:

==:宽松相等,会进行类型转换

===:严格相等,不会进行类型转换

1 == '1' // true 1 === '1' // false AI写代码 javascript 运行 1 2

  1. 什么是变量提升? 答案:

var 声明的变量会提升到作用域顶部

只有声明会提升,赋值不会提升

let 和 const 存在暂时性死区,不会提升

  1. let、const、var 的区别?

  2. 什么是作用域链? 答案:

函数在查找变量时,先从自身作用域查找

找不到则向父级作用域查找,直到全局作用域

这种链式查找关系称为作用域链

  1. 什么是闭包 ? 答案:

函数嵌套函数,内部函数可以访问外部函数的变量

外部函数执行完毕后,其变量仍然被内部函数引用

常见用途:私有变量、函数工厂、模块模式

  1. 闭包的优缺点? 答案: 优点:

创建私有变量和方法

实现函数柯里化

模块化开发

缺点:

内存泄漏(如果闭包引用不被释放)

性能考虑(每次创建函数都会创建闭包)

  1. 解释词法作用域? 答案:

JavaScript 采用词法作用域(静态作用域)

作用域在函数定义时就确定了,而不是调用时

与动态作用域相对

二、原型与继承 10. 什么是原型链? 答案:

每个对象都有 proto 属性,指向其构造函数的 prototype

prototype 也是对象,也有 proto,形成链式结构

查找属性时,沿着原型链向上查找

  1. 如何实现继承? 答案:

原型链继承:

function Parent() {} function Child() {} Child.prototype = new Parent() AI写代码 javascript 运行 1 2 3 构造函数继承:

function Child() { Parent.call(this) } AI写代码 javascript 运行 1 2 3 组合继承(最常用):

function Child() { Parent.call(this) } Child.prototype = Object.create(Parent.prototype) Child.prototype.constructor = Child AI写代码 javascript 运行 1 2 3 4 5 Class 继承(ES6):

class Child extends Parent { constructor() { super() } } AI写代码 javascript 运行 1 2 3 4 5

  1. new 操作符做了什么? 答案:

创建一个空对象

将空对象的 proto 指向构造函数的 prototype

将 this 指向这个空对象

执行构造函数

如果构造函数返回对象则返回该对象,否则返回新对象

  1. instanceof 的原理? 答案:

检查右边构造函数的 prototype 是否在左边对象的原型链上

实现原理:

function myInstanceof(left, right) { let proto = Object.getPrototypeOf(left) while (true) { if (proto === null) return false if (proto === right.prototype) return true proto = Object.getPrototypeOf(proto) } } AI写代码 javascript 运行 1 2 3 4 5 6 7 8 三、函数与 this

  1. 解释 this 的指向? 答案:

普通函数调用:this 指向全局对象(严格模式下为 undefined)

方法调用:this 指向调用该方法的对象

构造函数调用:this 指向新创建的实例

call/apply/bind 调用:this 指向第一个参数

箭头函数:this 指向定义时的上下文,不会改变

  1. call、apply、bind 的区别? 答案:

func.call(thisArg, arg1, arg2, ...) // 参数逐个传递 func.apply(thisArg, [argsArray]) // 参数作为数组传递 func.bind(thisArg, arg1, arg2, ...) // 返回新函数,不立即执行 AI写代码 javascript 运行 1 2 3

  1. 箭头函数与普通函数的区别? 答案:

箭头函数没有自己的 this,继承外层

箭头函数没有 arguments 对象

箭头函数不能作为构造函数(不能用 new)

箭头函数没有 prototype 属性

箭头函数不能使用 yield,不能用作生成器

  1. 什么是高阶函数? 答案:

接受函数作为参数

或返回一个函数

例如:map、filter、reduce、bind

异步编程

  1. 什么是事件循环? 答案:

JavaScript 是单线程的,通过事件循环处理异步

任务分为宏任务和微任务

执行顺序:同步代码 → 微任务 → 宏任务

宏任务:setTimeout、setInterval、I/O

微任务:Promise.then、process.nextTick、MutationObserver

  1. Promise 的状态? 答案:

pending:初始状态

fulfilled:操作成功完成

rejected:操作失败

状态一旦改变就不会再变

  1. Promise 的常用方法? 答案:

Promise.resolve(value) // 返回 resolved 状态的 Promise Promise.reject(reason) // 返回 rejected 状态的 Promise Promise.all(iterable) // 所有成功才成功,一个失败就失败 Promise.race(iterable) // 第一个改变状态的 Promise 决定结果 Promise.allSettled(iterable) // 所有 Promise 都完成后返回结果数组 AI写代码 javascript 运行 1 2 3 4 5

  1. async/await 的优点? 答案:

代码更简洁,类似同步写法

更好的错误处理(可以使用 try-catch)

更容易调试

避免回调地狱

  1. 实现一个 Promise? 答案:

class MyPromise { constructor(executor) { this.state = 'pending' this.value = undefined this.reason = undefined

const resolve = (value) => { if (this.state === 'pending') { this.state = 'fulfilled' this.value = value } } const reject = (reason) => { if (this.state === 'pending') { this.state = 'rejected' this.reason = reason } } try { executor(resolve, reject) } catch (err) { reject(err) } } then(onFulfilled, onRejected) { // 简化实现,实际更复杂 if (this.state === 'fulfilled') { onFulfilled(this.value) } if (this.state === 'rejected') { onRejected(this.reason) } }

} AI写代码 javascript 运行

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 四、ES6+ 新特性

  1. 解构赋值的用途? 答案:

// 数组解构 const [a, b] = [1, 2]

// 对象解构 const { name, age } = { name: 'John', age: 30 }

// 函数参数解构 function foo({ x, y }) { return x + y }

// 交换变量 [a, b] = [b, a] AI写代码 javascript 运行

1 2 3 4 5 6 7 8 9 10 11

  1. 扩展运算符的用途? 答案:

// 复制数组 const arr2 = [...arr1]

// 合并数组 const arr3 = [...arr1, ...arr2]

// 函数参数 Math.max(...numbers)

// 对象浅拷贝 const obj2 = { ...obj1 } AI写代码 javascript 运行

1 2 3 4 5 6 7 8 9 10 11

  1. 模板字符串的特性? 答案:

// 支持换行 const str = 第一行 第二行

// 支持表达式 const name = 'John' const greeting = Hello, ${name}!

// 标签模板 function tag(strings, ...values) { // strings: 模板字符串的静态部分 // values: 表达式的值 } AI写代码 javascript 运行

1 2 3 4 5 6 7 8 9 10 11 12 13

  1. Symbol 的作用? 答案:

创建唯一的值,避免属性名冲突

可用作对象的私有属性

内置 Symbol 值如 Symbol.iterator、Symbol.toStringTag

  1. Set 和 Map 的区别? 答案:

Set:值的集合,值唯一

Map:键值对的集合,键可以是任意类型

WeakSet:弱引用集合,只能存对象

WeakMap:弱引用键值对,键只能是对象

五、DOM 与 BOM 28. 事件委托是什么? 答案:

将事件监听器绑定到父元素

利用事件冒泡机制处理子元素事件

优点:减少内存消耗,动态元素也能处理

  1. 事件冒泡和事件捕获? 答案:

事件冒泡:从目标元素向上传播到根元素

事件捕获:从根元素向下传播到目标元素

DOM 事件流:捕获 → 目标 → 冒泡

addEventListener 第三个参数控制阶段

  1. 阻止事件默认行为和冒泡? 答案:

event.preventDefault() // 阻止默认行为 event.stopPropagation() // 阻止冒泡 event.stopImmediatePropagation() // 阻止同一事件的其他监听器 AI写代码 javascript 运行 1 2 3 六、性能与安全

  1. 什么是防抖和节流? 答案:

防抖:事件触发后延迟执行,如果期间再次触发则重新计时

节流:在一定时间内只执行一次

// 防抖 function debounce(fn, delay) { let timer return function(...args) { clearTimeout(timer) timer = setTimeout(() => fn.apply(this, args), delay) } }

// 节流 function throttle(fn, delay) { let lastTime = 0 return function(...args) { const now = Date.now() if (now - lastTime >= delay) { fn.apply(this, args) lastTime = now } } } AI写代码 javascript 运行

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

  1. 什么是跨域?如何解决? 答案:

浏览器同源策略限制

解决方案:

JSONP(仅 GET 请求)

CORS(服务器设置响应头)

代理服务器

postMessage

WebSocket AI写代码 bash 1 2 3 4 5 6 7 8 9

  1. 什么是 XSS 攻击?如何防范? 答案:

跨站脚本攻击,注入恶意脚本

防范措施:

输入过滤和转义

设置 HttpOnly Cookie

使用 CSP(内容安全策略)

避免内联事件处理

  1. 什么是 CSRF 攻击?如何防范? 答案:

跨站请求伪造,诱导用户发送恶意请求

防范措施:

使用 CSRF Token

验证 Referer 头

设置 SameSite Cookie

验证码

七、算法与数据结构 35. 数组去重的方法? 答案:

// 1. Set [...new Set(array)]

// 2. filter + indexOf array.filter((item, index) => array.indexOf(item) === index)

// 3. reduce array.reduce((acc, cur) => acc.includes(cur) ? acc : [...acc, cur], []) AI写代码 javascript 运行 1 2 3 4 5 6 7 8

  1. 数组扁平化的方法? 答案:

// 1. flat arr.flat(Infinity)

// 2. reduce + 递归 function flatten(arr) { return arr.reduce((acc, cur) => Array.isArray(cur) ? acc.concat(flatten(cur)) : acc.concat(cur), []) }

// 3. toString(仅限数字数组) arr.toString().split(',').map(Number) AI写代码 javascript 运行

1 2 3 4 5 6 7 8 9 10 11 12 13 14

  1. 深拷贝的实现? 答案:

function deepClone(obj, map = new WeakMap()) { if (obj === null || typeof obj !== 'object') return obj if (map.has(obj)) return map.get(obj)

const clone = Array.isArray(obj) ? [] : {} map.set(obj, clone) for (let key in obj) { if (obj.hasOwnProperty(key)) { clone[key] = deepClone(obj[key], map) } } return clone

} AI写代码 javascript 运行

1 2 3 4 5 6 7 8 9 10 11 12 13 14 八、 浏览器相关

  1. 从输入 URL 到页面显示的过程? 答案:

DNS 解析

TCP 连接

发送 HTTP 请求

服务器处理请求并返回响应

浏览器解析渲染

连接结束

  1. 重排和重绘? 答案:

重排:布局改变,需要重新计算

重绘:外观改变,不需要重新布局

优化:避免频繁操作 DOM,使用 transform 和 opacity

  1. 内存泄漏的原因? 答案:

意外的全局变量

未清理的定时器

闭包滥用

未解绑的事件监听

DOM 引用未清除

九、模块化 41. CommonJS 和 ES6 Module 的区别?

  1. AMD 和 CMD 的区别? 答案:

AMD:异步加载,提前执行(RequireJS)

CMD:异步加载,延迟执行(SeaJS)

现在多用 ES6 Module

其他重要概念

  1. 什么是柯里化? 答案:

将多参数函数转化为单参数函数序列

function curry(fn) { return function curried(...args) { if (args.length >= fn.length) { return fn.apply(this, args) } else { return function(...args2) { return curried.apply(this, args.concat(args2)) } } } } AI写代码 javascript 运行

1 2 3 4 5 6 7 8 9 10 11

  1. 什么是函数组合? 答案:

将多个函数组合成一个新函数

function compose(...fns) { return function(x) { return fns.reduceRight((acc, fn) => fn(acc), x) } } AI写代码 javascript 运行 1 2 3 4 5

  1. 什么是尾调用优化? 答案:

函数最后一步调用另一个函数

ES6 严格模式下支持

可以优化递归性能

  1. Generator 函数的作用? 答案:

可以暂停执行和恢复执行

配合 yield 使用

常用于异步编程(async/await 的基础)

function* gen() { yield 1 yield 2 return 3 } AI写代码 javascript 运行 1 2 3 4 5

  1. Proxy 和 Reflect? 答案:

Proxy:代理对象,拦截操作

Reflect:操作对象的 API,与 Proxy 对应

const proxy = new Proxy(target, { get(target, prop) { return Reflect.get(target, prop) } }) AI写代码 javascript 运行 1 2 3 4 5

  1. WeakMap 和 WeakSet 的特点? 答案:

弱引用,不影响垃圾回收

键/值必须是对象

不可遍历

没有 size 属性

  1. 什么是 Optional Chaining? 答案:

ES2020 新增,可选链操作符 ?.

避免访问嵌套对象时的空值错误

const name = obj?.user?.name AI写代码 bash 1

  1. 什么是 Nullish Coalescing? 答案:

ES2020 新增,空值合并操作符 ??

仅在左侧为 null 或 undefined 时返回右侧

const value = a ?? 'default'

一、JavaScript基础(50题)

  1. 数据类型与类型判断

题目1:JavaScript中有哪些数据类型?如何判断一个变量的类型?

答案:JavaScript中的数据类型分为基本类型和引用类型。基本类型包括:string、number、boolean、null、undefined、symbol、bigint。引用类型包括:object、array、function等。 javascript 体验AI代码助手 代码解读复制代码console.log(typeof 42); // "number" console.log(typeof "hello"); // "string" console.log(typeof null); // "object" console.log(Object.prototype.toString.call([])); // "[object Array]"

题目2:null和undefined的区别是什么?

答案:

undefined表示变量已声明但未赋值。 null表示一个空对象引用,通常用于显式清空变量。

javascript 体验AI代码助手 代码解读复制代码let a; console.log(a); // undefined let b = null; console.log(b); // null

题目3:如何判断一个变量是否是数组?

答案:使用Array.isArray()方法。 javascript 体验AI代码助手 代码解读复制代码console.log(Array.isArray([])); // true console.log(Array.isArray({})); // false

题目4:如何判断一个变量是否是对象?

答案:使用typeof和Object.prototype.toString.call()。 javascript 体验AI代码助手 代码解读复制代码function isObject(obj) { return obj !== null && typeof obj === 'object'; } console.log(isObject({})); // true console.log(isObject([])); // true console.log(isObject(null)); // false

题目5:如何判断一个变量是否是函数?

答案:使用typeof。 javascript 体验AI代码助手 代码解读复制代码console.log(typeof function() {}); // "function"

题目6:如何判断一个变量是否是正则表达式?

答案:使用Object.prototype.toString.call()。 javascript 体验AI代码助手 代码解读复制代码console.log(Object.prototype.toString.call(/abc/) === "[object RegExp]"); // true

题目7:如何判断一个变量是否是日期对象?

答案:使用Object.prototype.toString.call()。 javascript 体验AI代码助手 代码解读复制代码console.log(Object.prototype.toString.call(new Date()) === "[object Date]"); // true

题目8:如何判断一个变量是否是NaN?

答案:使用isNaN()或Number.isNaN()。 javascript 体验AI代码助手 代码解读复制代码console.log(isNaN(NaN)); // true console.log(Number.isNaN(NaN)); // true

题目9:如何判断一个变量是否是有限数?

答案:使用isFinite()。 javascript 体验AI代码助手 代码解读复制代码console.log(isFinite(42)); // true console.log(isFinite(Infinity)); // false

题目10:如何判断一个变量是否是BigInt?

答案:使用typeof。 javascript 体验AI代码助手 代码解读复制代码console.log(typeof 42n); // "bigint"

  1. 作用域与闭包

题目11:什么是作用域链?JavaScript是如何查找变量的?

答案:作用域链是JavaScript中用于查找变量的一种机制。当访问一个变量时,JavaScript引擎会首先在当前作用域中查找,如果找不到,就会沿着作用域链向上查找,直到全局作用域。 javascript 体验AI代码助手 代码解读复制代码let globalVar = "global"; function outer() { let outerVar = "outer"; function inner() { let innerVar = "inner"; console.log(globalVar); // "global" console.log(outerVar); // "outer" console.log(innerVar); // "inner" } inner(); } outer();

题目12:什么是闭包?闭包有哪些应用场景?

答案:闭包是指函数能够访问其词法作用域中的变量,即使函数在其词法作用域之外执行。 javascript 体验AI代码助手 代码解读复制代码function createCounter() { let count = 0; return function() { count++; console.log(count); }; } const counter = createCounter(); counter(); // 1 counter(); // 2

题目13:闭包会导致内存泄漏吗?如何避免?

答案:闭包可能导致内存泄漏,因为闭包会保留对其词法作用域的引用。避免方法包括:

及时清除不再使用的闭包。 使用弱引用(如WeakMap、WeakSet)。

题目14:如何实现一个私有变量?

答案:使用闭包。 javascript 体验AI代码助手 代码解读复制代码function createPrivateVar() { let privateVar = 0; return { get: function() { return privateVar; }, set: function(value) { privateVar = value; } }; } const obj = createPrivateVar(); obj.set(42); console.log(obj.get()); // 42

题目15:什么是立即执行函数表达式(IIFE)?有什么作用?

答案:IIFE是一个定义后立即执行的函数,用于创建独立的作用域。 javascript 体验AI代码助手 代码解读复制代码(function() { let localVar = "local"; console.log(localVar); // "local" })();

题目16:如何实现一个模块模式?

答案:使用IIFE和闭包。 javascript 体验AI代码助手 代码解读复制代码const module = (function() { let privateVar = 0; return { get: function() { return privateVar; }, set: function(value) { privateVar = value; } }; })(); module.set(42); console.log(module.get()); // 42

题目17:如何实现一个单例模式?

答案:使用IIFE和闭包。 javascript 体验AI代码助手 代码解读复制代码const singleton = (function() { let instance; function init() { return { value: 42 }; } return { getInstance: function() { if (!instance) { instance = init(); } return instance; } }; })(); const instance1 = singleton.getInstance(); const instance2 = singleton.getInstance(); console.log(instance1 === instance2); // true

题目18:如何实现一个工厂模式?

答案:使用函数返回对象。 javascript 体验AI代码助手 代码解读复制代码function createPerson(name, age) { return { name, age, sayHello: function() { console.log(Hello, my name is ${this.name}); } }; } const person = createPerson("Alice", 25); person.sayHello(); // "Hello, my name is Alice"

题目19:如何实现一个观察者模式?

答案:使用数组存储观察者,并提供订阅和发布方法。 javascript 体验AI代码助手 代码解读复制代码function Subject() { this.observers = []; } Subject.prototype.subscribe = function(observer) { this.observers.push(observer); }; Subject.prototype.unsubscribe = function(observer) { this.observers = this.observers.filter((obs) => obs !== observer); }; Subject.prototype.notify = function() { this.observers.forEach((observer) => observer.update()); }; function Observer(name) { this.name = name; } Observer.prototype.update = function() { console.log(${this.name} received an update); }; const subject = new Subject(); const observer1 = new Observer("Observer 1"); const observer2 = new Observer("Observer 2"); subject.subscribe(observer1); subject.subscribe(observer2); subject.notify(); // "Observer 1 received an update" "Observer 2 received an update"

题目20:如何实现一个发布-订阅模式?

答案:使用对象存储事件和回调函数。 javascript 体验AI代码助手 代码解读复制代码function PubSub() { this.events = {}; } PubSub.prototype.subscribe = function(event, callback) { if (!this.events[event]) { this.events[event] = []; } this.events[event].push(callback); }; PubSub.prototype.publish = function(event, data) { if (this.events[event]) { this.events[event].forEach((callback) => callback(data)); } }; const pubsub = new PubSub(); pubsub.subscribe("event1", (data) => { console.log(Event1 received: ${data}); }); pubsub.publish("event1", "Hello"); // "Event1 received: Hello"

  1. 原型与继承

题目21:如何实现JavaScript中的继承?有哪些方式?

答案:JavaScript中的继承可以通过以下几种方式实现:

原型链继承:通过将子类的原型指向父类的实例来实现继承。 构造函数继承:在子类构造函数中调用父类构造函数。 组合继承:结合原型链继承和构造函数继承。 原型式继承:使用Object.create()方法。 寄生式继承:在原型式继承的基础上增强对象。 寄生组合式继承:结合组合继承和寄生式继承,是目前最常用的继承方式。

javascript 体验AI代码助手 代码解读复制代码// 寄生组合式继承 function Parent(name) { this.name = name; } Parent.prototype.sayHello = function() { console.log(Hello, ${this.name}); }; function Child(name, age) { Parent.call(this, name); this.age = age; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; const child = new Child("Alice", 10); child.sayHello(); // "Hello, Alice"

题目22:__proto__和prototype的区别是什么?

答案:

prototype是函数对象的一个属性,指向该函数的原型对象。 __proto__是对象实例的一个属性,指向该对象的原型。

javascript 体验AI代码助手 代码解读复制代码function Person() {} const person = new Person(); console.log(person.proto === Person.prototype); // true

题目23:如何实现一个深拷贝函数?

答案: javascript 体验AI代码助手 代码解读复制代码function deepClone(obj) { if (obj === null || typeof obj !== 'object') { return obj; } let clone = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { clone[key] = deepClone(obj[key]); } } return clone; }

题目24:什么是原型链?如何避免原型链污染?

答案:原型链是JavaScript中对象继承的机制。避免原型链污染的方法包括:

使用Object.create(null)创建无原型的对象。 避免直接修改Object.prototype。

题目25:如何判断一个对象是否是另一个对象的实例?

答案:使用instanceof。 javascript 体验AI代码助手 代码解读复制代码function Person() {} const person = new Person(); console.log(person instanceof Person); // true

二、ES6+新特性(50题)

  1. let、const与块级作用域

题目26:let和const与var的区别是什么?

答案:

var存在变量提升,而let和const不会。 var声明的变量属于函数作用域,而let和const属于块级作用域。 const声明的变量必须初始化,且不能重新赋值。

javascript 体验AI代码助手 代码解读复制代码if (true) { var a = 1; let b = 2; const c = 3; } console.log(a); // 1 console.log(b); // ReferenceError console.log(c); // ReferenceError

题目27:什么是暂时性死区(TDZ)?

答案:在块级作用域中,let和const声明的变量在声明之前不可访问,这种现象称为暂时性死区。 javascript 体验AI代码助手 代码解读复制代码console.log(a); // ReferenceError let a = 1;

题目28:如何实现块级作用域?

答案:使用let或const。 javascript 体验AI代码助手 代码解读复制代码{ let a = 1; console.log(a); // 1 } console.log(a); // ReferenceError

题目29:const声明的对象可以修改吗?

答案:可以修改对象的属性,但不能重新赋值。 javascript 体验AI代码助手 代码解读复制代码const obj = { a: 1 }; obj.a = 2; console.log(obj.a); // 2 obj = {}; // TypeError

题目30:如何冻结一个对象?

答案:使用Object.freeze()。 javascript 体验AI代码助手 代码解读复制代码const obj = { a: 1 }; Object.freeze(obj); obj.a = 2; // 静默失败 console.log(obj.a); // 1

  1. 箭头函数

题目31:箭头函数与普通函数的区别是什么?

答案:

箭头函数没有自己的this,它的this继承自外层作用域。 箭头函数不能作为构造函数使用,不能使用new关键字。 箭头函数没有arguments对象,但可以使用rest参数。

javascript 体验AI代码助手 代码解读复制代码const obj = { value: 42, getValue: function() { return this.value; }, getValueArrow: () => { return this.value; } }; console.log(obj.getValue()); // 42 console.log(obj.getValueArrow()); // undefined

题目32:箭头函数能否使用yield关键字?

答案:不能,箭头函数不能用作生成器函数。

题目33:如何实现一个柯里化函数?

答案: javascript 体验AI代码助手 代码解读复制代码function curry(fn) { return function curried(...args) { if (args.length >= fn.length) { return fn.apply(this, args); } else { return function(...moreArgs) { return curried.apply(this, args.concat(moreArgs)); }; } }; } const add = (a, b, c) => a + b + c; const curriedAdd = curry(add); console.log(curriedAdd(1)(2)(3)); // 6

题目34:如何实现一个偏函数?

答案: javascript 体验AI代码助手 代码解读复制代码function partial(fn, ...args) { return function(...moreArgs) { return fn.apply(this, args.concat(moreArgs)); }; } const add = (a, b, c) => a + b + c; const partialAdd = partial(add, 1, 2); console.log(partialAdd(3)); // 6

题目35:如何实现一个惰性函数?

答案: javascript 体验AI代码助手 代码解读复制代码function lazyFunction() { let result; return function() { if (result === undefined) { result = computeExpensiveValue(); } return result; }; }

三、异步编程与事件循环(50题)

  1. Promise与异步编程

题目36:什么是Promise?如何实现一个Promise?

答案:Promise是ES6引入的一种异步编程解决方案,用于处理异步操作。 javascript 体验AI代码助手 代码解读复制代码const promise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Success!"); }, 1000); }); promise.then((result) => { console.log(result); // "Success!" });

题目37:async/await与Promise的关系是什么?

答案:async/await是ES8引入的语法糖,用于简化Promise的使用。 javascript 体验AI代码助手 代码解读复制代码async function fetchData() { const response = await fetch("https://api.example.com/data"); const data = await response.json(); console.log(data); } fetchData();

题目38:如何实现一个Promise.all?

答案: javascript 体验AI代码助手 代码解读复制代码function promiseAll(promises) { return new Promise((resolve, reject) => { let results = []; let completed = 0; promises.forEach((promise, index) => { promise.then((result) => { results[index] = result; completed++; if (completed === promises.length) { resolve(results); } }).catch(reject); }); }); }

题目39:如何实现一个Promise.race?

答案: javascript 体验AI代码助手 代码解读复制代码function promiseRace(promises) { return new Promise((resolve, reject) => { promises.forEach((promise) => { promise.then(resolve).catch(reject); }); }); }

题目40:如何处理Promise的错误?

答案:使用catch或try/catch。 javascript 体验AI代码助手 代码解读复制代码promise.catch((error) => { console.error(error); });

async function fetchData() { try { const response = await fetch("https://api.example.com/data"); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } }

  1. 事件循环

题目41:什么是事件循环(Event Loop)?请描述其工作原理。

答案:事件循环是JavaScript运行时处理异步任务的一种机制。 javascript 体验AI代码助手 代码解读复制代码console.log("Start"); setTimeout(() => { console.log("Timeout"); }, 0); Promise.resolve().then(() => { console.log("Promise"); }); console.log("End"); // 输出顺序:Start -> End -> Promise -> Timeout

题目42:什么是宏任务和微任务?请举例说明。

答案:宏任务包括setTimeout、setInterval、I/O操作等,微任务包括Promise回调、MutationObserver等。 javascript 体验AI代码助手 代码解读复制代码console.log("Start"); setTimeout(() => { console.log("Timeout"); }, 0); Promise.resolve().then(() => { console.log("Promise"); }); console.log("End"); // 输出顺序:Start -> End -> Promise -> Timeout

题目43:如何实现一个异步队列?

答案: javascript 体验AI代码助手 代码解读复制代码class AsyncQueue { constructor() { this.queue = []; this.processing = false; } enqueue(task) { this.queue.push(task); if (!this.processing) { this.process(); } } async process() { this.processing = true; while (this.queue.length > 0) { const task = this.queue.shift(); await task(); } this.processing = false; } } const queue = new AsyncQueue(); queue.enqueue(() => console.log("Task 1")); queue.enqueue(() => console.log("Task 2"));

题目44:如何实现一个异步限流器?

答案: javascript 体验AI代码助手 代码解读复制代码class RateLimiter { constructor(limit, interval) { this.limit = limit; this.interval = interval; this.queue = []; this.count = 0; } enqueue(task) { this.queue.push(task); this.run(); } run() { if (this.count < this.limit && this.queue.length > 0) { const task = this.queue.shift(); this.count++; task().finally(() => { this.count--; setTimeout(() => this.run(), this.interval); }); this.run(); } } } const limiter = new RateLimiter(2, 1000); for (let i = 0; i < 5; i++) { limiter.enqueue(() => new Promise((resolve) => setTimeout(resolve, 500))); }

题目45:如何实现一个异步重试机制?

答案: javascript 体验AI代码助手 代码解读复制代码async function retry(fn, retries, delay) { try { return await fn(); } catch (error) { if (retries === 0) throw error; await new Promise((resolve) => setTimeout(resolve, delay)); return retry(fn, retries - 1, delay); } }

四、性能优化(50题)

  1. 防抖与节流

题目46:什么是防抖(Debounce)和节流(Throttle)?如何实现?

答案:

防抖:在事件触发后,等待一段时间再执行回调。 节流:在一定时间内只执行一次回调。

javascript 体验AI代码助手 代码解读复制代码// 防抖 function debounce(func, wait) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; }

// 节流 function throttle(func, wait) { let lastTime = 0; return function(...args) { const now = Date.now(); if (now - lastTime >= wait) { func.apply(this, args); lastTime = now; } }; }

题目47:如何实现一个带立即执行选项的防抖函数?

答案: javascript 体验AI代码助手 代码解读复制代码function debounce(func, wait, immediate) { let timeout; return function(...args) { const callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(() => { timeout = null; if (!immediate) func.apply(this, args); }, wait); if (callNow) func.apply(this, args); }; }

题目48:如何实现一个带取消选项的防抖函数?

答案: javascript 体验AI代码助手 代码解读复制代码function debounce(func, wait) { let timeout; function debounced(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); } debounced.cancel = function() { clearTimeout(timeout); }; return debounced; }

题目49:如何实现一个带尾调用选项的节流函数?

答案: javascript 体验AI代码助手 代码解读复制代码function throttle(func, wait, trailing = true) { let lastTime = 0; let timeout; return function(...args) { const now = Date.now(); if (now - lastTime >= wait) { if (timeout) { clearTimeout(timeout); timeout = null; } func.apply(this, args); lastTime = now; } else if (trailing && !timeout) { timeout = setTimeout(() => { func.apply(this, args); lastTime = Date.now(); timeout = null; }, wait - (now - lastTime)); } }; }

题目50:如何实现一个带取消选项的节流函数?

答案: javascript 体验AI代码助手 代码解读复制代码function throttle(func, wait) { let lastTime = 0; let timeout; function throttled(...args) { const now = Date.now(); if (now - lastTime >= wait) { if (timeout) { clearTimeout(timeout); timeout = null; } func.apply(this, args); lastTime = now; } else if (!timeout) { timeout = setTimeout(() => { func.apply(this, args); lastTime = Date.now(); timeout = null; }, wait - (now - lastTime)); } } throttled.cancel = function() { clearTimeout(timeout); timeout = null; }; return throttled; }

五、综合题目(50题)

题目51:如何实现一个深拷贝函数?

答案: javascript 体验AI代码助手 代码解读复制代码function deepClone(obj) { if (obj === null || typeof obj !== 'object') { return obj; } let clone = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { clone[key] = deepClone(obj[key]); } } return clone; }

题目52:如何实现一个函数柯里化?

答案: javascript 体验AI代码助手 代码解读复制代码function curry(fn) { return function curried(...args) { if (args.length >= fn.length) { return fn.apply(this, args); } else { return function(...moreArgs) { return curried.apply(this, args.concat(moreArgs)); }; } }; } const add = (a, b, c) => a + b + c; const curriedAdd = curry(add); console.log(curriedAdd(1)(2)(3)); // 6

题目53:如何实现一个函数记忆化(Memoization)?

答案: javascript 体验AI代码助手 代码解读复制代码function memoize(fn) { const cache = new Map(); return function(...args) { const key = JSON.stringify(args); if (cache.has(key)) { return cache.get(key); } const result = fn.apply(this, args); cache.set(key, result); return result; }; } const factorial = memoize((n) => n <= 1 ? 1 : n * factorial(n - 1)); console.log(factorial(5)); // 120

题目54:如何实现一个函数组合(Compose)?

答案: javascript 体验AI代码助手 代码解读复制代码function compose(...fns) { return function(x) { return fns.reduceRight((acc, fn) => fn(acc), x); }; } const add1 = (x) => x + 1; const mul2 = (x) => x * 2; const addThenMul = compose(mul2, add1); console.log(addThenMul(5)); // 12

题目55:如何实现一个函数管道(Pipe)?

答案: javascript 体验AI代码助手 代码解读复制代码function pipe(...fns) { return function(x) { return fns.reduce((acc, fn) => fn(acc), x); }; } const add1 = (x) => x + 1; const mul2 = (x) => x * 2; const addThenMul = pipe(add1, mul2); console.log(addThenMul(5)); // 12

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:糀飞

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!