前端面试500题大全
前端开发面试 500 题大全(含答案、解析和示例)
本文收集了前端开发工程师最常见的 500 道面试题,涵盖了前端开发的各个方面,包括但不限于 HTML5、CSS3、JavaScript、ES6+、Vue、React、浏览器原理、网络协议、前端工程化等领域。每道题都配有详细的答案解析和实际示例,帮助你更好地理解和掌握相关知识点。
目录
HTML5 和 CSS3
1. 什么是语义化标签?请列举常用的语义化标签并说明其用途。
答案解析:
语义化标签是指使用恰当的 HTML 标签来传达文档结构和内容的含义,而不是单纯为了展示效果。使用语义化标签有以下好处:
提高代码可读性和可维护性
有利于 SEO 优化
提升无障碍访问体验
便于不同设备解析页面结构
常用的语义化标签包括:
- <header> - 页面或区块的头部
- <nav> - 导航栏
- <main> - 主要内容
- <article> - 独立的文章内容
- <section> - 区块
- <aside> - 侧边栏
- <footer> - 页面或区块的底部
- <figure> - 图片或图表
- <figcaption> - 图片或图表的说明
示例:
- <header>
- <nav>
- <ul>
- <li><a href="#home">首页</a>li>
- <li><a href="#about">关于</a>li>
- ul>
- nav>
- header>
- <main>
- <article>
- <h1>文章标题h1>
- <section>
- <h2>第一章h2>
- <p>章节内容...p>
- section>
- article>
- <aside>
- <h3>相关文章h3>
- <ul>
- <li><a href="#">推荐阅读 1</a>li>
- <li><a href="#">推荐阅读 2</a>li>
- ul>
- aside>
- main>
- <footer>
- <p>版权所有 © 2024p>
- footer>
2. 请详细解释 CSS 盒模型,以及标准盒模型和 IE 盒模型的区别。
答案解析:
CSS 盒模型描述了网页中的元素如何显示以及如何计算它们的尺寸。每个元素都被表示为一个矩形盒子,包含以下部分:
content(内容)
padding(内边距)
border(边框)
margin(外边距)
标准盒模型和 IE 盒模型的主要区别在于宽度和高度的计算方式:
标准盒模型(content-box):width/height 只包含 content
IE 盒模型(border-box):width/height 包含 content + padding + border
示例:
- /* 标准盒模型 */
- .standard-box {
- box-sizing: content-box; /* 默认值 */
- width: 200px;
- padding: 20px;
- border: 1px solid #000;
- /* 实际宽度 = 200px + 40px(padding) + 2px(border) = 242px */
- }
- /* IE盒模型 */
- .ie-box {
- box-sizing: border-box;
- width: 200px;
- padding: 20px;
- border: 1px solid #000;
- /* 实际宽度 = 200px(包含padding和border) */
- }
在实际开发中,通常推荐使用 IE 盒模型(border-box),因为它更直观且便于计算。常见的全局设置:
- * {
- box-sizing: border-box;
- }
3. 请解释 CSS 中的 BFC(Block Formatting Context)及其应用场景。
答案解析:
BFC(Block Formatting Context)是 CSS 中的一个重要概念,它是一个独立的渲染区域,有自己的渲染规则:
内部的盒子会在垂直方向上一个接一个放置
同一个 BFC 内的相邻元素的 margin 会发生重叠
BFC 区域不会与浮动元素重叠
BFC 能包含浮动元素(清除浮动)
BFC 就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素
触发 BFC 的条件:
根元素(<html>)
浮动元素(float 不为 none)
绝对定位元素(position 为 absolute 或 fixed)
display 为 inline-block、flex、grid、flow-root
overflow 不为 visible 的块级元素
应用场景示例:
清除浮动:
- <style>
- .container {
- overflow: hidden; /* 触发 BFC */
- }
- .float-item {
- float: left;
- width: 100px;
- height: 100px;
- background: #f0f0f0;
- }
- style>
- <div class="container">
- <div class="float-item">div>
- <div class="float-item">div>
- div>
防止 margin 重叠:
- <style>
- .box {
- margin: 20px 0;
- background: #f0f0f0;
- }
- .bfc-container {
- display: flow-root; /* 触发 BFC */
- }
- style>
- <div class="box">Box 1div>
- <div class="bfc-container">
- <div class="box">Box 2div>
- div>
自适应两栏布局:
- <style>
- .aside {
- float: left;
- width: 200px;
- background: #f0f0f0;
- }
- .main {
- display: flow-root; /* 触发 BFC */
- background: #ddd;
- }
- style>
- <div class="aside">侧边栏div>
- <div class="main">主要内容div>
4. 请详细解释 CSS Flexbox 布局模型及其常用属性。
答案解析:
Flexbox(弹性盒子)是一种一维布局模型,它提供了强大的空间分布和对齐能力。主要包含两个角色:容器(flex container)和项目(flex item)。
Flex 容器属性:
display: flex | inline-flex:定义 flex 容器
flex-direction:主轴方向
flex-wrap:是否换行
justify-content:主轴对齐方式
align-items:交叉轴对齐方式
align-content:多行对齐方式
Flex 项目属性:
flex-grow:放大比例
flex-shrink:缩小比例
flex-basis:基准大小
flex:上述三个属性的简写
align-self:单个项目对齐方式
order:排列顺序
示例:
- <style>
- .container {
- display: flex;
- justify-content: space-between;
- align-items: center;
- height: 200px;
- background: #f0f0f0;
- }
- .item {
- flex: 1;
- height: 100px;
- margin: 0 10px;
- background: #ddd;
- }
- .item:nth-child(2) {
- flex: 2; /* 占据2倍空间 */
- }
- style>
- <div class="container">
- <div class="item">1div>
- <div class="item">2div>
- <div class="item">3div>
- div>
常见布局场景:
居中布局:
- .center {
- display: flex;
- justify-content: center;
- align-items: center;
- }
等分布局:
- .equal {
- display: flex;
- }
- .equal > * {
- flex: 1;
- }
粘性页脚:
- .page {
- display: flex;
- flex-direction: column;
- min-height: 100vh;
- }
- .content {
- flex: 1;
- }
5. 请解释 CSS Grid 布局系统及其与 Flexbox 的区别。
答案解析:
CSS Grid 是一个二维布局系统,专门用于创建基于网格的布局。与 Flexbox 的一维布局不同,Grid 可以同时控制行和列的布局。
Grid 容器属性:
display: grid | inline-grid:定义网格容器
grid-template-columns:定义列的大小和数量
grid-template-rows:定义行的大小和数量
grid-gap:定义网格间距
grid-template-areas:定义网格区域
Grid 项目属性:
grid-column:指定列的开始和结束位置
grid-row:指定行的开始和结束位置
grid-area:指定项目所在的区域
示例:
- <style>
- .grid-container {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- grid-template-rows: auto 1fr auto;
- grid-gap: 20px;
- height: 100vh;
- }
- .header {
- grid-column: 1 / -1; /* 跨越所有列 */
- background: #f0f0f0;
- }
- .sidebar {
- grid-row: 2 / 3;
- background: #ddd;
- }
- .main {
- grid-column: 2 / -1;
- grid-row: 2 / 3;
- background: #ccc;
- }
- .footer {
- grid-column: 1 / -1;
- background: #f0f0f0;
- }
- style>
- <div class="grid-container">
- <header class="header">页头header>
- <aside class="sidebar">侧边栏aside>
- <main class="main">主要内容main>
- <footer class="footer">页脚footer>
- div>
Grid vs Flexbox:
维度:
Grid:二维布局(行和列)
Flexbox:一维布局(主轴)
适用场景:
Grid:适合整体页面布局
Flexbox:适合组件内部布局
方向控制:
Grid:可以同时控制两个方向
Flexbox:主要控制一个方向
对齐方式:
Grid:更适合大规模的网格系统
Flexbox:更适合小规模的灵活布局
6. 解释 CSS 中的定位(position)属性及其应用场景。
答案解析:
CSS 定位属性用于控制元素在页面中的位置,包括以下值:
static(默认值)
relative(相对定位)
absolute(绝对定位)
fixed(固定定位)
sticky(粘性定位)
各种定位的特点和应用:
relative:
相对于自身原始位置偏移
不脱离文档流
常用于为绝对定位子元素提供参考点
- .relative-box {
- position: relative;
- top: 20px;
- left: 20px;
- }
absolute:
相对于最近的非 static 定位祖先元素定位
脱离文档流
常用于弹窗、工具提示等
- .modal {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- }
fixed:
相对于视口定位
脱离文档流
常用于固定导航栏、返回顶部按钮等
- .navbar {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- }
sticky:
在阈值之前为相对定位,之后为固定定位
不脱离文档流
常用于吸顶导航、表格头等
- .sticky-header {
- position: sticky;
- top: 0;
- background: white;
- z-index: 1;
- }
实际应用示例:
固定导航栏:
- <style>
- .navbar {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- background: white;
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
- z-index: 1000;
- }
- style>
模态框居中:
- <style>
- .modal-overlay {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0,0,0,0.5);
- }
- .modal {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- background: white;
- padding: 20px;
- border-radius: 4px;
- }
- style>
吸顶效果:
- <style>
- .section-header {
- position: sticky;
- top: 0;
- padding: 10px;
- background: white;
- border-bottom: 1px solid #eee;
- }
- style>
7. CSS 预处理器(如 Sass/Less)的主要特性和优势是什么?
答案解析:
CSS 预处理器是一种专门的编程语言,让 CSS 具备了一定的编程能力,增强了 CSS 的可维护性和复用性。
主要特性:
变量:
- // SCSS
- $primary-color: #3498db;
- $spacing: 20px;
- .button {
- background: $primary-color;
- padding: $spacing;
- }
嵌套:
- .nav {
- background: #fff;
- &__item { // 编译为 .nav__item
- padding: 10px;
- &:hover { // 编译为 .nav__item:hover
- background: #f0f0f0;
- }
- }
- }
混合(Mixins):
- @mixin flex-center {
- display: flex;
- justify-content: center;
- align-items: center;
- }
- .card {
- @include flex-center;
- background: #fff;
- }
函数:
- @function calculate-width($n) {
- @return $n * 100px;
- }
- .container {
- width: calculate-width(3); // 300px
- }
继承:
- %button-base {
- padding: 10px 20px;
- border: none;
- border-radius: 4px;
- }
- .button-primary {
- @extend %button-base;
- background: blue;
- }
- .button-secondary {
- @extend %button-base;
- background: gray;
- }
主要优势:
代码复用性:
通过变量、混合、继承减少重复代码
可以创建可复用的样式库
可维护性:
更好的代码组织
模块化开发
命名空间
开发效率:
嵌套减少重复选择器
运算能力
自动化工具集成
逻辑处理:
条件语句
循环
函数
示例:完整的 SCSS 应用
- // 变量
- $primary: #3498db;
- $secondary: #2ecc71;
- $spacing: 20px;
- // 混合
- @mixin button($bg-color) {
- padding: $spacing / 2;
- background: $bg-color;
- border: none;
- border-radius: 4px;
- &:hover {
- background: darken($bg-color, 10%);
- }
- }
- // 函数
- @function strip-unit($value) {
- @return $value / ($value * 0 + 1);
- }
- // 基础样式
- %flex-center {
- display: flex;
- justify-content: center;
- align-items: center;
- }
- // 组件样式
- .container {
- @extend %flex-center;
- max-width: 1200px;
- margin: 0 auto;
- padding: $spacing;
- .content {
- flex: 1;
- @media (max-width: 768px) {
- padding: $spacing / 2;
- }
- }
- }
- .button {
- &--primary {
- @include button($primary);
- }
- &--secondary {
- @include button($secondary);
- }
- }
编译后的 CSS:
- .container {
- display: flex;
- justify-content: center;
- align-items: center;
- max-width: 1200px;
- margin: 0 auto;
- padding: 20px;
- }
- .container .content {
- flex: 1;
- }
- @media (max-width: 768px) {
- .container .content {
- padding: 10px;
- }
- }
- .button--primary {
- padding: 10px;
- background: #3498db;
- border: none;
- border-radius: 4px;
- }
- .button--primary:hover {
- background: #217dbb;
- }
- .button--secondary {
- padding: 10px;
- background: #2ecc71;
- border: none;
- border-radius: 4px;
- }
- .button--secondary:hover {
- background: #25a25a;
- }
8. 请详细解释 CSS3 中的转换(Transform)、过渡(Transition)和动画(Animation)。
答案解析:
CSS3 提供了强大的动画和过渡效果能力,主要包括三个方面:转换、过渡和动画。
1. 转换(Transform)
transform 属性允许你旋转、缩放、倾斜或平移给定元素:
- .transform-example {
- /* 2D 转换 */
- transform: translate(50px, 100px) /* 平移 */
- rotate(45deg) /* 旋转 */
- scale(1.5) /* 缩放 */
- skew(10deg, 20deg); /* 倾斜 */
- /* 3D 转换 */
- transform: rotateX(45deg) /* 3D 旋转 */
- perspective(1000px) /* 透视 */
- translateZ(100px); /* Z轴平移 */
- }
2. 过渡(Transition)
transition 允许在一定时间内平滑地改变属性值:
- .transition-example {
- width: 100px;
- background: blue;
- transition: all 0.3s ease-in-out;
- }
- .transition-example:hover {
- width: 200px;
- background: red;
- }
过渡属性包括:
transition-property:要过渡的属性
transition-duration:过渡持续时间
transition-timing-function:过渡时间函数
transition-delay:过渡延迟时间
3. 动画(Animation)
animation 允许创建复杂的自定义动画:
- /* 定义关键帧 */
- @keyframes slide-in {
- 0% {
- transform: translateX(-100%);
- opacity: 0;
- }
- 100% {
- transform: translateX(0);
- opacity: 1;
- }
- }
- .animation-example {
- animation: slide-in 1s ease-out forwards;
- }
- /* 多个动画组合 */
- .multiple-animations {
- animation:
- slide-in 1s ease-out,
- fade-in 2s ease-in;
- }
动画属性包括:
animation-name:动画名称
animation-duration:动画持续时间
animation-timing-function:动画时间函数
animation-delay:动画延迟时间
animation-iteration-count:动画重复次数
animation-direction:动画方向
animation-fill-mode:动画填充模式
animation-play-state:动画播放状态
实际应用示例:
按钮悬停效果:
- .button {
- padding: 10px 20px;
- background: #3498db;
- color: white;
- border: none;
- border-radius: 4px;
- transition: all 0.3s ease;
- }
- .button:hover {
- transform: translateY(-2px);
- box-shadow: 0 5px 15px rgba(0,0,0,0.2);
- }
加载动画:
- @keyframes spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
- }
- .loader {
- width: 40px;
- height: 40px;
- border: 4px solid #f3f3f3;
- border-top: 4px solid #3498db;
- border-radius: 50%;
- animation: spin 1s linear infinite;
- }
卡片翻转效果:
- .card {
- position: relative;
- width: 300px;
- height: 200px;
- transform-style: preserve-3d;
- transition: transform 0.6s;
- }
- .card:hover {
- transform: rotateY(180deg);
- }
- .card-front,
- .card-back {
- position: absolute;
- width: 100%;
- height: 100%;
- backface-visibility: hidden;
- }
- .card-back {
- transform: rotateY(180deg);
- }
9. 解释 CSS3 中的响应式设计和媒体查询。
答案解析:
响应式设计是一种让网站能够自适应不同设备和屏幕尺寸的设计方法。主要通过媒体查询(Media Queries)和弹性布局来实现。
1. 媒体查询基础语法:
- @media screen and (max-width: 768px) {
- /* 针对屏幕宽度小于等于 768px 的样式 */
- }
- @media screen and (min-width: 769px) and (max-width: 1024px) {
- /* 针对屏幕宽度在 769px 到 1024px 之间的样式 */
- }
2. 常见断点设置:
- /* 移动优先的响应式设计 */
- /* 基础样式(移动端) */
- .container {
- width: 100%;
- padding: 15px;
- }
- /* 平板 */
- @media screen and (min-width: 768px) {
- .container {
- width: 750px;
- margin: 0 auto;
- }
- }
- /* 桌面 */
- @media screen and (min-width: 1024px) {
- .container {
- width: 970px;
- }
- }
- /* 大屏幕 */
- @media screen and (min-width: 1200px) {
- .container {
- width: 1170px;
- }
- }
3. 响应式布局技巧:
流式布局:
- .fluid-layout {
- width: 90%;
- max-width: 1200px;
- margin: 0 auto;
- }
弹性图片:
- .responsive-image {
- max-width: 100%;
- height: auto;
- }
弹性字体:
- html {
- font-size: 16px;
- }
- @media screen and (max-width: 768px) {
- html {
- font-size: 14px;
- }
- }
- .text {
- font-size: 1rem; /* 相对于根元素的字体大小 */
- }
响应式网格:
- .grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
- gap: 20px;
- }
4. 完整的响应式设计示例:
- /* 基础样式 */
- :root {
- --primary-color: #3498db;
- --spacing: 20px;
- }
- /* 响应式排版 */
- html {
- font-size: 16px;
- }
- @media screen and (max-width: 768px) {
- html {
- font-size: 14px;
- }
- }
- /* 响应式容器 */
- .container {
- width: 90%;
- max-width: 1200px;
- margin: 0 auto;
- padding: var(--spacing);
- }
- /* 响应式导航 */
- .nav {
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- @media screen and (max-width: 768px) {
- .nav {
- flex-direction: column;
- }
- .nav-menu {
- width: 100%;
- display: none; /* 移动端菜单默认隐藏 */
- }
- .nav-menu.active {
- display: block;
- }
- }
- /* 响应式网格 */
- .grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
- gap: var(--spacing);
- }
- /* 响应式卡片 */
- .card {
- background: white;
- padding: var(--spacing);
- border-radius: 4px;
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
- }
- @media screen and (max-width: 768px) {
- .card {
- padding: calc(var(--spacing) / 2);
- }
- }
- /* 响应式表格 */
- .table {
- width: 100%;
- border-collapse: collapse;
- }
- @media screen and (max-width: 768px) {
- .table {
- display: block;
- overflow-x: auto;
- }
- }
- /* 响应式图片 */
- .image {
- max-width: 100%;
- height: auto;
- }
- /* 响应式视频 */
- .video-container {
- position: relative;
- padding-bottom: 56.25%; /* 16:9 比例 */
- height: 0;
- overflow: hidden;
- }
- .video-container iframe {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- }
10. CSS3 中的新选择器和伪类/伪元素有哪些?
答案解析:
CSS3 引入了许多新的选择器和伪类/伪元素,大大增强了样式选择的能力。
1. 属性选择器:
- /* 精确匹配 */
- [data-type="primary"] {
- background: blue;
- }
- /* 开头匹配 */
- [class^="btn-"] {
- padding: 10px;
- }
- /* 结尾匹配 */
- [href$=".pdf"] {
- color: red;
- }
- /* 包含匹配 */
- [title*="hello"] {
- font-weight: bold;
- }
2. 结构性伪类:
- /* 第一个子元素 */
- :first-child {
- margin-top: 0;
- }
- /* 最后一个子元素 */
- :last-child {
- margin-bottom: 0;
- }
- /* 第 n 个子元素 */
- :nth-child(2n) {
- background: #f0f0f0;
- }
- /* 第 n 个特定类型的元素 */
- p:nth-of-type(odd) {
- color: blue;
- }
3. 状态伪类:
- /* 输入框焦点 */
- input:focus {
- border-color: blue;
- }
- /* 禁用状态 */
- button:disabled {
- opacity: 0.5;
- }
- /* 选中状态 */
- input:checked + label {
- color: green;
- }
- /* 必填字段 */
- input:required {
- border-color: red;
- }
4. 目标伪类:
- /* 当 URL 片段匹配元素 ID 时 */
- :target {
- background: yellow;
- }
5. 否定伪类:
- /* 选择非特定元素 */
- .item:not(.active) {
- opacity: 0.5;
- }
6. 伪元素:
- /* 首字母样式 */
- p::first-letter {
- font-size: 2em;
- float: left;
- }
- /* 首行样式 */
- p::first-line {
- color: blue;
- }
- /* 前后插入内容 */
- .quote::before {
- content: "「";
- }
- .quote::after {
- content: "」";
- }
- /* 选择文本样式 */
- ::selection {
- background: yellow;
- color: black;
- }
实际应用示例:
自定义列表样式:
- .custom-list {
- list-style: none;
- padding: 0;
- }
- .custom-list li::before {
- content: "✓";
- color: green;
- margin-right: 8px;
- }
卡片悬停效果:
- .card {
- position: relative;
- padding: 20px;
- }
- .card::after {
- content: "";
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- border: 2px solid transparent;
- transition: all 0.3s;
- }
- .card:hover::after {
- border-color: #3498db;
- transform: scale(1.05);
- }
表单验证样式:
- .form-input:invalid {
- border-color: red;
- }
- .form-input:invalid::after {
- content: "⚠";
- color: red;
- margin-left: 8px;
- }
- .form-input:valid {
- border-color: green;
- }
- .form-input:valid::after {
- content: "✓";
- color: green;
- margin-left: 8px;
- }
交替背景色:
- .striped-list li:nth-child(odd) {
- background: #f9f9f9;
- }
- .striped-list li:nth-child(even) {
- background: #ffffff;
- }
高级导航样式:
- .nav-link {
- position: relative;
- padding: 10px;
- }
- .nav-link::after {
- content: "";
- position: absolute;
- bottom: 0;
- left: 50%;
- width: 0;
- height: 2px;
- background: #3498db;
- transition: all 0.3s;
- transform: translateX(-50%);
- }
- .nav-link:hover::after {
- width: 100%;
- }
- .nav-link.active::after {
- width: 100%;
- }
JavaScript 核心概念
11. JavaScript 中的数据类型有哪些?如何进行类型判断?
答案解析:
JavaScript 中的数据类型分为两大类:基本数据类型(原始类型)和引用数据类型(对象类型)。
1. 基本数据类型:
Number:数字
String:字符串
Boolean:布尔值
Undefined:未定义
Null:空值
Symbol:符号(ES6)
BigInt:大整数(ES2020)
2. 引用数据类型:
Object:对象
Array:数组
Function:函数
Date:日期
RegExp:正则表达式
Map:映射
Set:集合
Promise:期约
...等
类型判断的方法:
typeof 运算符:
- typeof 42; // "number"
- typeof "hello"; // "string"
- typeof true; // "boolean"
- typeof undefined; // "undefined"
- typeof Symbol(); // "symbol"
- typeof null; // "object" (这是一个历史遗留的bug)
- typeof {}; // "object"
- typeof []; // "object"
- typeof function(){}; // "function"
instanceof 运算符:
- [] instanceof Array; // true
- new Date() instanceof Date; // true
- /regex/ instanceof RegExp; // true
- // 自定义类型判断
- class MyClass {}
- const obj = new MyClass();
- obj instanceof MyClass; // true
Object.prototype.toString.call():
- Object.prototype.toString.call(42); // "[object Number]"
- Object.prototype.toString.call("hello"); // "[object String]"
- Object.prototype.toString.call(true); // "[object Boolean]"
- Object.prototype.toString.call(undefined); // "[object Undefined]"
- Object.prototype.toString.call(null); // "[object Null]"
- Object.prototype.toString.call({}); // "[object Object]"
- Object.prototype.toString.call([]); // "[object Array]"
- Object.prototype.toString.call(function(){}); // "[object Function]"
Array.isArray():
- Array.isArray([]); // true
- Array.isArray({}); // false
实际应用示例:
通用类型检查函数:
- function getType(value) {
- if (value === null) {
- return 'null';
- }
- if (typeof value === 'object') {
- return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
- }
- return typeof value;
- }
- // 使用示例
- getType(42); // "number"
- getType("hello"); // "string"
- getType(true); // "boolean"
- getType(undefined); // "undefined"
- getType(null); // "null"
- getType({}); // "object"
- getType([]); // "array"
- getType(function(){}); // "function"
- getType(new Date()); // "date"
- getType(/regex/); // "regexp"
类型安全的值比较:
- function safelyCompareValues(value1, value2) {
- // 首先比较类型
- if (getType(value1) !== getType(value2)) {
- return false;
- }
- // 对于数组,递归比较每个元素
- if (Array.isArray(value1)) {
- if (value1.length !== value2.length) {
- return false;
- }
- return value1.every((item, index) => safelyCompareValues(item, value2[index]));
- }
- // 对于对象,递归比较每个属性
- if (getType(value1) === 'object') {
- const keys1 = Object.keys(value1);
- const keys2 = Object.keys(value2);
- if (keys1.length !== keys2.length) {
- return false;
- }
- return keys1.every(key => safelyCompareValues(value1[key], value2[key]));
- }
- // 基本类型直接比较
- return value1 === value2;
- }
- // 使用示例
- safelyCompareValues([1, 2, 3], [1, 2, 3]); // true
- safelyCompareValues([1, 2, 3], [1, 2, '3']); // false
- safelyCompareValues({a: 1, b: 2}, {a: 1, b: 2}); // true
- safelyCompareValues({a: 1, b: 2}, {b: 2, a: 1}); // true
类型转换工具:
- const TypeConverter = {
- toNumber(value) {
- if (typeof value === 'number') return value;
- if (typeof value === 'string') {
- const num = Number(value);
- if (!isNaN(num)) return num;
- }
- throw new TypeError('Cannot convert value to number');
- },
- toString(value) {
- if (value === null) return 'null';
- if (value === undefined) return 'undefined';
- if (typeof value === 'object') {
- return JSON.stringify(value);
- }
- return String(value);
- },
- toBoolean(value) {
- return Boolean(value);
- },
- toArray(value) {
- if (Array.isArray(value)) return value;
- if (typeof value === 'string') return value.split('');
- if (value === null || value === undefined) return [];
- return [value];
- }
- };
- // 使用示例
- TypeConverter.toNumber('123'); // 123
- TypeConverter.toString({a: 1}); // "{"a":1}"
- TypeConverter.toBoolean(1); // true
- TypeConverter.toArray('hello'); // ['h', 'e', 'l', 'l', 'o']
12. 详细解释 JavaScript 中的作用域和作用域链。
答案解析:
作用域是变量和函数的可访问范围,JavaScript 中主要有全局作用域、函数作用域和块级作用域(ES6)。作用域链则是多个作用域的嵌套关系。
1. 作用域类型:
全局作用域:
- var globalVar = 'global'; // 全局变量
- function globalFunction() {
- console.log(globalVar); // 可以访问全局变量
- }
函数作用域:
- function outer() {
- var functionVar = 'function'; // 函数作用域变量
- function inner() {
- console.log(functionVar); // 可以访问外部函数变量
- }
- inner();
- }
块级作用域(ES6):
- {
- let blockVar = 'block'; // 块级作用域变量
- const constVar = 'const'; // 块级作用域常量
- }
- // console.log(blockVar); // ReferenceError
2. 作用域链:
作用域链是由当前作用域到全局作用域的链式查找过程:
- var global = 'global';
- function outer() {
- var outerVar = 'outer';
- function inner() {
- var innerVar = 'inner';
- console.log(innerVar); // 当前作用域
- console.log(outerVar); // 外部作用域
- console.log(global); // 全局作用域
- }
- inner();
- }
3. 变量提升:
- console.log(hoistedVar); // undefined
- var hoistedVar = 'value';
- // 等同于:
- var hoistedVar;
- console.log(hoistedVar);
- hoistedVar = 'value';
- // 函数声明也会提升
- hoistedFunction(); // 可以执行
- function hoistedFunction() {
- console.log('function hoisted');
- }
4. 闭包和作用域:
- function createCounter() {
- let count = 0; // 私有变量
- return {
- increment() {
- return ++count;
- },
- decrement() {
- return --count;
- },
- getCount() {
- return count;
- }
- };
- }
- const counter = createCounter();
- console.log(counter.increment()); // 1
- console.log(counter.increment()); // 2
- console.log(counter.getCount()); // 2
5. let/const 和暂时性死区(TDZ):
- {
- // TDZ 开始
- // console.log(blockVar); // ReferenceError
- let blockVar = 'block'; // TDZ 结束
- const constVar = 'const';
- // constVar = 'new value'; // TypeError
- }
实际应用示例:
模块化封装:
- const Module = (function() {
- // 私有变量和函数
- let privateVar = 'private';
- function privateFunction() {
- return privateVar;
- }
- // 公共 API
- return {
- publicMethod() {
- return privateFunction();
- },
- publicVar: 'public'
- };
- })();
- console.log(Module.publicVar); // "public"
- console.log(Module.publicMethod()); // "private"
- // console.log(Module.privateVar); // undefined
事件处理器:
- function createEventHandler(element, event) {
- let count = 0;
- element.addEventListener(event, function() {
- count++;
- console.log(`Event triggered ${count} times`);
- });
- return {
- getCount() {
- return count;
- },
- resetCount() {
- count = 0;
- }
- };
- }
- const handler = createEventHandler(button, 'click');
- // 点击按钮后...
- console.log(handler.getCount()); // 显示点击次数
迭代器生成器:
- function createIterator(array) {
- let index = 0;
- return {
- next() {
- if (index < array.length) {
- return {
- value: array[index++],
- done: false
- };
- }
- return { done: true };
- },
- current() {
- return array[index];
- }
- };
- }
- const iterator = createIterator([1, 2, 3]);
- console.log(iterator.next()); // { value: 1, done: false }
- console.log(iterator.next()); // { value: 2, done: false }
- console.log(iterator.next()); // { value: 3, done: false }
- console.log(iterator.next()); // { done: true }
13. 解释 JavaScript 中的原型和原型链。
答案解析:
原型(Prototype)是 JavaScript 实现继承的主要方式。每个对象都有一个原型对象,对象会从原型"继承"属性和方法。原型链则是由原型对象组成的链式查找机制。
1. 原型基础:
- // 构造函数
- function Person(name) {
- this.name = name;
- }
- // 原型方法
- Person.prototype.sayHello = function() {
- return `Hello, I'm ${this.name}`;
- };
- // 创建实例
- const person = new Person('John');
- console.log(person.sayHello()); // "Hello, I'm John"
- // 原型链查找
- console.log(person.__proto__ === Person.prototype); // true
- console.log(Person.prototype.__proto__ === Object.prototype); // true
- console.log(Object.prototype.__proto__ === null); // true
2. 继承实现:
原型链继承:
- function Animal(name) {
- this.name = name;
- }
- Animal.prototype.speak = function() {
- return `${this.name} makes a sound`;
- };
- function Dog(name) {
- Animal.call(this, name); // 调用父类构造函数
- }
- // 设置原型链
- Dog.prototype = Object.create(Animal.prototype);
- Dog.prototype.constructor = Dog;
- // 添加子类方法
- Dog.prototype.bark = function() {
- return `${this.name} barks`;
- };
- const dog = new Dog('Rex');
- console.log(dog.speak()); // "Rex makes a sound"
- console.log(dog.bark()); // "Rex barks"
Class 语法(ES6):
- class Animal {
- constructor(name) {
- this.name = name;
- }
- speak() {
- return `${this.name} makes a sound`;
- }
- }
- class Dog extends Animal {
- constructor(name) {
- super(name);
- }
- bark() {
- return `${this.name} barks`;
- }
- }
- const dog = new Dog('Rex');
- console.log(dog.speak()); // "Rex makes a sound"
- console.log(dog.bark()); // "Rex barks"
3. 实例方法和静态方法:
- class Calculator {
- // 实例方法
- add(a, b) {
- return a + b;
- }
- // 静态方法
- static multiply(a, b) {
- return a * b;
- }
- }
- const calc = new Calculator();
- console.log(calc.add(1, 2)); // 3
- console.log(Calculator.multiply(2, 3)); // 6
4. 原型方法扩展:
- // 扩展内置对象原型
- Array.prototype.first = function() {
- return this[0];
- };
- Array.prototype.last = function() {
- return this[this.length - 1];
- };
- const arr = [1, 2, 3];
- console.log(arr.first()); // 1
- console.log(arr.last()); // 3
实际应用示例:
组件继承:
- class Component {
- constructor(props = {}) {
- this.props = props;
- this.state = {};
- }
- setState(newState) {
- this.state = { ...this.state, ...newState };
- this.render();
- }
- render() {
- throw new Error('Component must implement render method');
- }
- }
- class Button extends Component {
- constructor(props) {
- super(props);
- this.state = {
- isPressed: false
- };
- }
- toggle() {
- this.setState({ isPressed: !this.state.isPressed });
- }
- render() {
- return `<button class="${this.state.isPressed ? 'pressed' : ''}">${this.props.label}button>`;
- }
- }
- const button = new Button({ label: 'Click me' });
- console.log(button.render()); // ''
- button.toggle();
- console.log(button.render()); // ''
混入(Mixin)模式:
- const LoggerMixin = {
- log(message) {
- console.log(`[${this.constructor.name}] ${message}`);
- },
- error(message) {
- console.error(`[${this.constructor.name}] Error: ${message}`);
- }
- };
- const EventMixin = {
- on(event, callback) {
- this._events = this._events || {};
- this._events[event] = this._events[event] || [];
- this._events[event].push(callback);
- },
- emit(event, data) {
- if (this._events && this._events[event]) {
- this._events[event].forEach(callback => callback(data));
- }
- }
- };
- class UserService {
- constructor() {
- Object.assign(this, LoggerMixin, EventMixin);
- }
- login(username) {
- this.log(`User ${username} logged in`);
- this.emit('login', { username });
- }
- }
- const service = new UserService();
- service.on('login', data => console.log('Login event:', data));
- service.login('john'); // 输出日志和触发事件
链式调用:
- class Query {
- constructor(collection) {
- this.collection = collection;
- }
- filter(predicate) {
- this.collection = this.collection.filter(predicate);
- return this;
- }
- map(transform) {
- this.collection = this.collection.map(transform);
- return this;
- }
- sort(comparator) {
- this.collection = this.collection.sort(comparator);
- return this;
- }
- value() {
- return this.collection;
- }
- }
- const data = [
- { id: 1, name: 'John', age: 30 },
- { id: 2, name: 'Jane', age: 25 },
- { id: 3, name: 'Bob', age: 35 }
- ];
- const result = new Query(data)
- .filter(item => item.age > 25)
- .map(item => item.name)
- .sort()
- .value();
- console.log(result); // ['Bob', 'John']
14. 详细解释 JavaScript 中的 this 关键字。
答案解析:
this 关键字是 JavaScript 中最容易混淆的概念之一,它的值取决于函数的调用方式。理解 this 的绑定规则对于编写可靠的 JavaScript 代码至关重要。
1. this 的绑定规则:
默认绑定(非严格模式下指向全局对象,严格模式下指向 undefined):
- function showThis() {
- console.log(this);
- }
- showThis(); // window (非严格模式) 或 undefined (严格模式)
隐式绑定(this 指向调用该方法的对象):
- const user = {
- name: 'John',
- greet() {
- console.log(`Hello, ${this.name}!`);
- }
- };
- user.greet(); // "Hello, John!"
- // 隐式绑定丢失
- const greet = user.greet;
- greet(); // "Hello, undefined!" (this 指向全局对象或 undefined)
显式绑定(使用 call、apply 或 bind):
- function greet() {
- console.log(`Hello, ${this.name}!`);
- }
- const user = { name: 'John' };
- // call 和 apply 立即调用
- greet.call(user); // "Hello, John!"
- greet.apply(user); // "Hello, John!"
- // bind 返回新函数
- const boundGreet = greet.bind(user);
- boundGreet(); // "Hello, John!"
new 绑定(构造函数中的 this 指向新创建的对象):
- function User(name) {
- this.name = name;
- this.greet = function() {
- console.log(`Hello, ${this.name}!`);
- };
- }
- const user = new User('John');
- user.greet(); // "Hello, John!"
2. 箭头函数中的 this:
箭头函数没有自己的 this,它继承自外层作用域:
- const obj = {
- name: 'John',
- // 普通函数
- greet: function() {
- setTimeout(function() {
- console.log(`Hello, ${this.name}!`); // this 指向 window
- }, 100);
- },
- // 箭头函数
- greetArrow: function() {
- setTimeout(() => {
- console.log(`Hello, ${this.name}!`); // this 指向 obj
- }, 100);
- }
- };
- obj.greet(); // "Hello, undefined!"
- obj.greetArrow(); // "Hello, John!"
3. 常见问题和解决方案:
回调函数中的 this:
- class Counter {
- constructor() {
- this.count = 0;
- this.button = document.querySelector('#button');
- }
- // 问题写法
- setup() {
- this.button.addEventListener('click', function() {
- this.count++; // this 指向 button 元素
- });
- }
- // 解决方案 1:箭头函数
- setupArrow() {
- this.button.addEventListener('click', () => {
- this.count++; // this 指向 Counter 实例
- });
- }
- // 解决方案 2:bind
- setupBind() {
- this.button.addEventListener('click', function() {
- this.count++;
- }.bind(this));
- }
- // 解决方案 3:保存 this
- setupSave() {
- const self = this;
- this.button.addEventListener('click', function() {
- self.count++;
- });
- }
- }
方法作为回调:
- class API {
- constructor() {
- this.baseURL = 'https://api.example.com';
- }
- // 问题写法
- fetch(endpoint) {
- return $.get(this.baseURL + endpoint)
- .then(function(response) {
- this.process(response); // this 是 undefined
- });
- }
- // 解决方案
- fetchFixed(endpoint) {
- return $.get(this.baseURL + endpoint)
- .then(response => {
- this.process(response); // this 指向 API 实例
- });
- }
- process(data) {
- // 处理数据
- }
- }
实际应用示例:
事件处理器类:
- class EventHandler {
- constructor(element) {
- this.element = element;
- this.handlers = {};
- // 绑定方法
- this.handleClick = this.handleClick.bind(this);
- this.handleMouseMove = this.handleMouseMove.bind(this);
- }
- on(event, handler) {
- this.handlers[event] = handler.bind(this);
- this.element.addEventListener(event, this.handlers[event]);
- }
- off(event) {
- if (this.handlers[event]) {
- this.element.removeEventListener(event, this.handlers[event]);
- delete this.handlers[event];
- }
- }
- handleClick(event) {
- console.log('Clicked at:', event.clientX, event.clientY);
- }
- handleMouseMove(event) {
- console.log('Mouse moved to:', event.clientX, event.clientY);
- }
- }
- const handler = new EventHandler(document.body);
- handler.on('click', handler.handleClick);
- handler.on('mousemove', handler.handleMouseMove);
方法装饰器:
- function debounce(func, wait) {
- let timeout;
- return function executedFunction(...args) {
- const later = () => {
- clearTimeout(timeout);
- func.apply(this, args);
- };
- clearTimeout(timeout);
- timeout = setTimeout(later, wait);
- };
- }
- class SearchBox {
- constructor() {
- this.results = [];
- this.search = debounce(this.search.bind(this), 300);
- }
- async search(query) {
- this.results = await fetch(`/api/search?q=${query}`)
- .then(res => res.json());
- this.render();
- }
- render() {
- // 渲染搜索结果
- console.log('Results:', this.results);
- }
- }
链式方法调用:
- class Calculator {
- constructor() {
- this.value = 0;
- }
- add(n) {
- this.value += n;
- return this;
- }
- subtract(n) {
- this.value -= n;
- return this;
- }
- multiply(n) {
- this.value *= n;
- return this;
- }
- divide(n) {
- if (n !== 0) {
- this.value /= n;
- }
- return this;
- }
- power(n) {
- this.value = Math.pow(this.value, n);
- return this;
- }
- result() {
- return this.value;
- }
- }
- const calc = new Calculator();
- const result = calc
- .add(5)
- .multiply(2)
- .subtract(3)
- .divide(2)
- .power(2)
- .result();
- console.log(result); // ((5 * 2 - 3) / 2)² = 16
15. 详细解释 JavaScript 中的异步编程模式。
答案解析:
JavaScript 中的异步编程有多种实现方式,从最早的回调函数,到 Promise,再到 async/await,每种方式都有其特点和应用场景。
1. 回调函数:
- // 基本回调
- function fetchData(callback) {
- setTimeout(() => {
- callback('Data fetched');
- }, 1000);
- }
- fetchData(data => {
- console.log(data); // "Data fetched"
- });
- // 回调地狱
- fetchUser(userId, function(user) {
- fetchPosts(user.id, function(posts) {
- fetchComments(posts[0].id, function(comments) {
- console.log(comments);
- }, handleError);
- }, handleError);
- }, handleError);
2. Promise:
- // 创建 Promise
- function fetchData() {
- return new Promise((resolve, reject) => {
- setTimeout(() => {
- if (Math.random() > 0.5) {
- resolve('Success');
- } else {
- reject('Error');
- }
- }, 1000);
- });
- }
- // 使用 Promise
- fetchData()
- .then(data => {
- console.log(data);
- return processData(data);
- })
- .then(result => {
- console.log(result);
- })
- .catch(error => {
- console.error(error);
- });
- // Promise.all
- Promise.all([
- fetch('/api/users'),
- fetch('/api/posts'),
- fetch('/api/comments')
- ])
- .then(([users, posts, comments]) => {
- // 处理所有结果
- })
- .catch(error => {
- // 处理任何错误
- });
- // Promise.race
- Promise.race([
- fetch('/api/fast'),
- fetch('/api/slow')
- ])
- .then(result => {
- // 处理最快的响应
- });
3. async/await:
- // 基本用法
- async function fetchData() {
- try {
- const response = await fetch('/api/data');
- const data = await response.json();
- return data;
- } catch (error) {
- console.error('Error:', error);
- }
- }
- // 并行执行
- async function fetchAll() {
- try {
- const [users, posts, comments] = await Promise.all([
- fetch('/api/users'),
- fetch('/api/posts'),
- fetch('/api/comments')
- ]);
- return {
- users: await users.json(),
- posts: await posts.json(),
- comments: await comments.json()
- };
- } catch (error) {
- console.error('Error:', error);
- }
- }
- // 错误处理
- async function handleErrors() {
- try {
- const data = await fetchData();
- return processData(data);
- } catch (error) {
- if (error instanceof NetworkError) {
- // 处理网络错误
- } else if (error instanceof ValidationError) {
- // 处理验证错误
- } else {
- // 处理其他错误
- }
- } finally {
- // 清理工作
- }
- }
实际应用示例:
数据加载器:
- class DataLoader {
- constructor() {
- this.cache = new Map();
- }
- async load(key, fetchFn) {
- if (this.cache.has(key)) {
- return this.cache.get(key);
- }
- try {
- const data = await fetchFn();
- this.cache.set(key, data);
- return data;
- } catch (error) {
- throw new Error(`Failed to load ${key}: ${error.message}`);
- }
- }
- async loadMany(keys, fetchFn) {
- return Promise.all(
- keys.map(key => this.load(key, () => fetchFn(key)))
- );
- }
- clear(key) {
- this.cache.delete(key);
- }
- clearAll() {
- this.cache.clear();
- }
- }
- // 使用示例
- const loader = new DataLoader();
- // 单个加载
- const user = await loader.load('user:1', () =>
- fetch('/api/users/1').then(res => res.json())
- );
- // 批量加载
- const users = await loader.loadMany(['user:1', 'user:2'], id =>
- fetch(`/api/users/${id}`).then(res => res.json())
- );
请求重试机制:
- async function fetchWithRetry(url, options = {}, retries = 3) {
- const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
- for (let i = 0; i < retries; i++) {
- try {
- const response = await fetch(url, options);
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
- return await response.json();
- } catch (error) {
- if (i === retries - 1) throw error;
- await delay(Math.pow(2, i) * 1000); // 指数退避
- console.log(`Retry attempt ${i + 1} for ${url}`);
- }
- }
- }
- // 使用示例
- try {
- const data = await fetchWithRetry('/api/data');
- console.log(data);
- } catch (error) {
- console.error('All retries failed:', error);
- }
并发控制:
- class ConcurrencyManager {
- constructor(maxConcurrent = 5) {
- this.maxConcurrent = maxConcurrent;
- this.running = 0;
- this.queue = [];
- }
- async add(task) {
- if (this.running >= this.maxConcurrent) {
- await new Promise(resolve => this.queue.push(resolve));
- }
- this.running++;
- try {
- return await task();
- } finally {
- this.running--;
- if (this.queue.length > 0) {
- const next = this.queue.shift();
- next();
- }
- }
- }
- async addAll(tasks) {
- return Promise.all(tasks.map(task => this.add(task)));
- }
- }
- // 使用示例
- const manager = new ConcurrencyManager(3);
- const urls = [
- '/api/data1',
- '/api/data2',
- '/api/data3',
- '/api/data4',
- '/api/data5'
- ];
- const results = await manager.addAll(
- urls.map(url => () => fetch(url).then(res => res.json()))
- );
异步队列处理器:
- class AsyncQueue {
- constructor() {
- this.queue = [];
- this.processing = false;
- }
- async add(task) {
- return new Promise((resolve, reject) => {
- this.queue.push({ task, resolve, reject });
- this.process();
- });
- }
- async process() {
- if (this.processing) return;
- this.processing = true;
- while (this.queue.length > 0) {
- const { task, resolve, reject } = this.queue.shift();
- try {
- const result = await task();
- resolve(result);
- } catch (error) {
- reject(error);
- }
- }
- this.processing = false;
- }
- clear() {
- this.queue = [];
- }
- get size() {
- return this.queue.length;
- }
- }
- // 使用示例
- const queue = new AsyncQueue();
- // 添加任务
- queue.add(async () => {
- const response = await fetch('/api/data1');
- return response.json();
- });
- queue.add(async () => {
- const response = await fetch('/api/data2');
- return response.json();
- });
- // 批量添加任务
- const tasks = urls.map(url => async () => {
- const response = await fetch(url);
- return response.json();
- });
- for (const task of tasks) {
- await queue.add(task);
- }
16. 解释 JavaScript 中的事件循环(Event Loop)机制。
答案解析:
事件循环是 JavaScript 实现异步编程的核心机制,它负责执行代码、收集和处理事件以及执行队列中的子任务。
1. 基本概念:
调用栈(Call Stack):
存储同步代码执行的上下文
遵循后进先出(LIFO)原则
任务队列(Task Queue):
宏任务(Macrotask):setTimeout, setInterval, setImmediate, I/O, UI rendering
微任务(Microtask):Promise, process.nextTick, MutationObserver
事件循环过程:
执行同步代码(调用栈清空)
执行微任务队列
执行宏任务队列
执行渲染
重复以上步骤
2. 代码示例:
- console.log('1'); // 同步代码
- setTimeout(() => {
- console.log('2'); // 宏任务
- }, 0);
- Promise.resolve().then(() => {
- console.log('3'); // 微任务
- });
- console.log('4'); // 同步代码
- // 输出顺序:1, 4, 3, 2
3. 实际应用:
任务调度器:
- class TaskScheduler {
- constructor() {
- this.microTasks = [];
- this.macroTasks = [];
- }
- addMicroTask(task) {
- Promise.resolve().then(() => task());
- }
- addMacroTask(task) {
- setTimeout(task, 0);
- }
- schedule(task, type = 'micro') {
- if (type === 'micro') {
- this.addMicroTask(task);
- } else {
- this.addMacroTask(task);
- }
- }
- }
- // 使用示例
- const scheduler = new TaskScheduler();
- scheduler.schedule(() => {
- console.log('Micro task 1');
- });
- scheduler.schedule(() => {
- console.log('Macro task 1');
- }, 'macro');
- console.log('Sync task');
防抖和节流:
- // 防抖
- function debounce(func, wait) {
- let timeout;
- return function executedFunction(...args) {
- const later = () => {
- clearTimeout(timeout);
- func.apply(this, args);
- };
- clearTimeout(timeout);
- timeout = setTimeout(later, wait);
- };
- }
- // 节流
- function throttle(func, limit) {
- let inThrottle;
- return function executedFunction(...args) {
- if (!inThrottle) {
- func.apply(this, args);
- inThrottle = true;
- setTimeout(() => {
- inThrottle = false;
- }, limit);
- }
- };
- }
- // 使用示例
- const debouncedSearch = debounce((query) => {
- // 执行搜索
- console.log('Searching for:', query);
- }, 300);
- const throttledScroll = throttle(() => {
- // 处理滚动事件
- console.log('Scroll event handled');
- }, 100);
异步任务队列:
- class AsyncTaskQueue {
- constructor() {
- this.tasks = [];
- this.running = false;
- }
- enqueue(task) {
- return new Promise((resolve, reject) => {
- this.tasks.push({ task, resolve, reject });
- if (!this.running) {
- this.run();
- }
- });
- }
- async run() {
- this.running = true;
- while (this.tasks.length > 0) {
- const { task, resolve, reject } = this.tasks.shift();
- try {
- const result = await task();
- resolve(result);
- } catch (error) {
- reject(error);
- }
- }
- this.running = false;
- }
- }
- // 使用示例
- const queue = new AsyncTaskQueue();
- // 添加任务
- queue.enqueue(async () => {
- const result = await fetch('/api/data');
- return result.json();
- }).then(data => {
- console.log('Task 1 completed:', data);
- });
- queue.enqueue(async () => {
- await new Promise(resolve => setTimeout(resolve, 1000));
- return 'Task 2 completed';
- }).then(message => {
- console.log(message);
- });
自定义事件发射器:
- class EventEmitter {
- constructor() {
- this.events = {};
- }
- on(event, callback) {
- if (!this.events[event]) {
- this.events[event] = [];
- }
- this.events[event].push(callback);
- return () => this.off(event, callback);
- }
- off(event, callback) {
- if (!this.events[event]) return;
- this.events[event] = this.events[event].filter(cb => cb !== callback);
- }
- emit(event, data) {
- if (!this.events[event]) return;
- this.events[event].forEach(callback => {
- // 使用微任务执行回调
- Promise.resolve().then(() => callback(data));
- });
- }
- once(event, callback) {
- const remove = this.on(event, (...args) => {
- remove();
- callback.apply(this, args);
- });
- }
- }
- // 使用示例
- const emitter = new EventEmitter();
- emitter.on('data', data => {
- console.log('Received data:', data);
- });
- emitter.emit('data', { message: 'Hello' });
17. 详细解释 JavaScript 中常用的设计模式。
答案解析:
设计模式是解决软件设计中常见问题的可复用方案。在 JavaScript 中,常用的设计模式包括创建型模式、结构型模式和行为型模式。
1. 创建型模式:
单例模式:
- class Singleton {
- constructor() {
- if (!Singleton.instance) {
- this.data = {};
- Singleton.instance = this;
- }
- return Singleton.instance;
- }
- static getInstance() {
- if (!Singleton.instance) {
- Singleton.instance = new Singleton();
- }
- return Singleton.instance;
- }
- }
- // 使用示例
- const instance1 = new Singleton();
- const instance2 = new Singleton();
- console.log(instance1 === instance2); // true
工厂模式:
- // 简单工厂
- class UserFactory {
- static createUser(type) {
- switch (type) {
- case 'admin':
- return new AdminUser();
- case 'regular':
- return new RegularUser();
- default:
- throw new Error('Unknown user type');
- }
- }
- }
- // 工厂方法
- class Creator {
- factoryMethod() {
- throw new Error('factoryMethod must be implemented');
- }
- someOperation() {
- const product = this.factoryMethod();
- return product.operation();
- }
- }
- class ConcreteCreatorA extends Creator {
- factoryMethod() {
- return new ConcreteProductA();
- }
- }
建造者模式:
- class UserBuilder {
- constructor() {
- this.user = {};
- }
- setName(name) {
- this.user.name = name;
- return this;
- }
- setAge(age) {
- this.user.age = age;
- return this;
- }
- setEmail(email) {
- this.user.email = email;
- return this;
- }
- build() {
- return this.user;
- }
- }
- // 使用示例
- const user = new UserBuilder()
- .setName('John')
- .setAge(30)
- .setEmail('john@example.com')
- .build();
2. 结构型模式:
适配器模式:
- // 旧接口
- class OldAPI {
- getUsers() {
- return [{ name: 'John', surname: 'Doe' }];
- }
- }
- // 新接口适配器
- class UserAPIAdapter {
- constructor(oldAPI) {
- this.oldAPI = oldAPI;
- }
- getUsers() {
- return this.oldAPI.getUsers().map(user => ({
- fullName: `${user.name} ${user.surname}`
- }));
- }
- }
- // 使用示例
- const oldAPI = new OldAPI();
- const adapter = new UserAPIAdapter(oldAPI);
- console.log(adapter.getUsers()); // [{ fullName: 'John Doe' }]
装饰器模式:
- class Coffee {
- cost() {
- return 5;
- }
- description() {
- return 'Simple coffee';
- }
- }
- class MilkDecorator {
- constructor(coffee) {
- this.coffee = coffee;
- }
- cost() {
- return this.coffee.cost() + 2;
- }
- description() {
- return `${this.coffee.description()} with milk`;
- }
- }
- // 使用示例
- let coffee = new Coffee();
- coffee = new MilkDecorator(coffee);
- console.log(coffee.cost()); // 7
- console.log(coffee.description()); // "Simple coffee with milk"
代理模式:
- class RealImage {
- constructor(filename) {
- this.filename = filename;
- this.loadImage();
- }
- loadImage() {
- console.log(`Loading ${this.filename}`);
- }
- display() {
- console.log(`Displaying ${this.filename}`);
- }
- }
- class ProxyImage {
- constructor(filename) {
- this.filename = filename;
- this.realImage = null;
- }
- display() {
- if (!this.realImage) {
- this.realImage = new RealImage(this.filename);
- }
- this.realImage.display();
- }
- }
- // 使用示例
- const image = new ProxyImage('photo.jpg');
- // 图片只在需要时才加载
- image.display();
3. 行为型模式:
观察者模式:
- class Subject {
- constructor() {
- this.observers = [];
- }
- addObserver(observer) {
- this.observers.push(observer);
- }
- removeObserver(observer) {
- this.observers = this.observers.filter(obs => obs !== observer);
- }
- notify(data) {
- this.observers.forEach(observer => observer.update(data));
- }
- }
- class Observer {
- update(data) {
- console.log('Received update:', data);
- }
- }
- // 使用示例
- const subject = new Subject();
- const observer1 = new Observer();
- const observer2 = new Observer();
- subject.addObserver(observer1);
- subject.addObserver(observer2);
- subject.notify('Hello observers!');
策略模式:
- class PaymentStrategy {
- pay(amount) {
- throw new Error('pay() must be implemented');
- }
- }
- class CreditCardStrategy extends PaymentStrategy {
- constructor(cardNumber, cvv) {
- super();
- this.cardNumber = cardNumber;
- this.cvv = cvv;
- }
- pay(amount) {
- console.log(`Paid ${amount} using credit card`);
- }
- }
- class PayPalStrategy extends PaymentStrategy {
- constructor(email) {
- super();
- this.email = email;
- }
- pay(amount) {
- console.log(`Paid ${amount} using PayPal`);
- }
- }
- class ShoppingCart {
- constructor() {
- this.amount = 0;
- this.paymentStrategy = null;
- }
- setPaymentStrategy(strategy) {
- this.paymentStrategy = strategy;
- }
- checkout() {
- if (!this.paymentStrategy) {
- throw new Error('Payment strategy not set');
- }
- this.paymentStrategy.pay(this.amount);
- }
- }
- // 使用示例
- const cart = new ShoppingCart();
- cart.amount = 100;
- cart.setPaymentStrategy(new CreditCardStrategy('1234', '123'));
- cart.checkout();
命令模式:
- class Command {
- execute() {
- throw new Error('execute() must be implemented');
- }
- undo() {
- throw new Error('undo() must be implemented');
- }
- }
- class AddTextCommand extends Command {
- constructor(editor, text) {
- super();
- this.editor = editor;
- this.text = text;
- }
- execute() {
- this.editor.addText(this.text);
- }
- undo() {
- this.editor.removeText(this.text);
- }
- }
- class Editor {
- constructor() {
- this.content = '';
- }
- addText(text) {
- this.content += text;
- }
- removeText(text) {
- this.content = this.content.replace(text, '');
- }
- }
- class CommandHistory {
- constructor() {
- this.history = [];
- this.current = -1;
- }
- execute(command) {
- command.execute();
- this.history.push(command);
- this.current++;
- }
- undo() {
- if (this.current >= 0) {
- this.history[this.current].undo();
- this.current--;
- }
- }
- redo() {
- if (this.current < this.history.length - 1) {
- this.current++;
- this.history[this.current].execute();
- }
- }
- }
- // 使用示例
- const editor = new Editor();
- const history = new CommandHistory();
- const addHelloCommand = new AddTextCommand(editor, 'Hello ');
- const addWorldCommand = new AddTextCommand(editor, 'World!');
- history.execute(addHelloCommand);
- history.execute(addWorldCommand);
- console.log(editor.content); // "Hello World!"
- history.undo();
- console.log(editor.content); // "Hello "
- history.redo();
- console.log(editor.content); // "Hello World!"
实际应用场景:
表单验证策略:
- class ValidationStrategy {
- validate(value) {
- throw new Error('validate() must be implemented');
- }
- }
- class RequiredFieldStrategy extends ValidationStrategy {
- validate(value) {
- return value.trim().length > 0;
- }
- }
- class EmailStrategy extends ValidationStrategy {
- validate(value) {
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
- }
- }
- class PasswordStrategy extends ValidationStrategy {
- validate(value) {
- return value.length >= 8 && /[A-Z]/.test(value) && /[0-9]/.test(value);
- }
- }
- class FormValidator {
- constructor() {
- this.validations = new Map();
- }
- addValidation(field, strategy) {
- this.validations.set(field, strategy);
- }
- validate(formData) {
- const errors = new Map();
- for (const [field, strategy] of this.validations) {
- if (!strategy.validate(formData[field])) {
- errors.set(field, `${field} is invalid`);
- }
- }
- return errors;
- }
- }
- // 使用示例
- const validator = new FormValidator();
- validator.addValidation('email', new EmailStrategy());
- validator.addValidation('password', new PasswordStrategy());
- const formData = {
- email: 'invalid-email',
- password: 'weak'
- };
- const errors = validator.validate(formData);
- console.log(errors);
状态管理:
- class State {
- constructor(context) {
- this.context = context;
- }
- handle() {
- throw new Error('handle() must be implemented');
- }
- }
- class PlayingState extends State {
- handle() {
- console.log('Playing...');
- this.context.setState(new PausedState(this.context));
- }
- }
- class PausedState extends State {
- handle() {
- console.log('Paused');
- this.context.setState(new PlayingState(this.context));
- }
- }
- class AudioPlayer {
- constructor() {
- this.state = new PausedState(this);
- }
- setState(state) {
- this.state = state;
- }
- toggle() {
- this.state.handle();
- }
- }
- // 使用示例
- const player = new AudioPlayer();
- player.toggle(); // "Playing..."
- player.toggle(); // "Paused"
- player.toggle(); // "Playing..."
数据访问代理:
- class DataService {
- constructor() {
- this.cache = new Map();
- }
- async fetchData(url) {
- if (this.cache.has(url)) {
- console.log('Returning from cache');
- return this.cache.get(url);
- }
- console.log('Fetching from server');
- const response = await fetch(url);
- const data = await response.json();
- this.cache.set(url, data);
- return data;
- }
- clearCache() {
- this.cache.clear();
- }
- }
- // 使用示例
- const service = new DataService();
- // 第一次调用:从服务器获取
- await service.fetchData('/api/data');
- // 第二次调用:从缓存获取
- await service.fetchData('/api/data');
18. 详细解释 ES6+ 中的新特性。
答案解析:
ES6(ES2015)及后续版本引入了许多重要的新特性,极大地提升了 JavaScript 的开发体验和功能性。
1. 变量声明:
let 和 const:
- // let:块级作用域
- {
- let x = 1;
- const y = 2;
- }
- // console.log(x); // ReferenceError
- // const:常量声明
- const PI = 3.14159;
- // PI = 3; // TypeError
- // 对象和数组的 const
- const obj = { a: 1 };
- obj.a = 2; // 可以修改属性
- // obj = {}; // TypeError
- const arr = [1, 2];
- arr.push(3); // 可以修改数组
- // arr = []; // TypeError
2. 解构赋值:
数组解构:
- const [a, b, ...rest] = [1, 2, 3, 4, 5];
- console.log(a); // 1
- console.log(b); // 2
- console.log(rest); // [3, 4, 5]
- // 设置默认值
- const [x = 1, y = 2] = [10];
- console.log(x, y); // 10, 2
- // 交换变量
- let m = 1, n = 2;
- [m, n] = [n, m];
对象解构:
- const { name, age, address: { city } = {} } = {
- name: 'John',
- age: 30,
- address: { city: 'New York' }
- };
- // 重命名
- const { name: userName } = { name: 'John' };
- console.log(userName); // "John"
- // 默认值
- const { x = 1, y = 2 } = { x: 10 };
- console.log(x, y); // 10, 2
3. 模板字符串:
- const name = 'John';
- const age = 30;
- // 基本用法
- const message = `Hello, ${name}!`;
- // 多行字符串
- const template = `
- <div>
- <h1>${name}h1>
- <p>Age: ${age}p>
- div>
- `;
- // 标签模板
- function tag(strings, ...values) {
- return strings.reduce((result, str, i) =>
- `${result}${str}${values[i] || ''}`, '');
- }
- const text = tag`Hello ${name}, you are ${age} years old`;
4. 箭头函数:
- // 基本语法
- const add = (a, b) => a + b;
- // 返回对象字面量
- const getUser = () => ({ name: 'John', age: 30 });
- // this 绑定
- class Counter {
- constructor() {
- this.count = 0;
- this.increment = () => {
- this.count++;
- };
- }
- }
- // 不适合使用箭头函数的场景
- const obj = {
- name: 'John',
- // 方法定义不要使用箭头函数
- getName: () => this.name, // this 指向外部作用域
- getNameCorrect() {
- return this.name;
- }
- };
5. 类和模块:
类:
- class Animal {
- constructor(name) {
- this.name = name;
- }
- static create(name) {
- return new Animal(name);
- }
- speak() {
- return `${this.name} makes a sound`;
- }
- }
- class Dog extends Animal {
- constructor(name, breed) {
- super(name);
- this.breed = breed;
- }
- speak() {
- return `${super.speak()} - woof!`;
- }
- get info() {
- return `${this.name} is a ${this.breed}`;
- }
- set nickname(value) {
- this._nickname = value;
- }
- }
- const dog = new Dog('Rex', 'German Shepherd');
模块:
- // math.js
- export const add = (a, b) => a + b;
- export const subtract = (a, b) => a - b;
- export default class Calculator {
- // ...
- }
- // main.js
- import Calculator, { add, subtract } from './math.js';
- import * as math from './math.js';
6. Promise 和异步编程:
- // Promise 链式调用
- fetch('/api/user')
- .then(response => response.json())
- .then(user => fetch(`/api/posts/${user.id}`))
- .then(response => response.json())
- .catch(error => console.error(error));
- // Promise.all
- Promise.all([
- fetch('/api/users'),
- fetch('/api/posts'),
- fetch('/api/comments')
- ])
- .then(([users, posts, comments]) => {
- // 处理所有响应
- });
- // Promise.race
- Promise.race([
- fetch('/api/fast'),
- fetch('/api/slow')
- ])
- .then(result => {
- // 处理最快的响应
- });
- // async/await
- async function fetchUserData() {
- try {
- const response = await fetch('/api/user');
- const user = await response.json();
- return user;
- } catch (error) {
- console.error(error);
- }
- }
7. 迭代器和生成器:
- // 迭代器
- const array = ['a', 'b', 'c'];
- const iterator = array[Symbol.iterator]();
- console.log(iterator.next()); // { value: 'a', done: false }
- // 自定义迭代器
- class Range {
- constructor(start, end) {
- this.start = start;
- this.end = end;
- }
- [Symbol.iterator]() {
- let current = this.start;
- const end = this.end;
- return {
- next() {
- return current <= end
- ? { value: current++, done: false }
- : { done: true };
- }
- };
- }
- }
- // 生成器
- function* numberGenerator() {
- yield 1;
- yield 2;
- yield 3;
- }
- const gen = numberGenerator();
- console.log(gen.next()); // { value: 1, done: false }
- // 无限序列
- function* fibonacci() {
- let prev = 0, curr = 1;
- while (true) {
- yield curr;
- [prev, curr] = [curr, prev + curr];
- }
- }
- // 异步生成器
- async function* asyncGenerator() {
- const responses = [
- await fetch('/api/data1'),
- await fetch('/api/data2'),
- await fetch('/api/data3')
- ];
- for (const response of responses) {
- yield await response.json();
- }
- }
8. Set 和 Map:
- // Set
- const set = new Set([1, 2, 2, 3, 3, 4]);
- console.log(set.size); // 4
- set.add(5);
- set.delete(1);
- console.log(set.has(2)); // true
- // WeakSet
- const weakSet = new WeakSet();
- let obj = { data: 123 };
- weakSet.add(obj);
- // Map
- const map = new Map();
- map.set('name', 'John');
- map.set(obj, 'metadata');
- console.log(map.get('name')); // "John"
- console.log(map.has(obj)); // true
- // WeakMap
- const weakMap = new WeakMap();
- weakMap.set(obj, 'private data');
9. Proxy 和 Reflect:
- // Proxy
- const handler = {
- get(target, prop) {
- console.log(`Accessing ${prop}`);
- return target[prop];
- },
- set(target, prop, value) {
- console.log(`Setting ${prop} = ${value}`);
- target[prop] = value;
- return true;
- }
- };
- const user = new Proxy({}, handler);
- user.name = 'John'; // "Setting name = John"
- console.log(user.name); // "Accessing name", "John"
- // Reflect
- const obj = { name: 'John' };
- console.log(Reflect.get(obj, 'name')); // "John"
- Reflect.set(obj, 'age', 30);
- console.log(Reflect.has(obj, 'age')); // true
10. 其他特性:
可选链操作符:
- const user = {
- address: {
- street: 'Main St'
- }
- };
- console.log(user?.address?.street); // "Main St"
- console.log(user?.contact?.phone); // undefined
空值合并操作符:
- const value = null ?? 'default';
- console.log(value); // "default"
- const count = 0 ?? 42;
- console.log(count); // 0
逻辑赋值操作符:
- let x = null;
- x ??= 42;
- console.log(x); // 42
- let y = 0;
- y ||= 42;
- console.log(y); // 42
- let z = 1;
- z &&= 42;
- console.log(z); // 42
BigInt:
- const bigInt = 9007199254740991n;
- const result = bigInt + 1n;
- console.log(result); // 9007199254740992n
globalThis:
- // 在任何环境下都指向全局对象
- console.log(globalThis === window); // true (在浏览器中)
- console.log(globalThis === global); // true (在 Node.js 中)
实际应用示例:
现代模块化开发:
- // api.js
- export class API {
- constructor(baseURL) {
- this.baseURL = baseURL;
- }
- async fetch(endpoint, options = {}) {
- const response = await fetch(`${this.baseURL}${endpoint}`, {
- headers: {
- 'Content-Type': 'application/json',
- ...options.headers
- },
- ...options
- });
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
- return response.json();
- }
- }
- // services.js
- import { API } from './api.js';
- export class UserService {
- constructor(api) {
- this.api = api;
- }
- async getUser(id) {
- return this.api.fetch(`/users/${id}`);
- }
- async updateUser(id, data) {
- return this.api.fetch(`/users/${id}`, {
- method: 'PUT',
- body: JSON.stringify(data)
- });
- }
- }
- // main.js
- import { API } from './api.js';
- import { UserService } from './services.js';
- const api = new API('https://api.example.com');
- const userService = new UserService(api);
- async function init() {
- try {
- const user = await userService.getUser(1);
- console.log(user);
- } catch (error) {
- console.error(error);
- }
- }
响应式数据绑定:
- class Observable {
- constructor(value) {
- this._value = value;
- this._subscribers = new Set();
- return new Proxy(this, {
- get: (target, prop) => {
- if (prop === 'value') {
- return target._value;
- }
- return target[prop];
- },
- set: (target, prop, value) => {
- if (prop === 'value') {
- target._value = value;
- target.notify();
- } else {
- target[prop] = value;
- }
- return true;
- }
- });
- }
- subscribe(callback) {
- this._subscribers.add(callback);
- return () => this._subscribers.delete(callback);
- }
- notify() {
- for (const callback of this._subscribers) {
- callback(this._value);
- }
- }
- }
- // 使用示例
- const counter = new Observable(0);
- const unsubscribe = counter.subscribe(value => {
- console.log('Counter changed:', value);
- });
- counter.value++; // "Counter changed: 1"
- counter.value++; // "Counter changed: 2"
- unsubscribe(); // 取消订阅
异步任务队列:
- class AsyncQueue {
- constructor() {
- this.queue = [];
- this.running = false;
- }
- async *[Symbol.asyncIterator]() {
- while (true) {
- if (this.queue.length === 0) {
- await new Promise(resolve => {
- this.queue.push = (...items) => {
- Array.prototype.push.apply(this.queue, items);
- resolve();
- this.queue.push = Array.prototype.push;
- };
- });
- }
- yield this.queue.shift();
- }
- }
- enqueue(...items) {
- this.queue.push(...items);
- }
- async process(callback) {
- if (this.running) return;
- this.running = true;
- try {
- for await (const item of this) {
- await callback(item);
- }
- } finally {
- this.running = false;
- }
- }
- }
- // 使用示例
- const queue = new AsyncQueue();
- // 启动处理
- queue.process(async item => {
- console.log('Processing:', item);
- await new Promise(resolve => setTimeout(resolve, 1000));
- });
- // 添加任务
- queue.enqueue('Task 1', 'Task 2', 'Task 3');
- // 稍后添加更多任务
- setTimeout(() => {
- queue.enqueue('Task 4', 'Task 5');
- }, 3000);
19. 详细解释 ES6+ 中的装饰器(Decorator)。
答案解析:
装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、访问符、属性或参数上。装饰器使用 @expression 这种形式,expression 求值后必须为一个函数。
1. 类装饰器:
- // 类装饰器
- function log(target) {
- // 保存原始构造函数
- const original = target;
- // 返回新的构造函数
- function construct(constructor, args) {
- console.log(`Creating new instance of ${constructor.name}`);
- const instance = new constructor(...args);
- return instance;
- }
- // 创建新的类
- const newConstructor = function (...args) {
- return construct(original, args);
- };
- // 复制原型链
- newConstructor.prototype = original.prototype;
- return newConstructor;
- }
- @log
- class Example {
- constructor(name) {
- this.name = name;
- }
- }
- const example = new Example('test'); // 输出: "Creating new instance of Example"
2. 方法装饰器:
- // 方法装饰器
- function measure(target, propertyKey, descriptor) {
- const originalMethod = descriptor.value;
- descriptor.value = function (...args) {
- const start = performance.now();
- const result = originalMethod.apply(this, args);
- const end = performance.now();
- console.log(`${propertyKey} took ${end - start}ms`);
- return result;
- };
- return descriptor;
- }
- class Calculator {
- @measure
- add(a, b) {
- return a + b;
- }
- }
- const calc = new Calculator();
- calc.add(1, 2); // 输出执行时间
3. 属性装饰器:
- // 属性装饰器
- function readonly(target, propertyKey) {
- Object.defineProperty(target, propertyKey, {
- writable: false
- });
- }
- class Example {
- @readonly
- pi = 3.14159;
- }
- const e = new Example();
- // e.pi = 3; // TypeError: Cannot assign to read only property 'pi'
4. 参数装饰器:
- // 参数装饰器
- function validate(target, propertyKey, parameterIndex) {
- const validateParams = target[propertyKey].validateParams || [];
- validateParams[parameterIndex] = true;
- target[propertyKey].validateParams = validateParams;
- }
- class UserService {
- createUser(@validate name: string, @validate age: number) {
- // 实现创建用户的逻辑
- }
- }
5. 多个装饰器组合:
- // 装饰器组合
- function first() {
- console.log("first(): factory evaluated");
- return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
- console.log("first(): called");
- };
- }
- function second() {
- console.log("second(): factory evaluated");
- return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
- console.log("second(): called");
- };
- }
- class Example {
- @first()
- @second()
- method() {}
- }
- // 输出顺序:
- // first(): factory evaluated
- // second(): factory evaluated
- // second(): called
- // first(): called
实际应用示例:
日志记录装饰器:
- function log(target, propertyKey, descriptor) {
- const originalMethod = descriptor.value;
- descriptor.value = function (...args) {
- console.log(`Calling ${propertyKey} with args:`, args);
- try {
- const result = originalMethod.apply(this, args);
- console.log(`${propertyKey} returned:`, result);
- return result;
- } catch (error) {
- console.error(`${propertyKey} threw error:`, error);
- throw error;
- }
- };
- return descriptor;
- }
- class UserService {
- @log
- async createUser(userData) {
- // 实现创建用户的逻辑
- }
- @log
- async updateUser(id, userData) {
- // 实现更新用户的逻辑
- }
- }
缓存装饰器:
- function memoize(target, propertyKey, descriptor) {
- const originalMethod = descriptor.value;
- const cache = new Map();
- descriptor.value = function (...args) {
- const key = JSON.stringify(args);
- if (cache.has(key)) {
- return cache.get(key);
- }
- const result = originalMethod.apply(this, args);
- cache.set(key, result);
- return result;
- };
- return descriptor;
- }
- class MathUtils {
- @memoize
- fibonacci(n) {
- if (n <= 1) return n;
- return this.fibonacci(n - 1) + this.fibonacci(n - 2);
- }
- }
权限控制装饰器:
- function requireRole(role) {
- return function (target, propertyKey, descriptor) {
- const originalMethod = descriptor.value;
- descriptor.value = function (...args) {
- const user = getCurrentUser(); // 假设这个函数获取当前用户
- if (!user || !user.roles.includes(role)) {
- throw new Error('Unauthorized');
- }
- return originalMethod.apply(this, args);
- };
- return descriptor;
- };
- }
- class AdminPanel {
- @requireRole('admin')
- deleteUser(userId) {
- // 实现删除用户的逻辑
- }
- @requireRole('moderator')
- banUser(userId) {
- // 实现封禁用户的逻辑
- }
- }
防抖装饰器:
- function debounce(wait) {
- return function (target, propertyKey, descriptor) {
- const originalMethod = descriptor.value;
- let timeout;
- descriptor.value = function (...args) {
- clearTimeout(timeout);
- timeout = setTimeout(() => {
- originalMethod.apply(this, args);
- }, wait);
- };
- return descriptor;
- };
- }
- class SearchComponent {
- @debounce(300)
- search(query) {
- // 实现搜索逻辑
- }
- }
验证装饰器:
- function validate(schema) {
- return function (target, propertyKey, descriptor) {
- const originalMethod = descriptor.value;
- descriptor.value = function (...args) {
- const validationResult = schema.validate(args[0]);
- if (validationResult.error) {
- throw new Error(validationResult.error.message);
- }
- return originalMethod.apply(this, args);
- };
- return descriptor;
- };
- }
- const userSchema = {
- validate(data) {
- const errors = [];
- if (!data.name) errors.push('Name is required');
- if (!data.email) errors.push('Email is required');
- return {
- error: errors.length ? { message: errors.join(', ') } : null
- };
- }
- };
- class UserController {
- @validate(userSchema)
- createUser(userData) {
- // 实现创建用户的逻辑
- }
- }
20. 详细解释 ES6+ 中的 Reflect 和 Proxy API。
答案解析:
Reflect 和 Proxy API 提供了拦截和自定义对象操作的能力,是实现元编程的重要工具。
1. Reflect API:
Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。
- // 基本操作
- const obj = { name: 'John' };
- // 获取属性
- console.log(Reflect.get(obj, 'name')); // "John"
- // 设置属性
- Reflect.set(obj, 'age', 30);
- // 删除属性
- Reflect.deleteProperty(obj, 'age');
- // 检查属性
- console.log(Reflect.has(obj, 'name')); // true
- // 获取所有属性
- console.log(Reflect.ownKeys(obj)); // ["name"]
- // 创建对象
- const instance = Reflect.construct(Array, [1, 2, 3]);
- // 调用函数
- const result = Reflect.apply(Math.max, null, [1, 2, 3]);
2. Proxy API:
Proxy 用于创建一个对象的代理,从而实现基本操作的拦截和自定义。
- // 基本用法
- const target = {
- name: 'John',
- age: 30
- };
- const handler = {
- get(target, prop) {
- console.log(`Accessing ${prop}`);
- return target[prop];
- },
- set(target, prop, value) {
- console.log(`Setting ${prop} = ${value}`);
- target[prop] = value;
- return true;
- }
- };
- const proxy = new Proxy(target, handler);
- // 使用代理
- proxy.name; // "Accessing name"
- proxy.age = 31; // "Setting age = 31"
3. 常用的代理处理器:
- const handler = {
- // 属性读取
- get(target, prop) {
- return target[prop];
- },
- // 属性设置
- set(target, prop, value) {
- target[prop] = value;
- return true;
- },
- // 属性删除
- deleteProperty(target, prop) {
- return delete target[prop];
- },
- // 属性存在性判断
- has(target, prop) {
- return prop in target;
- },
- // 获取属性描述符
- getOwnPropertyDescriptor(target, prop) {
- return Object.getOwnPropertyDescriptor(target, prop);
- },
- // 定义属性
- defineProperty(target, prop, descriptor) {
- return Object.defineProperty(target, prop, descriptor);
- },
- // 获取原型
- getPrototypeOf(target) {
- return Object.getPrototypeOf(target);
- },
- // 设置原型
- setPrototypeOf(target, proto) {
- return Object.setPrototypeOf(target, proto);
- }
- };
实际应用示例:
私有属性代理:
- function createPrivateProxy(target) {
- const privateProps = new WeakMap();
- return new Proxy(target, {
- get(target, prop) {
- if (prop.startsWith('_')) {
- throw new Error('Cannot access private property');
- }
- return target[prop];
- },
- set(target, prop, value) {
- if (prop.startsWith('_')) {
- privateProps.set(target, {
- ...privateProps.get(target),
- [prop]: value
- });
- } else {
- target[prop] = value;
- }
- return true;
- }
- });
- }
- class User {
- constructor(name) {
- this._password = 'secret';
- this.name = name;
- }
- }
- const user = createPrivateProxy(new User('John'));
- console.log(user.name); // "John"
- // console.log(user._password); // Error: Cannot access private property
验证代理:
- function createValidationProxy(target, validations) {
- return new Proxy(target, {
- set(target, prop, value) {
- if (validations[prop]) {
- const validation = validations[prop];
- if (!validation(value)) {
- throw new Error(`Invalid value for ${prop}`);
- }
- }
- target[prop] = value;
- return true;
- }
- });
- }
- const user = createValidationProxy({}, {
- age: value => typeof value === 'number' && value >= 0 && value <= 120,
- email: value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
- });
- user.age = 30; // OK
- // user.age = -1; // Error: Invalid value for age
- user.email = 'john@example.com'; // OK
- // user.email = 'invalid'; // Error: Invalid value for email
日志代理:
- function createLoggingProxy(target, name) {
- return new Proxy(target, {
- get(target, prop) {
- console.log(`[${name}] Getting ${prop}`);
- return target[prop];
- },
- set(target, prop, value) {
- console.log(`[${name}] Setting ${prop} = ${value}`);
- target[prop] = value;
- return true;
- },
- deleteProperty(target, prop) {
- console.log(`[${name}] Deleting ${prop}`);
- return delete target[prop];
- }
- });
- }
- const user = createLoggingProxy({
- name: 'John',
- age: 30
- }, 'UserObject');
- user.name; // [UserObject] Getting name
- user.age = 31; // [UserObject] Setting age = 31
- delete user.age; // [UserObject] Deleting age
缓存代理:
- function createCachingProxy(target) {
- const cache = new Map();
- return new Proxy(target, {
- get(target, prop) {
- if (cache.has(prop)) {
- console.log(`Cache hit for ${prop}`);
- return cache.get(prop);
- }
- const value = target[prop];
- if (typeof value === 'function') {
- return new Proxy(value, {
- apply(target, thisArg, args) {
- const key = `${prop}(${JSON.stringify(args)})`;
- if (cache.has(key)) {
- console.log(`Cache hit for ${key}`);
- return cache.get(key);
- }
- const result = target.apply(thisArg, args);
- cache.set(key, result);
- return result;
- }
- });
- }
- cache.set(prop, value);
- return value;
- }
- });
- }
- const calculator = createCachingProxy({
- add(a, b) {
- console.log('Computing...');
- return a + b;
- }
- });
- console.log(calculator.add(1, 2)); // Computing... 3
- console.log(calculator.add(1, 2)); // Cache hit for add([1,2]) 3
只读代理:
- function createReadOnlyProxy(target) {
- return new Proxy(target, {
- get(target, prop) {
- const value = target[prop];
- if (typeof value === 'object' && value !== null) {
- return createReadOnlyProxy(value);
- }
- return value;
- },
- set() {
- throw new Error('Cannot modify read-only object');
- },
- deleteProperty() {
- throw new Error('Cannot modify read-only object');
- },
- defineProperty() {
- throw new Error('Cannot modify read-only object');
- }
- });
- }
- const config = createReadOnlyProxy({
- api: {
- url: 'https://api.example.com',
- timeout: 5000
- }
- });
- console.log(config.api.url); // "https://api.example.com"
- // config.api.url = 'new-url'; // Error: Cannot modify read-only object
Vue.js 框架
21. 详细解释 Vue.js 的响应式原理。
答案解析:
Vue.js 的响应式系统是其核心特性之一,它通过数据劫持和发布订阅模式实现数据与视图的自动同步。
1. Vue 2.x 响应式原理:
Vue 2.x 使用 Object.defineProperty 实现数据劫持:
- // 简化版响应式实现
- function observe(obj) {
- if (typeof obj !== 'object' || obj === null) {
- return obj;
- }
- Object.keys(obj).forEach(key => {
- defineReactive(obj, key, obj[key]);
- });
- }
- function defineReactive(obj, key, val) {
- // 递归处理嵌套对象
- observe(val);
- const dep = new Dep();
- Object.defineProperty(obj, key, {
- get() {
- // 依赖收集
- if (Dep.target) {
- dep.depend();
- }
- return val;
- },
- set(newVal) {
- if (newVal === val) return;
- val = newVal;
- // 触发更新
- dep.notify();
- }
- });
- }
- // 发布订阅系统
- class Dep {
- constructor() {
- this.subscribers = new Set();
- }
- depend() {
- if (Dep.target) {
- this.subscribers.add(Dep.target);
- }
- }
- notify() {
- this.subscribers.forEach(sub => sub.update());
- }
- }
- // 观察者
- class Watcher {
- constructor(vm, key, callback) {
- this.vm = vm;
- this.key = key;
- this.callback = callback;
- // 触发 getter 进行依赖收集
- Dep.target = this;
- this.vm[this.key];
- Dep.target = null;
- }
- update() {
- this.callback.call(this.vm, this.vm[this.key]);
- }
- }
2. Vue 3.x 响应式原理:
Vue 3.x 使用 Proxy 实现数据劫持:
- // 简化版响应式实现
- function reactive(target) {
- if (typeof target !== 'object' || target === null) {
- return target;
- }
- const handler = {
- get(target, key, receiver) {
- // 依赖收集
- track(target, key);
- const result = Reflect.get(target, key, receiver);
- if (typeof result === 'object') {
- return reactive(result);
- }
- return result;
- },
- set(target, key, value, receiver) {
- const oldValue = target[key];
- const result = Reflect.set(target, key, value, receiver);
- if (oldValue !== value) {
- // 触发更新
- trigger(target, key);
- }
- return result;
- }
- };
- return new Proxy(target, handler);
- }
- // 依赖收集
- let activeEffect;
- const targetMap = new WeakMap();
- function track(target, key) {
- if (!activeEffect) return;
- let depsMap = targetMap.get(target);
- if (!depsMap) {
- targetMap.set(target, (depsMap = new Map()));
- }
- let dep = depsMap.get(key);
- if (!dep) {
- depsMap.set(key, (dep = new Set()));
- }
- dep.add(activeEffect);
- }
- // 触发更新
- function trigger(target, key) {
- const depsMap = targetMap.get(target);
- if (!depsMap) return;
- const dep = depsMap.get(key);
- if (dep) {
- dep.forEach(effect => effect());
- }
- }
- // 副作用函数
- function effect(fn) {
- const effectFn = () => {
- activeEffect = effectFn;
- fn();
- activeEffect = null;
- };
- effectFn();
- return effectFn;
- }
3. Vue 2.x vs Vue 3.x 响应式系统的区别:
实现方式:
Vue 2.x:Object.defineProperty
Vue 3.x:Proxy
优势对比:
- // Vue 2.x 的限制
- const data = {
- list: []
- };
- // 无法检测数组长度变化
- data.list.length = 2;
- // 无法检测对象属性的添加和删除
- data.newProp = 'value';
- // Vue 3.x 的改进
- const state = reactive({
- list: []
- });
- // 可以检测数组长度变化
- state.list.length = 2;
- // 可以检测对象属性的添加和删除
- state.newProp = 'value';
性能对比:
Vue 2.x:需要递归遍历对象进行劫持
Vue 3.x:惰性劫持,访问时才进行代理
实际应用示例:
简单的响应式系统:
- // Vue 3.x 风格的响应式实现
- function createReactiveStore() {
- const state = reactive({
- count: 0,
- todos: []
- });
- const actions = {
- increment() {
- state.count++;
- },
- addTodo(text) {
- state.todos.push({
- id: Date.now(),
- text,
- completed: false
- });
- },
- toggleTodo(id) {
- const todo = state.todos.find(t => t.id === id);
- if (todo) {
- todo.completed = !todo.completed;
- }
- }
- };
- return { state, actions };
- }
- const store = createReactiveStore();
- // 创建观察者
- effect(() => {
- console.log('Count changed:', store.state.count);
- });
- effect(() => {
- console.log('Todos changed:', store.state.todos.length);
- });
- // 触发更新
- store.actions.increment(); // 输出: Count changed: 1
- store.actions.addTodo('Learn Vue'); // 输出: Todos changed: 1
计算属性实现:
- function computed(getter) {
- let value;
- let dirty = true;
- const effectFn = effect(getter, {
- lazy: true,
- scheduler() {
- if (!dirty) {
- dirty = true;
- trigger(computedRef, 'value');
- }
- }
- });
- const computedRef = {
- get value() {
- if (dirty) {
- value = effectFn();
- dirty = false;
- }
- track(computedRef, 'value');
- return value;
- }
- };
- return computedRef;
- }
- // 使用示例
- const state = reactive({
- count: 0
- });
- const double = computed(() => state.count * 2);
- effect(() => {
- console.log('Double:', double.value);
- });
- state.count++; // 输出: Double: 2
监听器实现:
- function watch(source, callback) {
- let oldValue;
- const getter = typeof source === 'function'
- ? source
- : () => traverse(source);
- const effectFn = effect(getter, {
- lazy: true,
- scheduler() {
- const newValue = effectFn();
- callback(newValue, oldValue);
- oldValue = newValue;
- }
- });
- oldValue = effectFn();
- }
- // 深度遍历对象
- function traverse(value, seen = new Set()) {
- if (typeof value !== 'object' || value === null || seen.has(value)) {
- return value;
- }
- seen.add(value);
- if (Array.isArray(value)) {
- for (let i = 0; i < value.length; i++) {
- traverse(value[i], seen);
- }
- } else {
- for (const key of Object.keys(value)) {
- traverse(value[key], seen);
- }
- }
- return value;
- }
- // 使用示例
- const state = reactive({
- user: {
- name: 'John',
- age: 30
- }
- });
- watch(
- () => state.user,
- (newValue, oldValue) => {
- console.log('User changed:', newValue, oldValue);
- }
- );
- state.user.age = 31; // 输出: User changed: { name: 'John', age: 31 } { name: 'John', age: 30 }
22. 详细解释 Vue.js 的生命周期。
答案解析:
Vue.js 的生命周期包含了组件从创建到销毁的整个过程,每个阶段都有对应的钩子函数。
1. Vue 2.x 生命周期:
- export default {
- // 组件实例创建之前
- beforeCreate() {
- // 此时无法访问 data 和 methods
- console.log('beforeCreate');
- },
- // 组件实例创建完成
- created() {
- // 可以访问 data 和 methods,但还未挂载到 DOM
- console.log('created');
- this.fetchData();
- },
- // 组件挂载之前
- beforeMount() {
- // 模板编译完成,但还未渲染到 DOM
- console.log('beforeMount');
- },
- // 组件挂载完成
- mounted() {
- // 可以访问 DOM 元素
- console.log('mounted');
- this.initChart();
- },
- // 数据更新前
- beforeUpdate() {
- // 可以在更新前访问现有的 DOM
- console.log('beforeUpdate');
- },
- // 数据更新后
- updated() {
- // DOM 已经更新完成
- console.log('updated');
- },
- // 组件销毁前
- beforeDestroy() {
- // 清理定时器、事件监听器等
- console.log('beforeDestroy');
- this.cleanup();
- },
- // 组件销毁后
- destroyed() {
- // 组件完全销毁
- console.log('destroyed');
- }
- }
2. Vue 3.x 生命周期:
- import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue';
- export default {
- setup() {
- // 组件实例创建之前
- onBeforeMount(() => {
- console.log('onBeforeMount');
- });
- // 组件挂载完成
- onMounted(() => {
- console.log('onMounted');
- });
- // 数据更新前
- onBeforeUpdate(() => {
- console.log('onBeforeUpdate');
- });
- // 数据更新后
- onUpdated(() => {
- console.log('onUpdated');
- });
- // 组件销毁前
- onBeforeUnmount(() => {
- console.log('onBeforeUnmount');
- });
- // 组件销毁后
- onUnmounted(() => {
- console.log('onUnmounted');
- });
- }
- }
3. 生命周期最佳实践:
初始化数据:
- export default {
- data() {
- return {
- loading: false,
- data: null,
- error: null
- };
- },
- async created() {
- try {
- this.loading = true;
- this.data = await this.fetchData();
- } catch (error) {
- this.error = error;
- } finally {
- this.loading = false;
- }
- }
- }
DOM 操作和第三方库集成:
- export default {
- data() {
- return {
- chart: null
- };
- },
- mounted() {
- this.initChart();
- window.addEventListener('resize', this.handleResize);
- },
- beforeDestroy() {
- if (this.chart) {
- this.chart.dispose();
- }
- window.removeEventListener('resize', this.handleResize);
- },
- methods: {
- initChart() {
- this.chart = echarts.init(this.$refs.chart);
- this.updateChart();
- },
- handleResize() {
- if (this.chart) {
- this.chart.resize();
- }
- }
- }
- }
数据监听和缓存:
- export default {
- data() {
- return {
- searchQuery: '',
- debounceTimer: null
- };
- },
- watch: {
- searchQuery(newVal) {
- if (this.debounceTimer) {
- clearTimeout(this.debounceTimer);
- }
- this.debounceTimer = setTimeout(() => {
- this.search(newVal);
- }, 300);
- }
- },
- beforeDestroy() {
- if (this.debounceTimer) {
- clearTimeout(this.debounceTimer);
- }
- }
- }
实际应用示例:
数据加载组件:
- // DataLoader.vue
- export default {
- props: {
- fetchData: {
- type: Function,
- required: true
- }
- },
- data() {
- return {
- loading: false,
- data: null,
- error: null,
- retryCount: 0
- };
- },
- created() {
- this.loadData();
- },
- methods: {
- async loadData() {
- this.loading = true;
- this.error = null;
- try {
- this.data = await this.fetchData();
- } catch (error) {
- this.error = error;
- if (this.retryCount < 3) {
- this.retryCount++;
- setTimeout(() => {
- this.loadData();
- }, 1000 * this.retryCount);
- }
- } finally {
- this.loading = false;
- }
- }
- },
- render() {
- return this.$scopedSlots.default({
- loading: this.loading,
- data: this.data,
- error: this.error,
- retry: this.loadData
- });
- }
- }
图表组件:
- // ChartComponent.vue
- export default {
- props: {
- data: {
- type: Array,
- required: true
- },
- options: {
- type: Object,
- default: () => ({})
- }
- },
- data() {
- return {
- chart: null,
- resizeObserver: null
- };
- },
- mounted() {
- this.initChart();
- this.initResizeObserver();
- },
- beforeDestroy() {
- this.cleanup();
- },
- methods: {
- initChart() {
- this.chart = echarts.init(this.$refs.chart);
- this.updateChart();
- },
- updateChart() {
- if (!this.chart) return;
- const options = {
- ...this.defaultOptions,
- ...this.options,
- series: this.data
- };
- this.chart.setOption(options);
- },
- initResizeObserver() {
- this.resizeObserver = new ResizeObserver(() => {
- if (this.chart) {
- this.chart.resize();
- }
- });
- this.resizeObserver.observe(this.$refs.chart);
- },
- cleanup() {
- if (this.chart) {
- this.chart.dispose();
- this.chart = null;
- }
- if (this.resizeObserver) {
- this.resizeObserver.disconnect();
- this.resizeObserver = null;
- }
- }
- },
- watch: {
- data: {
- handler: 'updateChart',
- deep: true
- },
- options: {
- handler: 'updateChart',
- deep: true
- }
- }
- }
表单组件:
- // FormComponent.vue
- export default {
- data() {
- return {
- form: this.initForm(),
- validators: {},
- touched: {},
- errors: {}
- };
- },
- created() {
- this.initValidators();
- },
- beforeDestroy() {
- // 保存草稿或清理
- this.saveDraft();
- },
- methods: {
- initForm() {
- return {
- // 初始化表单数据
- };
- },
- initValidators() {
- // 设置验证规则
- },
- validate() {
- // 执行验证
- },
- saveDraft() {
- localStorage.setItem('formDraft', JSON.stringify(this.form));
- },
- async submit() {
- if (!this.validate()) return;
- try {
- await this.submitForm(this.form);
- this.$emit('success');
- } catch (error) {
- this.$emit('error', error);
- }
- }
- },
- watch: {
- form: {
- handler(newVal) {
- // 自动保存
- this.saveDraft();
- // 自动验证
- this.validate();
- },
- deep: true
- }
- }
- }
23. 详细解释 Vue.js 的组件通信方式。
答案解析:
Vue.js 提供了多种组件间通信的方式,每种方式都有其适用场景。
1. Props 和 Events:
父子组件最基本的通信方式:
- // 父组件
- <template>
- <child-component
- :message="message"
- @update="handleUpdate"
- />
- template>
- <script>
- export default {
- data() {
- return {
- message: 'Hello'
- };
- },
- methods: {
- handleUpdate(value) {
- console.log('Received:', value);
- }
- }
- };
- script>
- // 子组件
- <template>
- <div>
- <p>{{ message }}p>
- <button @click="sendUpdate">Updatebutton>
- div>
- template>
- <script>
- export default {
- props: {
- message: {
- type: String,
- required: true
- }
- },
- methods: {
- sendUpdate() {
- this.$emit('update', 'New value');
- }
- }
- };
- script>
2. Provide/Inject:
跨多级组件传递数据:
- // 根组件
- export default {
- provide() {
- return {
- theme: this.theme,
- updateTheme: this.updateTheme
- };
- },
- data() {
- return {
- theme: 'light'
- };
- },
- methods: {
- updateTheme(newTheme) {
- this.theme = newTheme;
- }
- }
- };
- // 深层子组件
- export default {
- inject: ['theme', 'updateTheme'],
- methods: {
- toggleTheme() {
- this.updateTheme(this.theme === 'light' ? 'dark' : 'light');
- }
- }
- };
3. EventBus:
非父子组件间的通信:
- // eventBus.js
- import Vue from 'vue';
- export const eventBus = new Vue();
- // 组件 A
- import { eventBus } from './eventBus';
- export default {
- methods: {
- sendMessage() {
- eventBus.$emit('message', 'Hello from A');
- }
- }
- };
- // 组件 B
- import { eventBus } from './eventBus';
- export default {
- created() {
- eventBus.$on('message', this.handleMessage);
- },
- beforeDestroy() {
- eventBus.$off('message', this.handleMessage);
- },
- methods: {
- handleMessage(msg) {
- console.log(msg);
- }
- }
- };
4. Vuex:
全局状态管理:
- // store.js
- import Vue from 'vue';
- import Vuex from 'vuex';
- Vue.use(Vuex);
- export default new Vuex.Store({
- state: {
- count: 0,
- todos: []
- },
- mutations: {
- INCREMENT(state) {
- state.count++;
- },
- ADD_TODO(state, todo) {
- state.todos.push(todo);
- }
- },
- actions: {
- async fetchTodos({ commit }) {
- const todos = await api.getTodos();
- todos.forEach(todo => commit('ADD_TODO', todo));
- }
- },
- getters: {
- completedTodos: state => state.todos.filter(todo => todo.completed)
- }
- });
- // 组件中使用
- export default {
- computed: {
- ...mapState(['count']),
- ...mapGetters(['completedTodos'])
- },
- methods: {
- ...mapMutations(['INCREMENT']),
- ...mapActions(['fetchTodos'])
- }
- };
5. $refs 和 $parent/$children:
直接访问组件实例:
- // 父组件
- <template>
- <div>
- <child-component ref="child" />
- <button @click="callChildMethod">Call Childbutton>
- div>
- template>
- <script>
- export default {
- methods: {
- callChildMethod() {
- this.$refs.child.someMethod();
- }
- }
- };
- script>
- // 子组件
- export default {
- methods: {
- someMethod() {
- console.log('Called from parent');
- },
- callParentMethod() {
- this.$parent.parentMethod();
- }
- }
- };
实际应用示例:
表单组件通信:
- // FormContainer.vue
- <template>
- <div>
- <form-item
- v-for="(field, index) in fields"
- :key="index"
- v-model="formData[field.name]"
- :field="field"
- @validate="handleValidate"
- />
- <button @click="submit">Submitbutton>
- div>
- template>
- <script>
- export default {
- provide() {
- return {
- form: this
- };
- },
- data() {
- return {
- formData: {},
- validationResults: {}
- };
- },
- methods: {
- handleValidate(fieldName, isValid, error) {
- this.validationResults[fieldName] = { isValid, error };
- },
- async submit() {
- if (Object.values(this.validationResults).every(r => r.isValid)) {
- await this.submitForm(this.formData);
- }
- }
- }
- };
- script>
- // FormItem.vue
- <template>
- <div class="form-item">
- <label>{{ field.label }}label>
- <input
- :value="value"
- @input="handleInput"
- @blur="validate"
- />
- <span class="error" v-if="error">{{ error }}span>
- div>
- template>
- <script>
- export default {
- inject: ['form'],
- props: ['value', 'field'],
- data() {
- return {
- error: null
- };
- },
- methods: {
- handleInput(e) {
- this.$emit('input', e.target.value);
- this.validate();
- },
- validate() {
- const value = this.value;
- const rules = this.field.rules;
- let isValid = true;
- let error = null;
- // 执行验证
- if (rules.required && !value) {
- isValid = false;
- error = `${this.field.label} is required`;
- }
- this.error = error;
- this.$emit('validate', this.field.name, isValid, error);
- }
- }
- };
- script>
多级菜单组件:
- // Menu.vue
- <template>
- <div class="menu">
- <menu-item
- v-for="item in items"
- :key="item.id"
- :item="item"
- />
- div>
- template>
- <script>
- export default {
- provide() {
- return {
- rootMenu: this
- };
- },
- data() {
- return {
- activeItem: null
- };
- },
- methods: {
- setActiveItem(item) {
- this.activeItem = item;
- }
- }
- };
- script>
- // MenuItem.vue
- <template>
- <div
- class="menu-item"
- :class="{ active: isActive }"
- @click="handleClick"
- >
- {{ item.label }}
- <div v-if="item.children" class="submenu">
- <menu-item
- v-for="child in item.children"
- :key="child.id"
- :item="child"
- />
- div>
- div>
- template>
- <script>
- export default {
- name: 'MenuItem',
- inject: ['rootMenu'],
- props: ['item'],
- computed: {
- isActive() {
- return this.rootMenu.activeItem === this.item;
- }
- },
- methods: {
- handleClick() {
- this.rootMenu.setActiveItem(this.item);
- if (this.item.onClick) {
- this.item.onClick();
- }
- }
- }
- };
- script>
全局消息通知:
- // messagePlugin.js
- let messageInstance = null;
- export default {
- install(Vue) {
- Vue.prototype.$message = this.showMessage;
- },
- showMessage({ type = 'info', message, duration = 3000 }) {
- if (!messageInstance) {
- const MessageConstructor = Vue.extend({
- render(h) {
- return h('div', {
- class: ['message', `message-${type}`]
- }, message);
- }
- });
- messageInstance = new MessageConstructor();
- const vm = messageInstance.$mount();
- document.body.appendChild(vm.$el);
- setTimeout(() => {
- document.body.removeChild(vm.$el);
- messageInstance = null;
- }, duration);
- }
- }
- };
- // 使用示例
- this.$message({
- type: 'success',
- message: 'Operation successful'
- });
24. 详细解释 Vue.js 的自定义指令。
答案解析:
Vue.js 的自定义指令用于对底层 DOM 元素进行直接操作,当内置指令无法满足需求时非常有用。
1. 指令定义:
- // 全局指令
- Vue.directive('focus', {
- // 指令绑定到元素时调用
- bind(el, binding, vnode) {
- // 进行初始化设置
- },
- // 被绑定元素插入父节点时调用
- inserted(el, binding, vnode) {
- el.focus();
- },
- // 组件更新时调用
- update(el, binding, vnode, oldVnode) {
- // 根据值的变化进行更新
- },
- // 组件更新完成后调用
- componentUpdated(el, binding, vnode, oldVnode) {
- // 组件和子组件更新完成后进行操作
- },
- // 指令与元素解绑时调用
- unbind(el, binding, vnode) {
- // 进行清理工作
- }
- });
- // 局部指令
- export default {
- directives: {
- focus: {
- inserted(el) {
- el.focus();
- }
- }
- }
- };
2. 指令钩子函数参数:
- Vue.directive('example', {
- bind(el, binding, vnode) {
- // el: 指令绑定的元素
- // binding: 指令的详细信息
- // - name: 指令名
- // - value: 指令的值
- // - oldValue: 指令前一个值
- // - expression: 指令表达式
- // - arg: 指令参数
- // - modifiers: 指令修饰符
- // vnode: Vue 编译生成的虚拟节点
- console.log(binding.value); // 指令值
- console.log(binding.arg); // 指令参数
- console.log(binding.modifiers); // 指令修饰符
- }
- });
3. 实际应用示例:
点击外部关闭指令:
- Vue.directive('click-outside', {
- bind(el, binding) {
- el.clickOutsideHandler = (event) => {
- if (!(el === event.target || el.contains(event.target))) {
- binding.value(event);
- }
- };
- document.addEventListener('click', el.clickOutsideHandler);
- },
- unbind(el) {
- document.removeEventListener('click', el.clickOutsideHandler);
- }
- });
- // 使用示例
- <template>
- <div v-click-outside="closeDropdown">
- div>
- template>
图片懒加载指令:
- Vue.directive('lazy', {
- bind(el, binding) {
- const observer = new IntersectionObserver(entries => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- el.src = binding.value;
- observer.unobserve(el);
- }
- });
- });
- observer.observe(el);
- }
- });
- // 使用示例
- <template>
- <img v-lazy="imageUrl" />
- template>
权限控制指令:
- Vue.directive('permission', {
- bind(el, binding) {
- const { value } = binding;
- const userRoles = store.state.user.roles;
- if (!userRoles.some(role => value.includes(role))) {
- el.parentNode.removeChild(el);
- }
- }
- });
- // 使用示例
- <template>
- <button v-permission="['admin', 'editor']">
- Delete
- button>
- template>
输入限制指令:
- Vue.directive('number-only', {
- bind(el) {
- el.handler = function(e) {
- e = e || window.event;
- const charCode = typeof e.charCode === 'number' ? e.charCode : e.keyCode;
- const re = /\d/;
- if (!re.test(String.fromCharCode(charCode)) && charCode > 9) {
- if (e.preventDefault) {
- e.preventDefault();
- } else {
- e.returnValue = false;
- }
- }
- };
- el.addEventListener('keypress', el.handler);
- },
- unbind(el) {
- el.removeEventListener('keypress', el.handler);
- }
- });
- // 使用示例
- <template>
- <input v-number-only v-model="value" />
- template>
元素拖拽指令:
- Vue.directive('draggable', {
- bind(el) {
- el.style.position = 'absolute';
- el.draggable = true;
- let startX, startY, initialX, initialY;
- function dragStart(e) {
- startX = e.clientX - initialX;
- startY = e.clientY - initialY;
- document.addEventListener('mousemove', drag);
- document.addEventListener('mouseup', dragEnd);
- }
- function drag(e) {
- e.preventDefault();
- const x = e.clientX - startX;
- const y = e.clientY - startY;
- el.style.left = `${x}px`;
- el.style.top = `${y}px`;
- }
- function dragEnd() {
- initialX = el.offsetLeft;
- initialY = el.offsetTop;
- document.removeEventListener('mousemove', drag);
- document.removeEventListener('mouseup', dragEnd);
- }
- el.addEventListener('mousedown', dragStart);
- }
- });
- // 使用示例
- <template>
- <div v-draggable class="draggable-element">
- Drag me!
- div>
- template>
25. 详细解释 Vue.js 的插件系统。
答案解析:
Vue.js 的插件系统允许我们为 Vue 添加全局功能,包括全局方法、指令、过滤器等。
1. 插件开发:
- // myPlugin.js
- export default {
- install(Vue, options = {}) {
- // 1. 添加全局方法或属性
- Vue.myGlobalMethod = function() {
- // 全局方法实现
- };
- // 2. 添加全局指令
- Vue.directive('my-directive', {
- bind(el, binding) {
- // 指令实现
- }
- });
- // 3. 添加实例方法
- Vue.prototype.$myMethod = function() {
- // 实例方法实现
- };
- // 4. 添加全局混入
- Vue.mixin({
- created() {
- // 混入逻辑
- }
- });
- // 5. 添加全局组件
- Vue.component('my-component', {
- // 组件选项
- });
- }
- };
- // 使用插件
- import MyPlugin from './myPlugin';
- Vue.use(MyPlugin, { /* 选项 */ });
2. 实际应用示例:
状态管理插件:
- // store.js
- class Store {
- constructor(options = {}) {
- this.state = Vue.observable(options.state || {});
- this.mutations = options.mutations || {};
- this.actions = options.actions || {};
- }
- commit(type, payload) {
- if (!this.mutations[type]) {
- throw new Error(`Unknown mutation type: ${type}`);
- }
- this.mutations[type](this.state, payload);
- }
- dispatch(type, payload) {
- if (!this.actions[type]) {
- throw new Error(`Unknown action type: ${type}`);
- }
- return this.actions[type]({
- state: this.state,
- commit: this.commit.bind(this)
- }, payload);
- }
- }
- export default {
- install(Vue) {
- Vue.prototype.$store = new Store({
- state: {
- count: 0
- },
- mutations: {
- INCREMENT(state) {
- state.count++;
- }
- },
- actions: {
- increment({ commit }) {
- commit('INCREMENT');
- }
- }
- });
- }
- };
API 请求插件:
- // api.js
- class ApiClient {
- constructor(baseURL) {
- this.baseURL = baseURL;
- }
- async request(method, endpoint, data = null) {
- const options = {
- method,
- headers: {
- 'Content-Type': 'application/json'
- }
- };
- if (data) {
- options.body = JSON.stringify(data);
- }
- const response = await fetch(`${this.baseURL}${endpoint}`, options);
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
- return response.json();
- }
- get(endpoint) {
- return this.request('GET', endpoint);
- }
- post(endpoint, data) {
- return this.request('POST', endpoint, data);
- }
- put(endpoint, data) {
- return this.request('PUT', endpoint, data);
- }
- delete(endpoint) {
- return this.request('DELETE', endpoint);
- }
- }
- export default {
- install(Vue, options = {}) {
- const api = new ApiClient(options.baseURL || '');
- Vue.prototype.$api = api;
- }
- };
国际化插件:
- // i18n.js
- class I18n {
- constructor(options = {}) {
- this.locale = options.locale || 'en';
- this.messages = options.messages || {};
- }
- setLocale(locale) {
- this.locale = locale;
- }
- t(key, params = {}) {
- const message = this.messages[this.locale]?.[key] || key;
- return message.replace(/\{(\w+)\}/g, (_, key) => params[key]);
- }
- }
- export default {
- install(Vue, options = {}) {
- const i18n = new I18n(options);
- Vue.prototype.$t = function(key, params) {
- return i18n.t(key, params);
- };
- Vue.prototype.$i18n = i18n;
- Vue.directive('t', {
- bind(el, binding) {
- el.textContent = i18n.t(binding.value);
- },
- update(el, binding) {
- el.textContent = i18n.t(binding.value);
- }
- });
- }
- };
- // 使用示例
- Vue.use(i18nPlugin, {
- locale: 'en',
- messages: {
- en: {
- hello: 'Hello {name}!',
- welcome: 'Welcome to our app'
- },
- zh: {
- hello: '你好 {name}!',
- welcome: '欢迎使用我们的应用'
- }
- }
- });
表单验证插件:
- // validator.js
- class Validator {
- constructor() {
- this.rules = {
- required: value => !!value || '此字段是必填的',
- email: value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) || '请输入有效的邮箱地址',
- minLength: min => value => value.length >= min || `最少需要 ${min} 个字符`,
- maxLength: max => value => value.length <= max || `最多允许 ${max} 个字符`
- };
- }
- validate(value, rules) {
- for (const rule of rules) {
- if (typeof rule === 'string') {
- const result = this.rules[rule](value);
- if (result !== true) return result;
- } else if (typeof rule === 'function') {
- const result = rule(value);
- if (result !== true) return result;
- }
- }
- return true;
- }
- addRule(name, validator) {
- this.rules[name] = validator;
- }
- }
- export default {
- install(Vue) {
- const validator = new Validator();
- Vue.prototype.$validator = validator;
- Vue.directive('validate', {
- bind(el, binding, vnode) {
- const rules = binding.value;
- const inputElement = el.tagName === 'INPUT' ? el : el.querySelector('input');
- inputElement.addEventListener('input', () => {
- const result = validator.validate(inputElement.value, rules);
- if (result !== true) {
- vnode.context.$emit('validation-error', result);
- } else {
- vnode.context.$emit('validation-success');
- }
- });
- }
- });
- }
- };
- // 使用示例
- <template>
- <div>
- <input
- v-validate="['required', 'email']"
- v-model="email"
- @validation-error="handleError"
- />
- <span class="error" v-if="error">{{ error }}span>
- div>
- template>
- <script>
- export default {
- data() {
- return {
- email: '',
- error: null
- };
- },
- methods: {
- handleError(error) {
- this.error = error;
- }
- }
- };
- script>
26. 详细解释 Vue.js 的虚拟 DOM 和渲染机制。
答案解析:
Vue.js 使用虚拟 DOM(Virtual DOM)来提高渲染性能,它是对真实 DOM 的一种抽象表示。
1. 虚拟 DOM 的实现:
- // VNode 类
- class VNode {
- constructor(tag, data, children, text) {
- this.tag = tag; // 标签名
- this.data = data; // 属性、事件等数据
- this.children = children; // 子节点
- this.text = text; // 文本内容
- }
- }
- // 创建虚拟节点
- function createElement(tag, data = {}, ...children) {
- if (typeof tag === 'string') {
- return new VNode(tag, data, children);
- } else {
- // 组件 VNode
- return new VNode(
- 'component',
- { ...data, component: tag },
- children
- );
- }
- }
- // 渲染函数示例
- function render() {
- return createElement('div', { class: 'container' },
- createElement('h1', { style: { color: 'red' } }, 'Hello'),
- createElement('p', null, 'This is a paragraph')
- );
- }
2. Diff 算法:
- function patch(oldVNode, newVNode) {
- // 1. 如果新节点不存在
- if (!newVNode) {
- return oldVNode.el.parentNode.removeChild(oldVNode.el);
- }
- // 2. 如果是相同节点
- if (sameVNode(oldVNode, newVNode)) {
- patchVNode(oldVNode, newVNode);
- } else {
- // 3. 不同节点,直接替换
- const oldEl = oldVNode.el;
- const parentEl = oldEl.parentNode;
- // 创建新元素
- createElm(newVNode);
- if (parentEl) {
- parentEl.replaceChild(newVNode.el, oldEl);
- }
- }
- }
- function sameVNode(oldVNode, newVNode) {
- return oldVNode.key === newVNode.key && oldVNode.tag === newVNode.tag;
- }
- function patchVNode(oldVNode, newVNode) {
- const el = newVNode.el = oldVNode.el;
- const oldCh = oldVNode.children;
- const newCh = newVNode.children;
- // 1. 文本节点
- if (typeof newVNode.text !== 'undefined') {
- if (newVNode.text !== oldVNode.text) {
- el.textContent = newVNode.text;
- }
- return;
- }
- // 2. 更新属性
- updateProperties(newVNode, oldVNode.data);
- // 3. 更新子节点
- if (oldCh && newCh) {
- updateChildren(el, oldCh, newCh);
- } else if (newCh) {
- // 只有新子节点
- for (let i = 0; i < newCh.length; i++) {
- el.appendChild(createElm(newCh[i]));
- }
- } else if (oldCh) {
- // 只有旧子节点
- el.innerHTML = '';
- }
- }
- function updateChildren(parentEl, oldCh, newCh) {
- let oldStartIdx = 0;
- let oldEndIdx = oldCh.length - 1;
- let newStartIdx = 0;
- let newEndIdx = newCh.length - 1;
- while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
- if (sameVNode(oldCh[oldStartIdx], newCh[newStartIdx])) {
- // 从头比较
- patchVNode(oldCh[oldStartIdx], newCh[newStartIdx]);
- oldStartIdx++;
- newStartIdx++;
- } else if (sameVNode(oldCh[oldEndIdx], newCh[newEndIdx])) {
- // 从尾比较
- patchVNode(oldCh[oldEndIdx], newCh[newEndIdx]);
- oldEndIdx--;
- newEndIdx--;
- } else {
- // 需要移动节点
- // ... 其他 diff 策略
- }
- }
- // 处理剩余节点
- if (oldStartIdx > oldEndIdx) {
- // 添加新节点
- for (let i = newStartIdx; i <= newEndIdx; i++) {
- parentEl.appendChild(createElm(newCh[i]));
- }
- } else if (newStartIdx > newEndIdx) {
- // 删除多余节点
- for (let i = oldStartIdx; i <= oldEndIdx; i++) {
- parentEl.removeChild(oldCh[i].el);
- }
- }
- }
3. 模板编译:
- // 模板编译过程
- function compile(template) {
- // 1. 解析模板
- const ast = parse(template);
- // 2. 优化静态节点
- optimize(ast);
- // 3. 生成代码
- const code = generate(ast);
- // 4. 创建渲染函数
- return new Function(`with(this){return ${code}}`);
- }
- // 解析模板
- function parse(template) {
- const stack = [];
- let currentParent;
- let root;
- parseHTML(template, {
- start(tag, attrs, unary) {
- const element = {
- type: 1,
- tag,
- attrsList: attrs,
- parent: currentParent,
- children: []
- };
- if (!root) {
- root = element;
- }
- if (currentParent) {
- currentParent.children.push(element);
- }
- if (!unary) {
- currentParent = element;
- stack.push(element);
- }
- },
- end() {
- stack.pop();
- currentParent = stack[stack.length - 1];
- },
- chars(text) {
- if (!currentParent) return;
- const children = currentParent.children;
- if (text.trim()) {
- children.push({
- type: 3,
- text
- });
- }
- }
- });
- return root;
- }
- // 优化静态节点
- function optimize(root) {
- // 1. 标记静态节点
- markStatic(root);
- // 2. 标记静态根节点
- markStaticRoots(root);
- }
- function markStatic(node) {
- node.static = isStatic(node);
- if (node.type === 1) {
- for (let i = 0; i < node.children.length; i++) {
- const child = node.children[i];
- markStatic(child);
- if (!child.static) {
- node.static = false;
- }
- }
- }
- }
- function isStatic(node) {
- if (node.type === 2) { // 表达式
- return false;
- }
- if (node.type === 3) { // 文本
- return true;
- }
- return !node.if && !node.for; // 元素
- }
- // 生成代码
- function generate(ast) {
- const code = genElement(ast);
- return `{render:function(){${code}}}`;
- }
- function genElement(el) {
- if (el.static) {
- return `_m(${el.staticIndex})`;
- }
- let code;
- const data = genData(el);
- const children = genChildren(el);
- code = `_c('${el.tag}'${
- data ? `,${data}` : ''
- }${
- children ? `,${children}` : ''
- })`;
- return code;
- }
4. 实际应用示例:
渲染函数组件:
- // 使用渲染函数创建组件
- Vue.component('anchored-heading', {
- render: function (createElement) {
- return createElement(
- 'h' + this.level, // 标签名
- this.$slots.default // 子节点数组
- );
- },
- props: {
- level: {
- type: Number,
- required: true
- }
- }
- });
- // 使用示例
- <anchored-heading :level="1">
- Hello world!
- anchored-heading>
函数式组件:
- // 函数式组件
- Vue.component('my-component', {
- functional: true,
- props: {
- // 声明 props
- message: {
- type: String,
- required: true
- }
- },
- render: function (createElement, context) {
- // context 包含:props、children、slots、data、parent 等
- return createElement('div', context.props.message);
- }
- });
JSX 支持:
- // 使用 JSX
- export default {
- data() {
- return {
- items: ['A', 'B', 'C']
- };
- },
- render() {
- return (
- <div class="container">
- {this.items.map(item => (
- <div key={item} class="item">
- {item}
- div>
- ))}
- div>
- );
- }
- };
高阶组件:
- // 高阶组件工厂函数
- function withLog(WrappedComponent) {
- return {
- mounted() {
- console.log(`${WrappedComponent.name} mounted`);
- },
- props: WrappedComponent.props,
- render(h) {
- return h(WrappedComponent, {
- props: this.$props,
- on: this.$listeners
- });
- }
- };
- }
- // 使用高阶组件
- const MyComponent = {
- name: 'MyComponent',
- props: ['message'],
- template: '{{ message }}'
- };
- const LoggedComponent = withLog(MyComponent);
动态组件:
- // 动态组件工厂
- const AsyncComponent = () => ({
- // 需要加载的组件
- component: import('./MyComponent.vue'),
- // 加载中应当渲染的组件
- loading: LoadingComponent,
- // 出错时渲染的组件
- error: ErrorComponent,
- // 展示加载时组件的延时时间。默认值是 200 (毫秒)
- delay: 200,
- // 如果提供了超时时间且组件加载也超时了,
- // 则使用加载失败时的组件。默认值是:`Infinity`
- timeout: 3000
- });
- export default {
- components: {
- AsyncComponent
- },
- template: `
- <div>
- <async-component v-if="show" />
- <button @click="show = !show">Togglebutton>
- div>
- `,
- data() {
- return {
- show: false
- };
- }
- };
