最经典的前端与全栈开发面试题

一、试题

1、为什么需要HTML5?
2、有哪些块标签?有哪些行内标签?有什么区别?
3、为什么需要前端预处理技术?常见的前端预处理技术有哪些?
4、常见的浏览器内核有哪些?什么内核最常用?
5、CSS3中的Rem与em有什么区别?
6、什么是防抖与限流?
7、Ollama是什么?LLM是什么?
8、你平时在编程中如何使用人工智能提高效率?
9、开发中你遇到错误是如何解决的?
10、什么是nodejs?有什么用?有何特点?
11、async,await,cjs,mjs
12、Jest有什么作用?
13、pnpm与npm有什么共同点与不同点?
14、什么是内网穿透,为什么需要内网穿透?
15、使用uniapp开发微信小程与腾讯原生框架开发小程序有什么区别?
16、ES中的模块规范主要有哪些?
17、AXIOS与AJAX间的关系是什么?
18、前后端分离的架构有什么优缺点?
19、微服务架构有什么特点?
20、什么是TypeScript?为什么需要TypeScript?Ts有什么特点?JavaScript的糟粕有哪些?
21、JavaScript有哪些数据类型?
22、TypeScript中any与unknown的区别是什么?
23、什么是前端工程化?有什么作用?你使用什么工具进行工程化?
24、什么是前端模块化?有什么作用?
25、如何应对npm网络不通畅的问题?
26、Vue3中ref与reactive有什么区别?
27、vuex与pinia有什么区别?
28、如何看看待AI对程序员的冲击?
29、watch与watchEffect有什么区别?
30、请解释 ES6 中的箭头函数和普通函数的区别?

二、答案

  1. 为什么需要 HTML5?
    • 增强的语义化:HTML5 提供了许多新的语义化标签,如<header><nav><article><section><footer>等。这些标签使得网页的结构更加清晰,搜索引擎能够更好地理解网页内容,从而提高网站的搜索引擎优化(SEO)效果。例如,一个新闻网站可以使用<article>标签包裹每一篇新闻内容,方便搜索引擎识别新闻主体部分。
    • 支持多媒体:HTML5 原生支持音频和视频播放,通过<audio><video>标签,无需像以前那样依赖第三方插件(如 Flash)。这不仅提高了网页的性能和安全性,还使得在网页中嵌入媒体内容变得更加容易。比如在一个在线教育网站,可以直接使用<video>标签嵌入教学视频。
    • 更好的交互性:新增了一些表单输入类型,如emailteldate等,这些类型可以在移动设备上提供更合适的键盘输入方式,增强用户体验。并且支持本地存储(localStoragesessionStorage),允许网页在用户浏览器中存储数据,方便离线应用的开发,像一些简单的待办事项应用可以将数据存储在本地。
    • 跨平台开发便利性:为跨平台移动应用开发提供了基础,结合一些框架(如 Cordova、Ionic 等)可以使用 HTML5 开发出能在不同移动操作系统(如 iOS 和 Android)上运行的应用,减少了开发成本。
  2. 有哪些块标签?有哪些行内标签?有什么区别?
    • 块标签:
      • 常见的块标签有<div><p><h1>-<h6><ul><ol><li><header><footer><section><article>等。
      • 块标签在页面中会独占一行,它会自动从新的一行开始显示,并且宽度默认是父容器的 100%(除非设置了宽度)。例如<div>标签,当在一个网页中放置多个<div>时,每个<div>都会占据一行,上下排列。
    • 行内标签:
      • 常见的行内标签有<span><a><img><input><label><strong><em>等。
      • 行内标签不会独占一行,它会在一行内按照从左到右的顺序依次排列,并且其宽度和高度只由内容撑开。例如多个<span>标签可以在同一行内显示不同的文本样式部分。
    • 区别:
      • 布局方式上,块标签主要用于构建网页的大框架和布局模块,而行内标签用于在块元素内部或者文本中修饰部分内容。
      • 盒模型属性方面,块标签可以设置宽度、高度、内外边距等属性来控制布局;行内标签设置宽度和高度通常无效(除了img等替换元素),并且上下外边距和内边距在垂直方向上的表现比较特殊,它不会像块元素那样撑开布局。
  3. 为什么需要前端预处理技术?常见的前端预处理技术有哪些?
    • 原因:
      • 提高代码可维护性:随着前端项目的复杂度增加,CSS 和 JavaScript 代码会变得越来越庞大和混乱。预处理技术允许开发者使用变量、函数、嵌套规则等高级特性,使代码结构更加清晰。例如,在 CSS 预处理中,可以定义颜色变量,这样如果要修改主题颜色,只需要修改变量的值,而不用在每个样式规则中去修改颜色代码。
      • 增强代码复用性:通过函数、混合(mixin)等方式,可以把一些通用的样式或功能提取出来,在多个地方复用。比如在 Sass 中,可以定义一个用于生成按钮样式的混合,然后在不同的按钮样式定义中调用这个混合。
      • 提前处理兼容性问题:可以处理一些浏览器兼容性问题。例如,自动添加浏览器厂商前缀,像在 CSS3 中,不同浏览器对于一些新特性(如渐变、动画等)需要不同的前缀(如-webkit--moz-等),预处理工具可以自动添加这些前缀。
    • 常见技术:
      • CSS 预处理:
        • Sass/SCSS:Sass(Syntactically Awesome Style Sheets)有两种语法,缩进语法和 SCSS(Sassy CSS)语法。SCSS 是 CSS3 语法的超集,它支持变量($variable)、嵌套规则、混合(@mixin@include)、继承(@extend)等功能。例如:

 

收起
scss
 
$primary-color: #333;
.button {
    background-color: $primary-color;
    &:hover {
        background-color: darken($primary-color, 10%);
    }
}

 

  • Less:Less 也是一种 CSS 预处理语言,它和 SCSS 类似,支持变量、混合、嵌套等功能。例如:

 

收起
less
 
@primary-color: #333;
.button {
    background-color: @primary-color;
    &:hover {
        background-color: darken(@primary-color, 10%);
    }
}

 

  • JavaScript 预处理:
    • Babel:Babel 主要用于将 ES6 + 代码转换为向后兼容的 JavaScript 代码。例如,将 ES6 的箭头函数转换为传统的函数表达式,以兼容旧版本的浏览器。在代码中使用了 ES6 的constlet以及箭头函数等新特性,通过 Babel 可以转换为 ES5 代码,让代码在旧浏览器中也能运行。

 

  1. 常见的浏览器内核有哪些?什么内核最常用?
    • 常见浏览器内核:
      • Trident 内核:是微软开发的浏览器内核,主要用于 Internet Explorer 浏览器。它在早期的 Windows 操作系统中占据主导地位,但是由于其对 Web 标准的支持不够完善,在现代网页开发中逐渐被淘汰。不过,一些基于 IE 的企业级应用仍然在使用这个内核。
      • Gecko 内核:由 Mozilla 基金会开发,主要用于 Firefox 浏览器。Gecko 内核对 Web 标准的支持较好,具有很高的性能和安全性。它支持许多 HTML5 和 CSS3 的新特性,并且有自己的一套 JavaScript 引擎(SpiderMonkey)。
      • WebKit 内核:最初是苹果公司为 Safari 浏览器开发的内核。它是一个开源的浏览器内核,具有快速的渲染速度和良好的兼容性。许多移动浏览器也基于 WebKit 内核或者其衍生版本,如 iOS 的 Safari 和一些早期的安卓浏览器。
      • Blink 内核:是由谷歌和 Opera 软件公司等共同开发的浏览器内核,它是从 WebKit 内核分支出来的。主要用于谷歌的 Chrome 浏览器和 Opera 浏览器。Blink 内核在性能、稳定性和对新 Web 标准的支持方面表现出色。
    • 最常用内核:目前 Blink 内核是最常用的浏览器内核。这主要是因为 Chrome 浏览器在全球浏览器市场份额中占据很高的比例,并且许多其他浏览器也采用了 Blink 内核或者与之兼容的技术。Blink 内核对于新的 Web 技术(如 HTML5、CSS3、WebAssembly 等)的快速支持和不断优化,使得它在开发者和用户中都很受欢迎。
  2. CSS3 中的 Rem 与 em 有什么区别?
    • 定义基础不同:
      • em是相对长度单位,它是相对于元素自身的字体大小来计算的。如果一个元素的字体大小没有设置,它会继承父元素的字体大小作为参考。例如,如果一个<p>元素的字体大小是16px,在这个<p>元素内部的一个<span>元素设置font - size: 1.5em,那么<span>元素的字体大小就是1.5 * 16px = 24px
      • rem是相对于根元素(<html>元素)的字体大小来计算的。例如,如果<html>元素的字体大小设置为10px,那么在文档中的任何元素设置font - size: 1.5rem,其字体大小就是1.5 * 10px = 15px
    • 应用场景区别:
      • em在进行局部字体大小调整或者基于某个元素自身字体大小进行相对布局时比较有用。但是如果在嵌套元素中多次使用em,字体大小的计算会变得复杂,因为它是层层嵌套相对计算的。
      • rem在整个页面布局中实现统一的比例缩放比较方便。例如,要根据用户的屏幕分辨率或者设备类型调整整个页面的字体大小,只需要修改根元素(<html>元素)的字体大小,所有使用rem单位的元素字体大小都会相应改变。
  3. 什么是防抖与限流?
    • 防抖(Debounce):
      • 概念:防抖是一种在事件被频繁触发时,限制函数执行频率的策略。当一个事件(如窗口大小调整、输入框输入等)被触发时,防抖函数会设置一个延迟时间。如果在这个延迟时间内,事件再次被触发,那么之前设置的延迟就会被清除,重新开始计时。只有当事件在延迟时间内没有再次被触发,函数才会执行。
      • 应用场景:例如,在一个搜索框的输入事件中,当用户在输入框中输入内容时,可能会频繁地触发搜索请求。使用防抖可以在用户停止输入一段时间(比如 500 毫秒)后才发送搜索请求,避免频繁发送请求,减轻服务器压力并且提高用户体验。
    • 限流(Throttle):
      • 概念:限流是指在一定时间内,限制函数执行的次数。无论事件被触发多少次,函数在规定的时间间隔内只会执行一次。例如,设置一个限流时间间隔为 1 秒,那么在这 1 秒内,无论事件触发多少次,函数都只会执行一次。
      • 应用场景:比如在页面滚动事件中,当用户快速滚动页面时,会频繁触发滚动事件。如果在滚动事件中有一些比较复杂的计算或者操作(如加载更多内容),使用限流可以确保这些操作不会过于频繁地执行,避免性能问题。
  4. Ollama 是什么?LLM 是什么?
    • Ollama:
      • Ollama 是一个用于运行大型语言模型(LLM)的工具。它允许用户在本地或者服务器上轻松地部署和运行各种语言模型,并且可以通过简单的 API 进行交互。这样可以更好地控制数据隐私,避免将数据发送到外部服务器进行处理。例如,开发者可以使用 Ollama 在自己的内部网络中部署一个语言模型,用于处理一些敏感的文档或者内部知识问答。
    • LLM(Large Language Model):
      • LLM 是指大型语言模型,它是一种基于深度学习中的 Transformer 架构的人工智能模型。这些模型通过在大量的文本数据上进行训练,能够生成自然流畅的语言文本。像 OpenAI 的 GPT 系列、谷歌的 Bard 等都是 LLM。它们可以用于文本生成、机器翻译、问答系统、情感分析等多种自然语言处理任务。例如,在内容创作领域,LLM 可以帮助作者生成文章大纲或者部分内容。
  5. 你平时在编程中如何使用人工智能提高效率?
    • 代码生成辅助:利用像 Copilot 这样的代码生成工具,在编写代码时,它可以根据上下文和已有的代码结构,自动生成代码片段。例如,在编写一个复杂的函数来处理数据验证时,Copilot 可以根据函数名和参数提示,生成部分验证逻辑代码,如检查数据类型、格式等部分,然后我可以在此基础上进行修改和完善。
    • 代码解释和文档编写:当遇到不熟悉的代码库或者复杂的代码片段时,使用人工智能工具来解释代码的功能和逻辑。并且在编写代码文档时,这些工具可以帮助总结代码的功能、参数说明等内容,提高文档编写效率。比如对于一个有很多复杂算法的 JavaScript 库,让人工智能工具帮忙梳理每个函数的主要功能和输入输出参数的含义,然后生成初步的文档内容。
    • 错误排查和优化建议:在代码出现错误或者性能问题时,人工智能工具可以根据错误信息和代码结构提供可能的原因和优化建议。例如,当遇到 JavaScript 中的内存泄漏问题时,工具可以分析代码中可能导致内存泄漏的部分,如未正确释放的定时器、闭包等情况,并提供解决方案。
  6. 开发中你遇到错误是如何解决的?
    • 查看错误信息:首先仔细查看控制台(浏览器控制台或者终端控制台)中的错误信息。错误信息通常会包含错误类型(如语法错误、引用错误、类型错误等)、出错的文件和行号。例如,如果是 JavaScript 语法错误,控制台会指出在哪个文件的哪一行出现了语法问题,像Uncaught SyntaxError: Unexpected token这样的信息,就可以根据行号去定位代码问题。
    • 调试工具使用:利用浏览器的开发者工具(如 Chrome 的 DevTools)进行调试。可以在可疑的代码行设置断点,然后逐步执行代码,观察变量的值、函数的调用过程等。对于复杂的 JavaScript 应用或者 CSS 布局问题,通过调试工具查看元素的样式计算、事件触发顺序等情况来找出问题所在。
    • 代码审查和逻辑分析:如果错误信息不明确或者调试没有发现明显问题,就需要对代码进行全面审查。检查代码的逻辑是否正确,是否有遗漏的情况或者不合理的假设。例如,在一个条件判断语句中,可能忽略了某些边界条件,导致程序出现错误行为。
    • 参考文档和社区资源:查阅相关的技术文档、官方文档或者在技术社区(如 Stack Overflow)中搜索类似的问题。很多时候,其他人已经遇到并解决了相同的问题,通过参考他们的解决方案可以快速解决自己遇到的问题。
  7. 什么是 nodejs? 有什么用?有何特点?
    • 定义:Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它允许开发者在服务器端使用 JavaScript 进行编程。
    • 用途:
      • 构建 Web 服务器:可以使用 Node.js 创建高性能的 Web 服务器,像 Express.js、Koa.js 等流行的 Web 框架都是基于 Node.js 构建的。例如,使用 Express.js 可以快速搭建一个能够处理 HTTP 请求和响应的服务器,用于提供网页服务或者 API 接口。
      • 命令行工具开发:方便开发各种命令行工具。例如,可以编写一个用于文件处理、代码格式化或者项目构建的命令行工具。Node.js 提供了process.argv等方式来获取命令行参数,并且可以通过fs模块进行文件系统操作。
      • 实时应用开发:适合开发实时通信应用,如聊天应用、实时数据推送应用等。通过使用 WebSockets 等技术,Node.js 可以在服务器和客户端之间建立实时连接,及时推送数据。
    • 特点:
      • 单线程、事件驱动:Node.js 采用单线程模型,通过事件循环(Event Loop)来处理异步操作。这使得它能够高效地处理大量并发请求,避免了线程创建和销毁的开销。当有请求进来时,Node.js 会将请求放入事件队列,然后通过事件循环不断地从队列中取出事件进行处理。
      • 非阻塞 I/O:在进行 I/O 操作(如文件读取、网络请求等)时,Node.js 不会阻塞线程等待操作完成,而是会继续处理其他事件,当 I/O 操作完成后,通过回调函数或者 Promise 来获取结果。这大大提高了应用程序的性能和响应速度。
      • 跨平台:Node.js 可以在不同的操作系统(如 Windows、Linux、Mac OS)上运行,方便开发者在不同的开发环境和服务器环境中进行部署。
  8. async,await,cjs,mjs
    • async 和 await:
      • 概念:asyncawait是 JavaScript 中的异步编程语法糖,用于简化 Promise 的使用。async用于定义一个异步函数,这个函数总是返回一个 Promise。await只能在async函数内部使用,它会暂停async函数的执行,直到 Promise 被解决(resolved)或者被拒绝(rejected)。
      • 示例:

 

收起
javascript
 
async function getData() {
    const response = await fetch('https://example.com/api/data');
    const data = await response.json();
    return data;
}

 

在这个例子中,getData函数是一个async函数,在函数内部,await会等待fetch操作(返回一个 Promise)完成,获取响应后,又使用await等待将响应解析为 JSON 格式的数据完成。

 

  • cjs(CommonJS)和 mjs(ECMAScript Modules):
    • 概念:
      • CommonJS(cjs):是一种模块规范,主要用于 Node.js 环境。在 CommonJS 中,通过require函数来导入模块,通过module.exports来导出模块。例如,在一个 Node.js 文件中定义一个模块:

 

收起
javascript
 
// math.js
function add(a, b) {
    return a + b;
}
module.exports = {
    add
};

 

在另一个文件中可以这样导入并使用:

 

收起
javascript
 
const math = require('./math.js');
console.log(math.add(1, 2));

 

  • ECMAScript Modules(mjs):是 JavaScript 原生的模块规范,它在浏览器和现代的 JavaScript 环境中得到越来越多的支持。在 ES Modules 中,使用import语句导入模块,使用export语句导出模块。例如:

 

收起
javascript
 
// math.mjs
export function add(a, b) {
    return a + b;
}

 

在另一个文件中可以这样导入:

 

收起
javascript
 
import { add } from './math.mjs';
console.log(add(1, 2));

  1. Jest 有什么作用?
    • 单元测试框架:Jest 是一个流行的 JavaScript 单元测试框架。它用于对 JavaScript 代码(包括但不限于函数、类等)进行单元测试,以确保代码的正确性。例如,在开发一个工具函数库时,使用 Jest 可以编写测试用例来验证每个函数在给定输入下是否返回预期的输出。
    • 断言功能:提供了丰富的断言函数来验证代码的行为。例如,expect()函数可以用来检查变量的值是否符合预期。像expect(sum(1, 2)).toBe(3),这里sum是一个自定义的加法函数,toBe是一个断言,用于检查sum(1, 2)的结果是否等于 3。
    • 模拟(Mock)功能:能够模拟函数、模块等。在测试过程中,当代码依赖于外部资源(如网络请求、数据库操作等)时,可以使用 Jest 的模拟功能来模拟这些依赖项,使得测试不依赖于外部环境,并且可以控制返回值来测试不同的情况。例如,当测试一个发送 HTTP 请求的函数时,可以模拟fetch函数的返回值,而不用真正地发送请求。
    • 测试覆盖率报告:可以生成测试覆盖率报告,显示代码中有多少部分被测试用例覆盖到了。这有助于开发者了解测试的完整性,找到未被测试的代码路径,提高代码质量。例如,通过覆盖率报告可以发现一些分支语句或者边界条件没有被测试到,从而完善测试用例。
  2. pnpm 与 npm 有什么共同点与不同点?
    • 共同点:
      • 包管理功能:pnpm 和 npm 都是用于管理 JavaScript 项目中的依赖包。它们都可以用于安装、更新、删除项目所需的各种库和工具。例如,在一个 Vue.js 项目中,都可以用来安装vuevue-router等依赖包。
      • 依赖版本控制:两者都支持指定依赖包的版本范围。可以通过在package.json文件中使用语义化版本号(如^1.0.0~1.0.0等)来控制依赖包的版本更新策略,以确保项目的稳定性和兼容性。
    • 不同点:
      • 安装方式和存储结构:
        • npm:在安装依赖包时,会将每个依赖包的文件复制到项目的node_modules目录下。这样会导致相同版本的依赖包在不同项目或者不同层级的依赖中被多次复制,占用较多磁盘空间。例如,如果多个项目都使用了lodash库的相同版本,每个项目的node_modules中都会有一份lodash的文件副本。
        • pnpm:采用了一种更节省空间的存储方式。它会将所有的依赖包存储在一个全局的存储位置(内容可寻址存储),在项目的node_modules目录下,只会创建指向全局存储中实际依赖包的硬链接。这样相同版本的依赖包在磁盘上只有一份实际的存储,大大节省了磁盘空间。
      • 安装速度:
        • npm:在安装大量依赖包时,由于需要逐个复制文件,可能会比较慢,特别是当依赖树比较复杂时。例如,在一个大型的 React 项目中,首次安装所有依赖可能需要较长时间。
        • pnpm:由于其采用的存储方式,在安装依赖包时,如果全局存储中已经有了所需的版本,只需要创建硬链接,速度相对较快。并且在处理依赖树时,pnpm 也有一些优化策略,使得安装过程更加高效。
  3. 什么是内网穿透,为什么需要内网穿透?
    • 定义:内网穿透是一种将位于局域网内的设备(如本地开发服务器)暴露给公网访问的技术。简单来说,就是让外部网络(如互联网)能够访问到在内部网络(如家庭网络、公司内部网络)中的服务器或服务。
    • 需求原因:
      • 开发测试需求:在本地开发环境中,开发人员可能需要将自己本地运行的 Web 应用(如使用 Node.js 开发的 Web 服务器)展示给外部人员(如远程的同事或者客户)进行测试或者演示。但是本地开发服务器通常在局域网内,无法直接被外部访问。通过内网穿透,可以将本地服务器映射到一个公网地址,方便外部人员通过公网访问。
      • 远程设备访问:对于一些部署在家庭网络或者企业内部网络中的设备(如智能家居设备、内部服务器等),当用户在外部网络环境下想要访问这些设备时,就需要内网穿透技术。例如,用户在外面想要访问家里的监控摄像头或者控制智能家居设备,通过内网穿透可以实现远程访问。
  4. 使用 uniapp 开发微信小程与腾讯原生框架开发小程序有什么区别?
    • 开发效率:
      • uniapp:是一个跨平台的开发框架,它可以用于开发微信小程序、支付宝小程序、H5、App 等多种平台的应用。使用 uniapp 开发微信小程序,代码复用性高。开发者可以使用一套代码(如 Vue.js 语法)来适配多个平台,减少了开发时间和工作量。例如,在开发一个电商小程序时,不需要为微信小程序单独编写一套完全不同的代码,而是在一定程度上可以共用代码。
      • 腾讯原生框架:专门针对微信小程序进行开发,在开发微信小程序时可以更好地利用微信提供的原生 API 和组件,提供了最贴合微信小程序生态的开发体验。但如果要开发其他平台的应用,需要重新编写代码,开发效率相对较低。
    • 性能表现:
      • uniapp:在性能方面,由于 uniapp 需要将代码转换为微信小程序能够识别的格式,并且要考虑跨平台兼容性,可能会有一定的性能损耗。不过,uniapp 也在不断优化性能,并且对于大多数非极致性能要求的应用来说,其性能可以满足需求。
      • 腾讯原生框架:由于是原生开发,能够更好地利用微信小程序的底层优化和性能特性,在性能上可能会有一定的优势,特别是对于一些对性能要求较高的应用,如游戏小程序等。
    • 生态和支持:
      • uniapp:有自己的一套生态系统,包括社区、插件等。开发者可以在社区中分享和获取跨平台开发的经验和资源,并且可以使用 uniapp 提供的插件来快速实现一些功能。但相对来说,与微信小程序原生生态的融合度没有腾讯原生框架高。
      • 腾讯原生框架:可以充分利用微信小程序官方提供的各种组件、API、开发工具和文档,并且在遇到问题时可以更容易地从微信官方渠道获取支持。
  5. ES 中的模块规范主要有哪些?
    • CommonJS(CJS):
      • 主要用于服务器端的 JavaScript 环境(如 Node.js)。它通过require函数来导入模块,通过module.exports来导出模块。这种模块规范的特点是模块加载是同步的。例如,在 Node.js 中:
 
收起
javascript
 
// 导出模块
const myFunction = () => {
    console.log("This is my function");
};
module.exports = myFunction;
// 导入模块
const importedFunction = require('./myModule');
importedFunction();
 
  • ECMAScript Modules(ESM):
    • 这是 JavaScript 原生的模块规范,在浏览器和现代 JavaScript 环境中得到广泛支持。使用import语句导入模块,使用export语句导出模块,并且模块加载可以是异步的。例如:


收起
javascript
 
// 导出模块
export const myFunction = () => {
    console.log("This is my function");
};
// 导入模块
import { myFunction } from './myModule.mjs';
myFunction();
 
  • AMD(Asynchronous Module Definition):
    • 主要用于浏览器环境,强调模块的异步加载。它通过define函数来定义模块,通过require函数来加载模块。例如:


收起
javascript
 
// 定义模块
define(['dependencyModule'], function(dependency) {
    const myFunction = () => {
        console.log("This is my function using dependency: ", dependency);
    };
    return myFunction;
});
// 加载模块
require(['myModule'], function(myModule) {
    myModule();
});
 
  • UMD(Universal Module Definition):
    • 是一种兼容多种模块规范的通用模块定义方式。它可以在不同的环境(如浏览器、Node.js)和不同的模块加载系统(如 CommonJS、AMD)中使用。UMD 模块通常会在代码开头判断当前环境,然后采用相应的模块加载方式。例如:


收起
javascript
 
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD环境
        define(['dependencyModule'], factory);
    } else if (typeof module === 'object' && module.exports) {
        // CommonJS环境
        module.exports = factory(require('dependencyModule'));
    } else {
        // 浏览器全局变量环境
        root.myModule = factory(root.dependencyModule);
    }
}(this, function (dependency) {
    const myFunction = () => {
        console.log("This is my function using dependency: ", dependency);
    };
    return myFunction;
}));
 
  1. AXIOS 与 AJAX 间的关系是什么?
    • AJAX(Asynchronous JavaScript and XML):
      • 是一种用于创建快速动态网页的技术,它允许在不重新加载整个页面的情况下,与服务器进行数据交互。其核心是通过 JavaScript 的XMLHttpRequest对象来发送异步请求和接收响应。例如,传统的 AJAX 请求可以这样实现:
 
收起
javascript
 
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://example.com/api/data', true);
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
        const data = JSON.parse(xhr.responseText);
        console.log(data);
    }
};
xhr.send();
 
  • AXIOS:
    • 是一个基于 Promise 的 HTTP 客户端库,它在底层还是使用了XMLHttpRequest(在浏览器环境)或者http模块(在 Node.js 环境)来进行数据请求,但提供了更简洁、易用的 API。它简化了 AJAX 请求的过程,并且对请求和响应进行了更好的封装。例如,使用 AXIOS 发送一个 GET 请求可以这样写:


收起
javascript
 
import axios from 'axios';
axios.get('https://example.com/api/data')
   .then(response => {
        console.log(response.data);
    })
   .catch(error => {
        console.error(error);
    });
 
  • 关系:AXIOS 是对 AJAX 技术的一种封装,它使用了 AJAX 的核心原理(通过XMLHttpRequest或其他底层方式与服务器通信),但是提供了更高级、更方便的接口,使得开发者能够更轻松地进行 HTTP 请求操作,并且更好地处理异步操作(基于 Promise)。


    1. 前后端分离的架构有什么优缺点?
      • 优点:
        • 职责分工明确:前端团队专注于用户界面的设计、交互逻辑和视觉效果,使用 HTML、CSS、JavaScript 等技术。后端团队专注于数据存储、业务逻辑处理和服务器性能优化,通过编程语言如 Python(Flask、Django)、Java(Spring Boot)等实现。例如,在一个电商网站中,前端开发人员可以专注于商品展示页面的布局和用户操作体验,后端开发人员则负责处理订单处理、库存管理等业务逻辑。
        • 开发效率提高:前后端可以并行开发,前端和后端开发人员可以根据事先定义好的接口文档独立进行开发。一旦接口确定,前端和后端就可以同时开展工作,不需要等待对方完成某个部分才能继续。比如,前端开发可以先使用模拟数据进行页面开发,后端开发人员同时进行接口开发,之后再进行联调。
        • 技术选型灵活:前端和后端可以根据自身需求选择最适合的技术栈。前端可以选择流行的框架如 Vue.js、React.js 等,后端可以选择适合业务场景的语言和框架。例如,前端为了实现更好的用户体验选择了 React.js 构建单页面应用,后端为了高效的数据处理选择了 Node.js。
        • 易于维护和扩展:当需要对系统进行维护或者功能扩展时,由于前后端的分离,只需要对相关的部分进行修改即可。如果要添加一个新的前端功能,如添加一个新的用户界面组件,通常只需要在前端代码中操作,而不会影响后端。同样,后端的业务逻辑修改也不会直接影响前端,只要接口保持兼容。
      • 缺点:
        • 开发成本增加初期:需要额外的时间和精力来设计和定义前后端之间的接口,包括接口的参数、返回值、数据格式等。并且在开发过程中,前后端团队需要频繁沟通来确保接口的准确性和一致性,这在一定程度上增加了开发成本。
        • 部署复杂:前后端分离后,需要分别部署前端应用和后端应用。前端应用通常需要部署到 Web 服务器或者 CDN(内容分发网络)上,后端应用需要部署到应用服务器上。这增加了部署的复杂性,并且需要配置合适的跨域策略来允许前端和后端之间的通信。
    2. 微服务架构有什么特点?
      • 独立部署和扩展:每个微服务都可以独立地进行部署和扩展。这意味着不同的微服务可以根据自身的负载情况和业务需求,在不同的服务器或者容器中进行部署,并且可以动态地调整资源分配。例如,在一个电商系统中,订单处理微服务在购物高峰期可以独立地扩展服务器资源,而用户认证微服务在负载较低时可以保持较少的资源分配。
      • 松耦合:微服务之间通过轻量级的通信机制(如 RESTful API、消息队列等)进行交互,它们相互之间是松耦合的关系。这使得一个微服务的修改或者更新不会对其他微服务产生直接的影响,只要接口保持不变。例如,商品管理微服务的内部业务逻辑更新,只要其对外提供的 API 接口(如查询商品信息、更新商品价格等接口)不变,就不会影响其他微服务(如购物车微服务、订单微服务)。
      • 技术多样性:不同的微服务可以根据自身的功能需求和特点选择不同的技术栈。例如,对于一个数据分析微服务,可以选择使用 Python 和相关的数据处理库(如 Pandas、NumPy);对于一个用户界面微服务,可以选择使用 JavaScript 框架(如 Vue.js、React.js)。这种技术多样性可以充分发挥各种技术的优势,提高系统的整体性能和开发效率。
      • 易于理解和维护:由于每个微服务的功能相对单一,代码规模相对较小,开发人员更容易理解和维护。相比一个庞大的单体应用,微服务架构将系统分解为多个小的服务单元,每个单元的职责明确。例如,在维护一个支付微服务时,开发人员只需要关注支付相关的业务逻辑、数据库操作等,而不需要在庞大的代码库中寻找相关功能。
  1. 什么是 TypeScript? 为什么需要 TypeScript?Ts 有什么特点?JavaScript 的糟粕有哪些?
    • 定义:TypeScript 是微软开发的一种开源的编程语言,它是 JavaScript 的超集。这意味着任何合法的 JavaScript 代码都是合法的 TypeScript 代码,并且 TypeScript 在 JavaScript 的基础上添加了静态类型检查系统。
    • 为什么需要 TypeScript:
      • 增强代码可靠性:通过静态类型检查,可以在编译阶段发现很多潜在的错误。例如,在 JavaScript 中,将一个字符串错误地当作数字进行运算可能在运行时才会出现错误;而在 TypeScript 中,这种类型不匹配的情况在编译时就可以被发现并提示错误,从而减少了运行时错误的发生概率。
      • 提高代码可维护性:随着项目规模的增大,TypeScript 的类型系统可以让代码的结构和意图更加清晰。开发人员可以通过查看函数参数的类型、返回值的类型等来理解代码的功能,并且在修改代码时也更容易发现可能影响其他部分的改动。
      • 更好的代码智能提示:在集成开发环境(IDE)中,TypeScript 能够提供更准确的代码自动补全和参数提示。这对于开发复杂的应用程序非常有帮助,提高了开发效率。
    • 特点:
      • 静态类型系统:可以定义变量、函数参数、返回值等的类型。例如,可以定义一个函数function add(a: number, b: number): number { return a + b; },这里明确了ab是数字类型,返回值也是数字类型。
      • 支持面向对象编程:具有类、接口、继承等面向对象编程的特性。例如,可以定义一个类class Person { constructor(public name: string, public age: number) {} },并且可以通过继承来扩展类的功能。
      • 与 JavaScript 的兼容性:可以无缝地与 JavaScript 代码混合使用。既可以在 TypeScript 项目中使用已有的 JavaScript 库,也可以将 TypeScript 代码编译为 JavaScript 代码后在浏览器或者 Node.js 环境中运行。
    • JavaScript 的糟粕:
      • 隐式类型转换:JavaScript 的隐式类型转换可能会导致一些意想不到的结果。例如,'' + 1会得到'1',而[] + 1会得到'1',这种隐式转换的规则比较复杂,容易引起混淆。
      • 全局变量污染:在 JavaScript 中,如果不小心使用了全局变量,可能会导致变量名冲突和难以追踪的问题。例如,在一个大型的 JavaScript 项目中,如果不同的模块都使用了相同的全局变量名,可能会相互干扰。
      • this 指针的复杂性:JavaScript 中的this指针的指向在不同的场景下可能会
 
继续
 
  • this 指针的复杂性(续):JavaScript 中的this指针的指向在不同的场景下可能会发生变化,这使得代码的行为有时难以预测。例如,在事件处理函数、对象方法和回调函数中,this的指向可能会因为调用方式的不同而不同。在一个 HTML 按钮的点击事件处理函数中,如果该函数作为对象的方法被传递,this可能就不再指向预期的对象,这可能导致代码出现错误或者不符合预期的行为。

  1. JavaScript 有哪些数据类型?
    • 基本数据类型:
      • Number(数字):包括整数和浮点数,如423.14。在 JavaScript 中,所有的数字都是以双精度 64 - bit 的 IEEE 754 格式存储。还有一些特殊的数字值,如NaN(Not a Number),它表示一个不是数字的值,通常是在一些非法的数学运算(如0/0)后产生;Infinity表示无穷大,-Infinity表示负无穷大。
      • String(字符串):用于表示文本数据,由零个或多个字符组成,如'Hello'、 "World"。字符串可以使用单引号或者双引号包裹,并且可以包含转义字符(如\n表示换行,\t表示制表符)。
      • Boolean(布尔值):只有两个值truefalse,用于表示逻辑真和假。布尔值通常用于条件判断和逻辑运算,如if语句和&&(逻辑与)、||(逻辑或)等运算。
      • Undefined:表示一个变量已经声明但尚未赋值。例如,let x;,此时x的值就是Undefined。如果一个函数没有明确的返回值,其返回值也是Undefined
      • Null:表示一个空值,通常用于表示一个有意的 “空” 或者 “不存在” 的对象引用。与Undefined不同,Null是开发者主动赋值的结果,如let y = null;
    • 引用数据类型:
      • Object(对象):是一种复杂的数据类型,用于存储多个键值对。可以通过对象字面量(如{ key1: value1, key2: value2 })或者使用构造函数(如new Object())来创建对象。对象的键可以是字符串或者符号(Symbol),值可以是任何数据类型。例如,let person = { name: 'John', age: 30 };
      • Array(数组):是一种特殊的对象,用于存储一组有序的数据。可以通过数组字面量(如[1, 2, 3])或者使用构造函数(如new Array())来创建数组。数组中的元素可以是任何数据类型,并且可以通过索引(从 0 开始)来访问和操作元素,如let numbers = [1, 2, 3]; console.log(numbers[0]);会输出1
      • Function(函数):函数在 JavaScript 中也是一种对象,它可以包含一段可执行的代码。可以通过函数声明(如function add(a, b) { return a + b; })或者函数表达式(如let multiply = function(a, b) { return a * b; })来定义函数。函数可以接收参数并且返回一个值,它们是 JavaScript 实现各种逻辑和操作的关键部分。
      • Date(日期):用于处理日期和时间相关的数据。可以通过new Date()来创建一个日期对象,并且可以使用该对象的各种方法(如getFullYear()getMonth()getDate()等)来获取日期和时间的信息。例如,let today = new Date(); console.log(today.getFullYear());可以获取当前年份。
      • RegExp(正则表达式):用于匹配和处理文本中的模式。可以通过正则表达式字面量(如/pattern/flags)或者使用构造函数(如new RegExp('pattern', 'flags'))来创建正则表达式。例如,let pattern = /abc/; console.log(pattern.test('abcdef'));会输出true,因为字符串abcdef中包含abc这个模式。
  2. TypeScript 中 any 与 unknown 的区别是什么?
    • any 类型:
      • 含义:any类型是一种宽松的类型声明,它可以表示任何类型的值。当一个变量被声明为any类型时,TypeScript 编译器基本上会放弃对该变量的类型检查。例如,let variable: any; variable = 10; variable = 'string'; variable = true;都是合法的操作,编译器不会对这些赋值操作进行类型错误的提示。
      • 使用场景:通常用于在将 JavaScript 代码迁移到 TypeScript 代码的过程中,当遇到一些复杂的、难以确定类型或者需要动态改变类型的情况时,可以暂时使用any类型。但过度使用any会削弱 TypeScript 的类型安全优势。
    • unknown 类型:
      • 含义:unknown类型表示一个值的类型是未知的。与any不同的是,TypeScript 对unknown类型的值进行了更严格的类型检查。在对unknown类型的值进行操作之前,必须先进行类型断言或者类型检查。例如,let value: unknown; value = 10;是合法的,但是如果想对value进行数学运算,如let result = value + 5;,编译器会报错,因为它不知道value是否是数字类型。
      • 使用场景:用于在真正的类型不确定的情况下,并且需要在后续代码中进行类型检查来确保类型安全。比如在处理用户输入或者从外部接口获取的数据,这些数据的类型可能是各种各样的,使用unknown类型可以在确保安全的前提下处理这些不确定类型的数据。
  3. 什么是前端工程化?有什么作用?你使用什么工具进行工程化?
    • 定义:前端工程化是指使用一系列工具和方法,对前端开发的全过程(包括但不限于代码开发、构建、测试、部署等)进行规范化和自动化管理。
    • 作用:
      • 提高开发效率:通过自动化构建工具(如 Webpack),可以自动处理诸如代码压缩、模块打包、CSS 预处理等任务。例如,在一个大型的前端项目中,每次修改代码后不需要手动去压缩代码或者将多个 JavaScript 文件合并,构建工具可以自动完成这些操作,节省时间。
      • 代码质量保证:代码检查工具(如 ESLint)可以检查代码是否符合一定的代码规范(如缩进规则、变量命名规则等),并且可以发现一些潜在的错误(如未使用的变量、可能的语法错误等)。单元测试工具(如 Jest)可以对代码进行单元测试,确保每个功能模块的正确性。
      • 项目结构优化:工程化可以帮助组织项目的文件结构,使得代码的组织更加合理。例如,通过模块打包工具,可以将不同功能的代码模块清晰地划分和组织,方便开发人员维护和理解项目结构。
    • 工具:
      • Webpack:是一个强大的模块打包工具。它可以将多个 JavaScript 模块(包括 ES6 模块、CommonJS 模块等)打包成一个或多个文件,并且可以处理各种资源(如 CSS、图片、字体等)。例如,在一个使用 React.js 开发的项目中,Webpack 可以将所有的 React 组件和相关的代码打包成一个bundle.js文件,方便在浏览器中加载。
      • Babel:主要用于将 ES6 + 代码转换为向后兼容的 JavaScript 代码。它可以处理新的 JavaScript 语法(如箭头函数、类、解构赋值等),使其能够在旧版本的浏览器中运行。在前端项目中,Babel 通常与 Webpack 等构建工具一起使用,确保代码的兼容性。
      • ESLint:是一个代码检查工具,它可以通过配置规则来检查代码是否符合特定的风格和质量标准。可以自定义规则或者使用一些流行的代码规范(如 Airbnb 风格指南)。例如,ESLint 可以检查变量命名是否符合驼峰命名法,函数定义是否有正确的缩进等。
  4. 什么是前端模块化?有什么作用?
    • 定义:前端模块化是一种将前端代码按照功能或者逻辑划分为独立的模块,每个模块有自己的作用域和接口,并且可以被其他模块引用和复用的开发方式。
    • 作用:
      • 代码复用:通过将代码封装成模块,可以方便地在不同的项目或者项目的不同部分复用代码。例如,开发了一个用于处理日期格式化的模块,在多个页面或者应用中都需要进行日期格式化操作时,就可以直接引用这个模块,而不需要在每个地方重新编写相同的代码。
      • 避免命名冲突:每个模块都有自己独立的作用域,这样可以避免不同模块之间的变量名和函数名冲突。例如,在一个大型的前端项目中,不同的开发人员可能会定义相同名称的函数,但是通过模块化,这些函数被封装在各自的模块中,不会相互干扰。
      • 提高可维护性:当代码被划分为模块后,每个模块的功能相对独立,代码结构更加清晰。在维护或者修改代码时,可以更容易地定位到相关的模块进行操作。例如,如果要修改一个用户登录模块的功能,只需要找到对应的登录模块代码进行修改,而不会影响其他无关的模块。
  5. 如何应对 npm 网络不通畅的问题?
    • 切换镜像源:
      • 国内有许多 npm 镜像源,如淘宝镜像(https://registry.npm.taobao.org/)。可以通过配置 npm 的镜像源来加快包的下载速度。可以使用命令npm config set registry https://registry.npm.taobao.org/来将默认的镜像源切换为淘宝镜像。这样在安装依赖包时,就会从淘宝镜像服务器获取,通常速度会更快,尤其是在网络环境不佳或者访问国外服务器较慢的情况下。
    • 使用代理服务器:
      • 如果所在的网络环境限制了直接访问 npm 仓库,但允许通过代理服务器访问,那么可以配置 npm 使用代理。可以通过npm config set proxy http://your - proxy - server - address:portnpm config set https - proxy http://your - proxy - server - address:port来设置 HTTP 和 HTTPS 代理,其中your - proxy - server - address是代理服务器的地址,port是代理服务器的端口。
    • 离线安装:
      • 如果网络完全不通畅,可以先在有网络的环境下将所需的依赖包下载好,然后将这些包复制到没有网络的开发环境中进行离线安装。可以使用npm pack命令将一个本地的包打包成一个.tgz文件,然后在离线环境中使用npm install命令并指定这个打包好的文件来进行安装。例如,在有网络的机器上执行npm pack my - package得到一个包文件,然后将这个文件复制到离线环境中,执行npm install my - package - 1.0.0.tgz
  6. Vue3 中 ref 与 reactive 有什么区别?
    • ref:
      • 基本概念:ref是一个函数,用于创建一个响应式的数据引用。它接受一个内部值并返回一个响应式的、可变的Ref对象。这个Ref对象有一个.value属性,用于访问和修改内部值。例如,const count = ref(0); console.log(count.value);可以访问count的值,count.value ++;可以修改count的值。
      • 应用场景:主要用于基本数据类型(如数字、字符串、布尔值等)的响应式处理。在组合式 API 中,ref使得这些基本类型的数据也能够在模板或者其他响应式计算中被追踪变化。例如,在一个计数器组件中,使用ref来保存计数的值,当这个值发生变化时,模板会自动更新。
    • reactive:
      • 基本概念:reactive也是一个函数,用于创建一个深层次响应式的对象。它将一个普通对象转换为响应式对象,这个对象的所有属性都会被转换为响应式的。例如,const state = reactive({ count: 0, message: 'Hello' }); console.log(state.count);可以访问对象state的属性,并且当属性发生变化时(如state.count ++;),与之绑定的模板部分也会自动更新。
      • 应用场景:适合处理复杂的对象数据结构,如包含多个属性的对象或者嵌套对象。在组件中,当需要管理一个包含多个相关数据的状态对象时,使用reactive是比较合适的。例如,在一个表单组件中,使用reactive来管理表单的所有字段(如姓名、年龄、邮箱等)的数据状态。
    • 区别总结:
      • 数据类型处理:ref主要用于基本数据类型,返回的是一个带有.value属性的Ref对象;reactive用于对象类型,直接将对象转换为响应式的。
      • 响应式转换范围:ref对基本数据类型进行简单的包裹,使其具有响应式;reactive会对对象进行深层次的响应式转换,包括对象的所有嵌套属性。
  7. vuex 与 pinia 有什么区别?
    • 状态管理理念:
      • vuex:是 Vue.js 的官方状态管理库,它基于 Flux 架构模式。采用集中式存储管理应用的所有组件的状态,通过定义mutations(同步修改状态)、actions(可以包含异步操作,通过提交mutations来间接修改状态)和getters(用于获取和计算状态)来管理状态的变化。例如,在一个大型的 Vue.js 应用中,所有组件的共享状态(如用户登录信息、购物车数据等)都存储在store中,通过mutationsactions来确保状态的一致性和可预测性。
      • pinia:是 Vue.js 的下一代状态管理库,它简化了状态管理的过程。它也采用集中式存储,但更强调简单和直观。没有了mutations的严格限制,actions可以直接修改状态,使得代码更加灵活。例如,在一个小型的 Vue.js 应用或者快速原型开发中,pinia 可以让开发者更快速地定义和使用状态管理。
    • API 复杂度:
      • vuex:由于其严格的架构和规则,API 相对复杂。开发者需要理解mutationsactionsgetters的概念和使用方法,并且在修改状态时需要遵循一定的流程。例如,在mutations中只能进行同步操作,异步操作必须放在actions中,这对于初学者来说可能会有一定的学习成本。
      • pinia:API 更加简洁和直观。它的store定义更像是一个普通的 JavaScript 对象,只是具有响应式的属性。例如,在 pinia 中定义一个store,可以直接通过store的方法来修改状态,不需要像 vuex 那样通过复杂的mutationsactions流程。
    • 模块系统:
      • vuex:具有模块(module)系统,用于将大型的store划分为多个小的模块,每个模块有自己的statemutationsactionsgetters。这有助于组织和管理复杂的状态。例如,在一个电商应用中,可以将用户模块、产品模块、订单模块等分别定义为不同的vuex模块。
      • pinia:也支持模块,但模块的定义和使用更加灵活。它可以更好地处理动态模块的添加和删除,并且模块之间的嵌套和共享更加方便。例如,在一个具有动态加载功能的应用中,pinia 的模块可以更容易地根据需要进行添加和配置。
  8. 如何看看待 AI 对程序员的冲击?
    • 积极方面:
      • 提高生产力:AI 可以帮助程序员更快地生成代码片段、提供代码优化建议和查找错误。例如,在编写复杂的算法或者处理重复性的任务(如数据验证、格式转换等)时,AI 工具可以提供现成的代码模板或者自动完成部分代码,节省时间和精力。
      • 辅助学习和知识获取:对于新技术和编程语言,AI 可以作为一个很好的学习辅助工具。它可以解释复杂的概念、提供示例代码和推荐相关的学习资源。例如,当程序员想要学习一个新的框架(如 React.js 或者 Spring Boot)时,AI 可以帮助梳理框架的核心概念和常见的应用场景。
      • 创新推动:AI 的发展也促使程序员思考如何将 AI 技术更好地融入到软件产品中,开发出更智能的应用。例如,开发具有自然语言处理能力的应用(如智能客服系统、文本生成工具等),这为程序员提供了新的开发方向和创新机会。
    • 消极方面:
      • 就业竞争压力:随着 AI 技术的发展,一些简单的编程任务可能会被自动化,导致对初级程序员的需求可能会减少。例如,一些基础的网站模板生成、简单的数据处理脚本等工作可能会被 AI 工具部分替代,使得初级程序员在就业市场上面临更大的竞争压力。
      • 过度依赖风险:如果程序员过度依赖 AI 工具,可能会导致自身编程能力的退化。例如,长期使用 AI 生成代码而不深入理解代码背后的原理和逻辑,可能会在遇到 AI 无法解决的复杂问题或者没有 AI 工具可用的情况下,难以独立完成工作。
  
  1. watch 与 watchEffect 有什么区别?
    • watch:
      • 基本概念:watch是一个用于监听响应式数据变化的函数。它需要明确指定要监听的数据
 
继续
 
  • watch(续):
    • 可以是一个ref创建的响应式数据、reactive创建的响应式对象的某个属性或者是一个返回响应式数据的函数等。并且可以配置一些选项,比如immediate(是否在首次渲染时就立即执行回调函数)、deep(是否深度监听对象内部属性的变化,对于复杂的嵌套对象很有用)等。例如:

收起
javascript
 
import { ref, watch } from 'vue';
const count = ref(0);
watch(count, (newValue, oldValue) => {
    console.log(`count changed from ${oldValue} to ${newValue}`);
});

这里监听了ref类型的count数据,当count的值发生变化时,回调函数就会执行,输出相应的变化信息。

  • 特点:它更侧重于对特定数据的精准监听,能够明确知道是哪个数据发生了变化,并且可以获取到变化前后的值来做相应的逻辑处理,比较适合有针对性地响应数据变动情况,例如根据某个表单字段的变化去触发特定的验证逻辑或者接口请求等。
  • watchEffect:
    • 基本概念:watchEffect同样是用于响应式数据变化的,但它会立即执行一次传入的回调函数,然后自动追踪回调函数内部使用到的响应式数据,当这些数据发生变化时,回调函数会再次被执行。不需要像watch那样明确指定要监听的数据,它是隐式地根据代码中使用的响应式依赖来进行监听的。例如:

收起
javascript
 
import { ref, watchEffect } from 'vue';
const count = ref(0);
const doubleCount = ref(0);
watchEffect(() => {
    doubleCount.value = count.value * 2;
});

在这个例子中,watchEffect里的回调函数使用了count这个响应式数据,所以当count的值改变时,回调函数会再次执行,相应地更新doubleCount的值。

  • 特点:使用起来更加简洁直观,适合在需要基于某些响应式数据的变化自动执行相关逻辑,且不需要特别关注具体是哪个数据变化,只关心依赖的数据变化后要执行的后续操作的场景,比如根据多个相关数据的变化来实时更新页面上某个展示元素的状态等。
  • 区别总结:
    • 监听方式:watch需要明确指定要监听的数据,而watchEffect是自动追踪回调函数内部使用的响应式依赖来监听。
    • 首次执行:watch默认不会在首次渲染时执行回调(除非配置immediatetrue),而watchEffect会立即执行一次回调函数。
    • 使用场景侧重:watch适合精准监听特定数据变化并依据变化前后的值做针对性处理的情况,watchEffect更适合简洁地处理基于响应式依赖变化而执行后续逻辑的场景。

  1. 请解释 ES6 中的箭头函数和普通函数的区别?
    • 语法形式:
      • 普通函数:有多种定义方式,比如函数声明形式function funcName(arg1, arg2) { /* 函数体 */ },还有函数表达式形式const funcName = function(arg1, arg2) { /* 函数体 */ };,并且可以通过function关键字以及括号、花括号等来构成完整的函数语法结构。例如:

收起
javascript
 
function add(a, b) {
    return a + b;
}
const multiply = function(a, b) {
    return a * b;
}

  • 箭头函数:使用箭头(=>)来定义函数,语法更加简洁。如果函数体只有一行且返回值时,可以省略花括号和return关键字,直接写表达式;如果函数体有多行,则需要用花括号包裹并加上return等语句。例如:

收起
javascript
 
const add = (a, b) => a + b;  // 简洁写法,等价于上面普通函数定义的加法函数
const multiply = (a, b) => {
    return a * b;
}

  • this 指向:
    • 普通函数:this的指向在不同的调用场景下会发生变化,取决于函数的调用方式。在全局作用域中调用函数时,this指向全局对象(在浏览器环境中是window,在 Node.js 环境中是global);在对象方法中调用时,this指向调用该方法的对象;在事件处理函数等回调函数中,this的指向又可能会根据具体的绑定情况而改变。例如:

收起
javascript
 
const obj = {
    name: 'John',
    sayName: function() {
        console.log(this.name);
    }
};
obj.sayName();  // 这里this指向obj,输出 'John'
const sayNameFn = obj.sayName;
sayNameFn();  // 这里this指向全局对象(浏览器中是window),可能输出undefined等情况

  • 箭头函数:没有自己的this,它的this是继承自外层作用域的this。这使得在箭头函数内部使用this时,其指向更加明确和稳定,不会因为调用方式的改变而改变。例如:

收起
javascript
 
const obj = {
    name: 'John',
    sayName: () => {
        console.log(this.name);  // 这里this是继承自外层作用域的this,而不是obj
    }
};
obj.sayName();

  • arguments 对象:
    • 普通函数:内置了arguments对象,它是一个类数组对象,包含了函数调用时传入的所有实际参数,无论函数定义时声明了几个参数。例如:

收起
javascript
 
function sum() {
    console.log(arguments);
    return Array.from(arguments).reduce((acc, cur) => acc + cur, 0);
}
sum(1, 2, 3);  // 这里arguments包含了1、2、3这几个参数

  • 箭头函数:没有自己的arguments对象,若要获取参数列表,需要通过函数定义时的参数来处理,或者使用剩余参数(...rest)等方式。例如:

收起
javascript
 
const sum = (...args) => {
    console.log(args);
    return args.reduce((acc, cur) => acc + cur, 0);
}
sum(1, 2, 3);  // 这里通过剩余参数args来获取传入的参数

  • 作为构造函数:
    • 普通函数:可以通过new关键字来作为构造函数创建对象实例,函数内部会自动执行一些初始化操作,比如创建this对象、将函数的属性和方法绑定到this对象上等。例如:

收起
javascript
 
function Person(name) {
    this.name = name;
}
const person = new Person('Alice');

  • 箭头函数:不能作为构造函数使用,使用new关键字调用箭头函数会报错,因为它本身没有构造函数相关的内部机制,比如不会创建this对象等。例如:

收起
javascript
 
const Person = (name) => {
    this.name = name;  // 这样写不符合箭头函数的规则,即使不考虑报错,也无法像普通函数那样创建实例
}
const person = new Person('Bob');  // 会报错

  • 使用场景侧重:
    • 普通函数:由于其灵活的this指向以及具备arguments对象等特点,更适合在需要动态改变this指向、处理不定数量的参数等较为复杂的传统编程场景中使用,比如在对象的方法定义、传统的事件处理函数等场景应用广泛。
    • 箭头函数:凭借简洁的语法和稳定的this指向特点,常用于回调函数、需要简洁写法的函数定义场景中,像在数组的mapfilterreduce等方法中作为回调函数使用时,能让代码看起来更加简洁明了,并且避免了this指向问题带来的困扰。
 
 
 
posted @ 2025-01-07 16:29  张果  阅读(649)  评论(0)    收藏  举报
AmazingCounters.com