当前位置: 首页 > news >正文

vue前端面试题——记录一次面试当中遇到的题(7)

目录

一、前端基础

1.CSS伪类和伪元素有哪些,它们的区别和实际应用

2.BFC的布局规则,实现原理,可以解决的问题

3.src和href的区别

4.iframe有哪些优点和缺点?

iframe 的优点

iframe 的缺点

5.对HTML语义化的理解,写出常见的语义化标签

一、对 HTML 语义化的理解

二、常见的语义化标签

面试回答技巧

6.position的属性有哪些,区别是什么

7.JavaScript有哪些数据类型

核心区别:原始类型 vs. 引用类型

一、原始类型(基本类型)

二、引用类型(对象类型)

8.Vue的性能优化有哪些?

9.Vue中的Key的作用是什么?

10.GET和POST请求的区别

核心区别总结表

详细解析

面试回答技巧

11.HTTP常见状态码及含义

🟢 2xx 成功

🟡 3xx 重定向

🔴 4xx 客户端错误

🟠 5xx 服务器错误

面试回答技巧

12.promise 有哪些状态

二、代码题

1.交换a,b的值,不能用临时变量

方法一:算术运算(加减法)

方法二:算术运算(乘除法)

方法三:位运算(异或操作)⭐ - 最优雅

方法四:ES6 解构赋值(严格来说用了临时变量,但语法简洁)

各种方法的比较

2.有个数组a=[3,6,9,10,15,......],其中包含了很多正整数,请编程找出其中最大的数字。

方法一:使用 Math.max() 和扩展运算符(推荐)

方法二:使用 Math.max() 和 apply()

方法三:使用 reduce() 方法

方法四:使用 for 循环(传统方法)

方法五:使用 for...of 循环

3.下列代码输出的结果是?

4.下面代码输出的结果是?

5.对下列数组去重

方法一:使用 Set(ES6,最简洁)⭐

方法二:使用 filter() + indexOf()

方法三:使用 reduce()

方法四:使用 for 循环 + includes()

方法五:使用 Map(ES6)

性能比较和选择建议

6.不使用sort方法对下列数组排序(从小到大)

方法一:冒泡排序

方法二:选择排序

方法三:插入排序

方法四:快速排序(递归)

方法五:使用 Math.min() 和循环

7.在字符串的原型链上添加一个方法(reverse),实现字符串反转;

方法一:使用数组的 reverse() 方法(推荐)

方法二:使用扩展运算符

方法三:使用 for 循环(传统方法)

方法四:使用递归

方法五:使用 reduce()


一、前端基础

1.CSS伪类和伪元素有哪些,它们的区别和实际应用

根本区别:

  • 伪类:选择元素的特定状态(如:hover, :focus)

  • 伪元素:选择元素的特定部分(如::before, ::after)

语法区别:

  • 伪类:单冒号 :

  • 伪元素:双冒号 ::(CSS3规范)

功能区别:

  • 伪类:描述元素的状态,不创建新内容

  • 伪元素:可以创建虚拟元素和内容

常用分类:

伪类:

  1. 状态伪类::hover, :active, :focus, :visited

  2. 结构伪类::nth-child(), :first-child, :last-child

  3. 表单伪类::checked, :disabled, :valid

  4. 其他伪类::not(), :target, :root

伪元素:

  1. 内容生成:::before, ::after

  2. 文本选择:::first-letter, ::first-line, ::selection

  3. 其他:::placeholder, ::marker

实际应用原则:

  1. 交互反馈:使用:hover, :focus提供视觉反馈

  2. 内容装饰:使用::before, ::after添加装饰性内容

  3. 文本美化:使用::first-letter, ::first-line增强排版

  4. 表单增强:使用:checked, :valid美化表单元素

  5. 布局辅助:使用:empty, :first-child处理边界情况

2.BFC的布局规则,实现原理,可以解决的问题

BFC的布局规则:

  1. 垂直排列:内部元素在垂直方向依次排列

  2. margin重叠:同一BFC内相邻元素的垂直margin会重叠

  3. 左边缘接触:元素左边缘与包含块左边缘接触

  4. 不与浮动重叠:BFC区域不会与浮动元素重叠

  5. 独立容器:BFC内外互不影响

  6. 包含浮动:计算高度时包含浮动元素

创建BFC的方法:

  1. 根元素<html>

  2. 浮动float: left/right

  3. 定位position: absolute/fixed

  4. displayinline-blocktable-cellflow-root

  5. overflowoverflow: hidden/auto/scroll

  6. 其他contain: layout、多列布局等

BFC解决的问题:

  1. margin重叠:通过创建BFC隔离margin

  2. 高度塌陷:清除浮动,父元素包含浮动子元素

  3. 浮动覆盖:阻止元素被浮动元素覆盖

  4. 自适应布局:实现两栏自适应布局

  5. 文字环绕:控制文字与浮动元素的关系

最佳实践:

  • 优先使用 display: flow-root(无副作用)

  • 兼容性考虑使用 overflow: hidden

  • 只在需要时创建BFC,避免过度使用

  • 理解不同创建方法的副作用

现代CSS的替代方案:

  • Flexbox:更适合一维布局

  • Grid:更适合二维布局

  • display: flow-root:专门用于创建BFC

3.src和href的区别

语义区别:

  • src源资源,用于替换当前元素的内容

  • href超文本引用,用于建立关联关系

加载行为区别:

  • src同步加载,会阻塞文档解析(如script)

  • href异步加载,通常不阻塞文档解析(如CSS)

使用场景区别:

  • src 用于

    • 嵌入可执行代码:<script src>

    • 嵌入媒体资源:<img src><video src>

    • 嵌入框架内容:<iframe src>

  • href 用于

    • 链接样式表:<link href rel="stylesheet">

    • 超链接导航:<a href>

    • 资源预加载:<link href rel="preload">

浏览器处理区别:

  • src:浏览器会下载并处理资源,替换当前元素

  • href:浏览器建立关联,在需要时加载资源

性能影响:

  • src:可能阻塞渲染,需要优化(async/defer)

  • href:通常不阻塞,但CSS会阻塞渲染

现代开发最佳实践:

  • 使用 async/defer 优化 script src

  • 使用 loading="lazy" 优化图片加载

  • 使用 preload/prefetch 优化资源加载

  • 合理使用模块化加载(type="module")

4.iframe有哪些优点和缺点?
iframe 的优点
  1. 隔离性(沙箱机制)

    • 安全隔离:iframe 拥有独立的浏览上下文,这意味着它的 JavaScript、CSS 和 DOM 与父页面是隔离的。这可以有效防止恶意脚本攻击父页面(例如,使用 sandbox 属性可以进一步限制其能力)。

    • 样式隔离:iframe 内的样式不会影响父页面,父页面的样式也不会影响 iframe(除非特别指定)。这对于嵌入第三方组件(如在线编辑器、地图)非常有用,可以避免 CSS 污染和冲突。

  2. 嵌入第三方内容

    • 跨域资源嵌入:这是 iframe 最核心的用途之一。你可以嵌入来自其他域名的页面,例如 YouTube 视频、Google 地图、在线支付页面、广告等。这是实现跨域数据展示和功能集成的常用手段。

    • 历史记录与导航:每个 iframe 都有自己的会话历史记录和 window.location 对象。这在嵌入单页面应用或需要独立导航逻辑的场景下很有用。

  3. 并行加载

    • iframe 可以和父页面并行加载,浏览器会为它分配独立的连接和资源加载线程,在某些情况下可能不会阻塞父页面的渲染。

  4. 保活机制

    • 在一些复杂的前端应用中,可以用 iframe 来“保活”父页面的某些状态(例如登录状态),防止因页面跳转或刷新而丢失。

  5. 文件上传

    • 在过去,通过 iframe 可以实现“无刷新”的文件上传体验(配合 form 的 target 属性),虽然现在已被 FormData 和 fetch/XMLHttpRequest 取代,但在一些老项目中仍然可见。


iframe 的缺点
  1. 性能开销大

    • 资源消耗:每个 iframe 都是一个完整的文档环境,需要创建额外的 DOM 树、样式上下文、JavaScript 环境等,会消耗更多的内存和 CPU 资源。

    • 阻塞父页面加载:虽然可以并行,但 iframe 的 onload 事件会阻塞父窗口的 onload 事件。如果 iframe 加载缓慢,会导致整个页面被认为“未加载完成”。

    • 连接数限制:浏览器对同一域名的并发 HTTP 请求数有限制。iframe 内的资源请求会占用这些连接,可能影响父页面其他重要资源的加载。

  2. SEO(搜索引擎优化)问题

    • 大多数搜索引擎爬虫对 iframe 内的内容权重较低,甚至可能忽略。嵌入在 iframe 中的重要内容很难被搜索引擎索引,不利于 SEO。

  3. 可访问性差

    • 屏幕阅读器等辅助技术可能难以理解和导航 iframe 内的内容,尤其是动态加载的 iframe。

  4. 跨域通信复杂

    • 虽然 iframe 可以嵌入跨域内容,但父子页面之间的 JavaScript 直接通信受到同源策略的严格限制。必须使用 postMessage API 进行通信,这增加了开发的复杂性。

  5. 布局和响应式设计困难

    • iframe 的高度需要手动计算和调整(例如通过 postMessage 通信获取内容高度),难以实现完美的自适应。在移动端,处理 iframe 的视口和缩放也是一个挑战。

  6. 安全性风险

    • 点击劫持:如果没有适当的防护(如使用 X-Frame-Options 或 Content-Security-Policy 响应头),你的网站可能被恶意网站通过 iframe 嵌入,并被用于欺骗用户进行操作(点击劫持攻击)。

    • 脚本风险:如果嵌入的第三方内容被黑,可能会对用户造成影响(尽管有沙箱隔离,但仍可能进行钓鱼攻击)。

  7. 管理复杂度高

    • 页面中存在多个 iframe 时,状态管理、事件通信、生命周期管理都会变得非常复杂。

5.对HTML语义化的理解,写出常见的语义化标签
一、对 HTML 语义化的理解

核心思想: 用正确的 HTML 标签来表达正确的内容结构,让标签本身具有含义。

通俗解释: 不仅仅是为了让页面展示出来,更是为了让机器(如搜索引擎、屏幕阅读器等)能够“读懂”你的页面结构。看到 <nav> 就知道这是导航,看到 <article> 就知道这是一篇独立的文章内容。

为什么要进行语义化?(优点)

  1. 对 SEO(搜索引擎优化)友好

    • 搜索引擎的爬虫依赖于标签来确定内容的上下文和权重。使用 <h1><article><strong> 等标签可以帮助爬虫更好地理解页面结构,抓取重要内容,从而提升网页在搜索结果中的排名。

  2. 提升可访问性

    • 屏幕阅读器等辅助技术可以为视障用户朗读网页。当它们遇到 <nav> 时,会告知用户“导航区域”;遇到 <button> 时,会明确这是一个可点击的按钮。这极大地提升了残障用户的访问体验。

  3. 便于团队开发和维护

    • 语义化的代码结构清晰,像一份文档大纲。新成员接手项目或自己日后维护时,能快速理解页面结构,降低理解和维护成本。

    • 例子: 看到 <div id="nav"> 和 <nav>,后者显然更直观。

  4. 页面结构更清晰

    • 在没有 CSS 的情况下,语义化良好的页面也能以一种文档格式清晰地显示出内容结构。

  5. 有利于跨平台数据交换

    • 移动设备、智能设备等可以更具语义地渲染网页,提供更好的体验。


二、常见的语义化标签

可以将这些标签分为几大类来记忆:

1. 布局结构类(替代无语义的 <div>

这些标签构成了页面的宏观骨架。

标签描述说明
<header>页眉通常包含网站的 Logo、标题、导航等。一个页面可以有多个,例如文章页面的文章头部也可以用它。
<nav>导航定义页面的主要导航链接区域。
<main>主内容定义文档的唯一主要内容。一个页面只应有一个 <main>
<article>文章/独立内容定义一块独立、可重复使用的内容,如博客文章、论坛帖子、新闻稿件、评论等。
<section>章节/区域定义文档中的一个主题内容分组,通常会有自己的标题。可以理解为内容的分段。
<aside>侧边栏/附属信息定义与主内容间接相关的部分,如侧边栏、引用、广告、文章摘要等。
<footer>页脚通常包含版权信息、联系方式、相关链接等。和 <header> 一样,一个页面可以有多个
<figure>独立流内容表示一段独立的内容,通常与 <figcaption> 配合使用,如图片、代码、图表等。
<figcaption>图注为 <figure> 元素定义标题。

2. 文本内容类(替代无语义的 <span>

这些标签用于标记行内文本的语义。

标签描述说明
<h1> - <h6>标题定义标题级别,<h1> 最高,<h6> 最低。应根据层次结构使用,而非仅仅为了加粗或改变字体大小。
<p>段落定义段落。
<ul> / <ol> / <li>列表定义无序列表、有序列表和列表项。
<blockquote>块引用定义一段长引用。
<q>短引用定义一段短的行内引用。
<strong>重要文本表示内容重要性,通常呈现为粗体。(有语义)
<em>强调文本表示内容需要强调,通常呈现为斜体。(有语义)
<cite>引用来源表示对某个作品(如书籍、文章)的引用。
<time>时间/日期定义一个人类可读的日期或时间。
<mark>标记/高亮表示由于相关性而需要突出显示或标记的文本。
<code>代码片段表示一小段计算机代码。
<pre>预格式化文本保留文本中的空格和换行,常用于显示代码块。

面试回答技巧

问题: “谈谈你对 HTML 语义化的理解。”

回答结构建议:

  1. 下定义: “HTML 语义化指的是在编写 HTML 时,选择最恰当的、具有明确含义的标签来构建页面结构,让标签本身就能传达内容的信息。”

  2. 讲目的/优点: “这么做的目的主要有三点:第一,对 SEO 友好,帮助搜索引擎理解页面内容;第二,提升可访问性,方便屏幕阅读器等辅助工具解析;第三,代码结构清晰,易于开发和维护。”

  3. 举例子: “比如,我们不应该用一堆 <div> 和 <span> 来搭建整个页面,而应该使用 <header><nav><main><article> 这样的标签来划分结构。对于文本强调,应该用 <strong> 和 <em> 而不是 <b> 和 <i>,因为后者没有语义,只是表示样式。”

  4. 做总结: “总之,语义化是编写高质量、可访问、易维护的 HTML 代码的基础。”

6.position的属性有哪些,区别是什么

CSS 的 position 属性用于指定一个元素在文档中的定位方式

总结表格

属性值定位基准点是否脱离文档流特点与应用场景
static正常文档流位置默认值,无法使用偏移属性。
relative自身原位置否(保留原空间)1. 微调元素位置。
2. 作为 absolute 的定位父级。
absolute最近的非static祖先是(不占空间)1. 精确布局,创建浮动层。
2. 需要父级有 relative 等定位。
fixed浏览器视口是(不占空间)固定在屏幕某个位置,不随滚动移动。用于固定栏、弹窗。
sticky视口(达到阈值后)否(但会浮动)滚动时达到阈值后变为固定定位。用于吸顶、吸底效果。
7.JavaScript有哪些数据类型

JavaScript的数据类型可以分为两大类:原始类型(基本类型) 和引用类型(对象类型)

核心区别:原始类型 vs. 引用类型
特征原始类型引用类型
存储方式值直接存储在对象本身存储在中,变量在栈中存储的是内存地址(引用)
可变性不可变。修改一个变量只会创建一个新值,不会改变原始值。可变。可以修改对象的属性,而引用地址不变。
比较方式按值比较。比较两个变量时,比较的是它们的实际值。按引用比较。比较两个变量时,比较的是它们的内存地址,而不是对象的内容。
赋值与拷贝赋值是值的拷贝。创建一个新的独立副本。赋值是引用的拷贝。两个变量指向同一个对象
一、原始类型(基本类型)

原始类型的值直接存储在变量访问的位置(栈内存),它们是不可变的。有7种原始类型:

  1. number (数字)

    • 用于整数和浮点数。

    • 还包括一些特殊值:Infinity(无穷大)、-Infinity(负无穷大)和 NaN(不是一个数字)。

    • 例子: 423.14NaNInfinity

  2. string (字符串)

    • 用于表示文本数据。可以使用单引号 ''、双引号 "" 或反引号 ``(模板字符串)创建。

    • 例子: "Hello"'World'`My name is ${name}`

  3. boolean (布尔值)

    • 只有两个值:true 和 false

    • 例子: truefalse

  4. undefined (未定义)

    • 表示一个变量已被声明但尚未被赋值。

    • 函数没有明确返回值时,默认返回 undefined

    • 例子: let a; console.log(a); // undefined

  5. null (空值)

    • 表示一个“空”或“不存在”的值。它是一个特殊的关键字。

    • 注意: typeof null 返回 "object",这是JavaScript的一个历史悠久的bug,但本身是原始值。

  6. symbol (符号,ES6新增)

    • 表示唯一的、不可变的值,主要用于对象的唯一属性名,以避免属性名冲突。

    • 例子: let id = Symbol("id");

  7. bigint (大整数,ES2020新增)

    • 用于表示任意长度的整数。通过在整数末尾加 n 来创建。

    • 用于解决 number 类型无法安全地表示大于 2^53 - 1 的整数的问题。

    • 例子: const bigNumber = 123456789012345678901234567890n;


二、引用类型(对象类型)

引用类型的值是对象,存储在堆内存中。变量中存储的实际上是指向该对象内存地址的指针(引用)

  1. object (对象)

    • 用于存储各种键值对和更复杂的实体。

    • 可以使用花括号 {} 创建。

    • 例子:

    javascript

    let person = {name: "Alice",age: 30
    };
  2. array (数组)

    • 用于存储有序的数据集合。它是一种特殊的对象,索引是属性名。

    • 可以使用方括号 [] 创建。

    • 注意: typeof [] 返回 "object"。可以使用 Array.isArray([]) 来专门检测。

    • 例子: let fruits = ["apple", "banana"];

  3. function (函数)

    • 函数也是对象,是一种“可调用对象”。

    • 注意: typeof function() {} 返回 "function",但它本质上属于对象类型。

    • 例子:

    javascript

    function greet(name) {return `Hello, ${name}!`;
    }
  4. 其他内置对象

    • Date:日期和时间

    • RegExp:正则表达式

    • MapSetWeakMapWeakSet(ES6新增)

    • Error:错误对象

    • 等等。

8.Vue的性能优化有哪些?

编码优化:

  • 不要把所有数据都放在data中
  • v-for时给每个元素绑定事件用事件代替
  • keep-alive缓存组件
  • 尽可能拆分组件,提高复用性、维护性
  • key值要保证唯一
  • 合理使用路由懒加载、异步组件
  • 数据持久化存储的使用尽量用防抖、节流优化

加载优化

  • 按需加载
  • 内容懒加载
  • 图片懒加载

用户体验

  • 骨架屏

SEO优化

  • 预渲染
  • 服务端渲染ssr

打包优化

  • CDN形式加载第三方模块
  • 多线程打包
  • 抽离公共文件

缓存和压缩

  • 客户端缓存、服务端缓存
  • 服务端Gzip压缩
9.Vue中的Key的作用是什么?

key 的主要作用是给 Vue 的虚拟 DOM 节点提供一个唯一的身份标识,以便它能够高效、准确地跟踪每个节点的身份,从而重用和重新排序现有元素,而不是随意地销毁和创建它们。

10.GET和POST请求的区别
核心区别总结表
特性GETPOST
语义/用途获取数据(幂等、安全)提交/创建数据(非幂等、不安全)
参数位置URL 的查询字符串请求正文
数据长度限制有(因浏览器和服务器对 URL 长度限制)理论上无限制
安全性参数在 URL 中可见,不安全参数在正文中,相对安全,但仍需加密
缓存会被浏览器主动缓存默认不会缓存
幂等性幂等(多次执行效果相同)非幂等(多次执行可能产生不同结果)
后退/刷新无害浏览器会提示重新提交
可见性参数直接暴露在 URL 中参数对用户不可见(但在开发者工具中可见)
书签/分享可被收藏为书签或分享不可
详细解析

1. 语义和用途(最根本的区别)

  • GET:用于请求资源,强调“读”的操作。它不应该对服务器上的数据产生任何副作用(如修改、删除)。它是安全幂等的。

    • 安全:意味着操作不会修改服务器数据。

    • 幂等:意味着执行一次和执行 N 次,对资源的状态是相同的。就像你刷新页面,多次 GET 同一个 URL,得到的数据应该是一样的。

    • 场景:获取网页、搜索、查询商品列表。

  • POST:用于提交数据,请求服务器处理一个实体(如创建新资源)。它通常会对服务器数据产生副作用。

    • 不安全:操作会修改服务器数据。

    • 非幂等:多次提交相同的数据可能会产生不同的结果(例如,重复提交订单会创建多个订单)。

    • 场景:用户登录、提交表单、上传文件、支付。

2. 参数位置和长度限制

  • GET

    • 参数通过 URL 的查询字符串传递,格式为 ?key1=value1&key2=value2

    • 由于 URL 长度有限制(不同浏览器和服务器限制不同,通常在 2KB 到 8KB 之间),所以 GET 请求传递的数据量较小。

  • POST

    • 参数放在 HTTP 请求的正文中。

    • 数据长度理论上无限制,因为请求体的大小可以由服务器配置。适合传输大量数据,如文件上传、长文本等。

    • 需要设置 Content-Type 请求头来指明正文的格式,如 application/x-www-form-urlencoded(表单格式)、multipart/form-data(文件上传)、application/json(JSON 数据)。

3. 安全性与可见性

  • GET非常不安全。所有参数都明文展示在 URL 中,会出现在:

    • 浏览器地址栏

    • 浏览器历史记录

    • 服务器日志

    • Referer 头(如果从该页面跳转)

    • 绝对不能用 GET 请求传输密码等敏感信息!

  • POST相对安全。参数在请求体中,不会直接暴露在 URL 中。但这不等于加密!数据在传输过程中仍然是明文的,除非使用 HTTPS。在浏览器开发者工具的 Network 面板中依然可以查看。

4. 缓存、书签与历史记录

  • GET

    • 可以被浏览器、代理服务器缓存。

    • 可以收藏为书签。

    • 后退、刷新操作是无害的,浏览器会直接从缓存加载或重新发起一个相同的 GET 请求。

  • POST

    • 默认不会被缓存。

    • 无法收藏为书签(因为书签只保存 URL,不保存请求体)。

    • 后退或刷新时,浏览器会提示“确认重新提交表单”,因为可能会重复执行非幂等的操作。


常见误区与深度辨析

误区一:GET 只能获取数据,POST 只能创建数据?

不对。 从技术上讲,你完全可以用 GET 请求通过 URL 参数去“删除”一条数据,也可以用 POST 请求去“查询”一个复杂条件的数据(例如,查询接口要求传入一个巨大的 JSON 对象)。

但是,这是违反 HTTP 协议语义的糟糕实践。一个好的 RESTful API 设计会严格遵守这些语义:

  • GET /users - 获取用户列表

  • POST /users - 创建一个新用户

  • GET /users/1 - 获取 ID 为 1 的用户

  • PUT /users/1 - 更新 ID 为 1 的用户

  • DELETE /users/1 - 删除 ID 为 1 的用户

误区二:POST 比 GET “更安全”?

不完全对。 如上所述,POST 只是“眼不见为净”,数据没有直接暴露在 URL 中。但如果不用 HTTPS,两者在网络传输中都是裸奔的,都能被中间人窃取。真正的安全必须依靠 HTTPS

误区三:GET 产生一个 TCP 数据包,POST 产生两个?

这是一个流传很广但不完全准确的说法。其逻辑是:GET 请求的 Header 和 Data 可以一起发送,而 POST 需要先发 Header,服务器返回 100 Continue 后再发 Data。

事实是

  • 现代浏览器对 GET 请求,如果 URL 和 Header 长度超过一个 TCP 包的容量,也会分成多个包。

  • 对于 POST 请求,大多数浏览器(如 Firefox)会直接将 Header 和 Body 一起发送,而不是分两步。

所以,不能简单地用数据包数量来区分它们


面试回答技巧

问题: “说说 GET 和 POST 请求的区别。”

回答结构建议:

  1. 总起:“GET 和 POST 是 HTTP 协议中最基础的两种请求方法,它们的核心区别在于语义不同。”

  2. 阐述核心区别

    • “GET 的语义是获取数据,它是安全幂等的,通常用于查询操作。它的参数会附加在 URL 的查询字符串中,所以有长度限制,且不安全。”

    • “POST 的语义是提交数据,它是非安全非幂等的,通常用于创建或更新资源。它的参数放在请求体中,没有长度限制,相对安全一些。”

  3. 补充其他重要区别:“除此之外,它们在缓存行为上也有很大不同:GET 请求会被浏览器主动缓存,可以收藏为书签;而 POST 默认不会。另外,在浏览器中后退或刷新时,GET 是无害的,而 POST 会提示重新提交。”

  4. 总结与最佳实践:“因此,在实际开发中,我们应该遵循 HTTP 协议的语义来使用它们,而不是混用。并且要记住,无论是 GET 还是 POST,传输敏感信息时都必须使用 HTTPS 来保证真正的安全。”

11.HTTP常见状态码及含义
🟢 2xx 成功
状态码含义场景
200 OK请求成功这是最常見的成功状态。GET 请求成功获取资源,POST 请求成功提交数据后返回此状态。
201 Created已创建请求成功,并且服务器创建了新的资源(例如,通过 POST 或 PUT 请求创建了新用户)。通常配合 Location header 返回新资源的地址。
204 No Content无内容服务器成功处理了请求,但不需要返回任何实体内容。(响应体为空)
206 Partial Content部分内容客户端进行了范围请求(如断点续传),服务器成功返回了部分资源。
🟡 3xx 重定向
状态码含义场景与区别
301 Moved Permanently永久重定向请求的资源已被永久移动到新位置。浏览器和搜索引擎会缓存这个重定向,下次直接访问新地址。
302 Found临时重定向请求的资源临时从不同的 URI 响应。浏览器不会缓存,下次仍访问原地址。 这是最常见的临时重定向。
304 Not Modified未修改用于缓存控制。当客户端发送带条件的 GET 请求(如 If-Modified-Since)时,如果资源未改变,服务器会返回 304,告诉客户端直接使用本地缓存。(重要性能优化)

面试常问:301 和 302 的区别?

核心区别:缓存和行为。

  • 301 是永久搬家,告诉浏览器和搜索引擎:“以后请直接去新地址找我。” 会更新书签和搜索引擎索引。

  • 302 是临时出差,告诉浏览器:“这次请去另一个地址拿东西,但下次来原地址找我。” 不会更新书签和索引。

🔴 4xx 客户端错误
状态码含义场景与排查方向
400 Bad Request错误请求服务器无法理解请求的格式,通常是请求参数有误、JSON 格式错误、缺少必要参数(前端需要检查发送的数据)
401 Unauthorized未授权请求要求身份认证。(用户未登录或 Token 无效) 通常需要弹出登录框。
403 Forbidden禁止访问服务器理解请求,但拒绝执行。(用户已登录,但权限不足)
404 Not Found未找到服务器找不到请求的资源。(检查请求的 URL 路径是否正确,资源是否已删除)
405 Method Not Allowed方法不允许请求行中指定的请求方法不能被用于请求相应的资源。(例如,接口只支持 POST,但你用了 GET)

面试常问:401 和 403 的区别?

核心区别:身份认证 vs 权限控制。

  • 401 Unauthorized:意思是“你是谁?”。问题出在身份未验证,服务器不知道你的身份。

  • 403 Forbidden:意思是“我知道你是谁,但你不被允许做这个操作”。问题出在权限不足,服务器知道你的身份,但你的权限不够。

🟠 5xx 服务器错误
状态码含义场景与排查方向
500 Internal Server Error服务器内部错误服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。(一个通用的、笼统的错误,后端代码 Bug、数据库连接失败等都可能导致)
502 Bad Gateway坏网关服务器作为网关或代理,从上游服务器收到无效响应。(常见于 Nginx 反向代理后端的服务挂掉或无法连接)
503 Service Unavailable服务不可用服务器当前无法处理请求(由于超载或系统维护)。(通常意味着服务暂时不可用,可能伴随着 Retry-After 头告诉客户端何时重试)
504 Gateway Timeout网关超时服务器作为网关或代理,没有及时从上游服务器收到请求。(常见于 Nginx 等待后端服务响应超时)

面试常问:502、503、504 的区别?

  • 502:网关本身是好的,但后面的服务挂了或返回了无法解析的响应。

  • 503:服务是存在的,但现在因为负载过高或维护而暂时无法处理任何请求。

  • 504:网关后面的服务还在运行,但处理请求太慢,超过了网关的等待时间。


面试回答技巧

问题: “请说一下你了解的 HTTP 状态码。”

回答结构建议:

  1. 总起分类: “HTTP 状态码用第一位数字表示了响应的类型,总共分为 5 类:1xx 表示信息,2xx 表示成功,3xx 表示重定向,4xx 表示客户端错误,5xx 表示服务器错误。”

  2. 列举核心状态码(按类别说):

    • “在 2xx 中,最常见的是 200 OK 表示成功,201 Created 表示创建成功,204 No Content 表示成功但无返回内容。”

    • “在 3xx 中,301 是永久重定向,302 是临时重定向,304 是缓存相关,表示资源未修改。”

    • “在 4xx 中,400 是请求参数错误,401 是未登录,403 是权限不足,404 是资源未找到。”

    • “在 5xx 中,500 是服务器内部错误,502 是网关错误,503 是服务不可用。”

12.promise 有哪些状态
  1. “Promise 有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。”

  2.  “其中 pending 是初始状态。状态一旦从 pending 变为 fulfilled 或 rejected,就不可逆,会一直保持这个结果。”

  3.  “当我们在 Promise 执行器内部调用 resolve 时,状态会变为 fulfilled;调用 reject 或发生异常时,状态会变为 rejected。”

  4.  “我们通过 .then() 方法来监听 fulfilled 状态并获取结果值,通过 .catch() 方法来监听 rejected 状态并捕获失败的原因。”

二、代码题

1.交换a,b的值,不能用临时变量

var a=100 ,b=200

答案:

方法一:算术运算(加减法)

原理

  1. 先将两数之和存入 a

  2. 用和减去 b 得到原来的 a,存入 b

  3. 用和减去新的 b(即原来的 a)得到原来的 b,存入 a

var a = 100, b = 200;a = a + b; // a = 300 (100 + 200)
b = a - b; // b = 100 (300 - 200)
a = a - b; // a = 200 (300 - 100)console.log(a, b); // 输出: 200 100
方法二:算术运算(乘除法)
var a = 100, b = 200;a = a * b; // a = 20000 (100 × 200)
b = a / b; // b = 100 (20000 ÷ 200)
a = a / b; // a = 200 (20000 ÷ 100)console.log(a, b); // 输出: 200 100

注意:这种方法要确保值不为 0,否则会出现除零错误。

方法三:位运算(异或操作)⭐ - 最优雅

原理(异或运算的特性):

  • x ^ x = 0

  • x ^ 0 = x

  • 异或满足交换律和结合律

var a = 100, b = 200;a = a ^ b; // a = 100 ^ 200
b = a ^ b; // b = (100 ^ 200) ^ 200 = 100
a = a ^ b; // a = (100 ^ 200) ^ 100 = 200console.log(a, b); // 输出: 200 100
方法四:ES6 解构赋值(严格来说用了临时变量,但语法简洁)
var a = 100, b = 200;[a, b] = [b, a];console.log(a, b); // 输出: 200 100
各种方法的比较
方法优点缺点适用场景
加减法直观易懂可能溢出(数字很大时)一般数值交换
乘除法直观不能处理0值,可能精度丢失非零数值
异或运算不会溢出,效率高对非整数可能不适用最佳通用方案
解构赋值代码简洁底层仍用临时变量ES6+ 环境

2.有个数组a=[3,6,9,10,15,......],其中包含了很多正整数,请编程找出其中最大的数字。

答案:

方法一:使用 Math.max() 和扩展运算符(推荐)
const a = [3, 6, 9, 10, 15, 8, 20, 5];
const max = Math.max(...a);
console.log(max); // 输出: 20
方法二:使用 Math.max() 和 apply()
const a = [3, 6, 9, 10, 15, 8, 20, 5];
const max = Math.max.apply(null, a);
console.log(max); // 输出: 20
方法三:使用 reduce() 方法
const max = a.reduce((a, b) => Math.max(a, b));
方法四:使用 for 循环(传统方法)
const a = [3, 6, 9, 10, 15, 8, 20, 5];
let max = a[0]; // 假设第一个元素是最大的for (let i = 1; i < a.length; i++) {if (a[i] > max) {max = a[i];}
}console.log(max); // 输出: 20
方法五:使用 for...of 循环
const a = [3, 6, 9, 10, 15, 8, 20, 5];
let max = a[0];for (const num of a) {if (num > max) {max = num;}
}console.log(max); // 输出: 20
3.下列代码输出的结果是?
const promise =new Promise((resolve,reject)=>{console.log(1);console.log(2);
});
promise.then(()=>{console.log(3);
});
console.log(4);

答案:1,2,4

4.下面代码输出的结果是?
const promise =new Promise((resolve,reject)=>{resolve('success1');reject('error');resolve('success2')
});
promise.then((res)=>{console.log('then:',res);
}).catch((err)=>{console.log('catch:',err);
})

答案:then: success1

5.对下列数组去重

const array=[1 , 2 , 3,  5, 1, 5, 9, 1, 2, 8 ]

答案:

方法一:使用 Set(ES6,最简洁)⭐
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // [1, 2, 3, 5, 9, 8]

或者

const uniqueArray = Array.from(new Set(array));
方法二:使用 filter() + indexOf()
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
const uniqueArray = array.filter((item, index) => {return array.indexOf(item) === index;
});
console.log(uniqueArray); // [1, 2, 3, 5, 9, 8]
方法三:使用 reduce()
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
const uniqueArray = array.reduce((acc, current) => {if (!acc.includes(current)) {acc.push(current);}return acc;
}, []);
console.log(uniqueArray); // [1, 2, 3, 5, 9, 8]
方法四:使用 for 循环 + includes()
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
const uniqueArray = [];for (let i = 0; i < array.length; i++) {if (!uniqueArray.includes(array[i])) {uniqueArray.push(array[i]);}
}
console.log(uniqueArray); // [1, 2, 3, 5, 9, 8]
方法五:使用 Map(ES6)
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8];
const uniqueArray = [...new Map(array.map(item => [item, item])).values()];
console.log(uniqueArray); // [1, 2, 3, 5, 9, 8]
性能比较和选择建议
方法优点缺点适用场景
Set⭐ 最简洁,性能好ES6+ 环境现代项目首选
filter + indexOf代码清晰性能较差(O(n²))小数组,兼容性好
reduce函数式编程代码稍复杂函数式编程偏好
for 循环兼容性好,可控性强代码较长需要兼容老浏览器
Map性能好代码较难理解需要保持顺序的复杂数据
6.不使用sort方法对下列数组排序(从小到大)

const array=[ 1, 5, 3, 4, 9, 0, -9, 6, 7, 8]

答案:

方法一:冒泡排序
const array = [1, 5, 3, 4, 9, 0, -9, 6, 7, 8];function bubbleSort(arr) {const result = [...arr]; // 创建副本,不修改原数组const n = result.length;for (let i = 0; i < n - 1; i++) {for (let j = 0; j < n - i - 1; j++) {if (result[j] > result[j + 1]) {// 交换元素[result[j], result[j + 1]] = [result[j + 1], result[j]];}}}return result;
}const sortedArray = bubbleSort(array);
console.log(sortedArray); // [-9, 0, 1, 3, 4, 5, 6, 7, 8, 9]
方法二:选择排序
const array = [1, 5, 3, 4, 9, 0, -9, 6, 7, 8];function selectionSort(arr) {const result = [...arr];const n = result.length;for (let i = 0; i < n - 1; i++) {let minIndex = i;// 找到未排序部分的最小元素for (let j = i + 1; j < n; j++) {if (result[j] < result[minIndex]) {minIndex = j;}}// 将最小元素交换到已排序部分的末尾if (minIndex !== i) {[result[i], result[minIndex]] = [result[minIndex], result[i]];}}return result;
}const sortedArray = selectionSort(array);
console.log(sortedArray); // [-9, 0, 1, 3, 4, 5, 6, 7, 8, 9]
方法三:插入排序
const array = [1, 5, 3, 4, 9, 0, -9, 6, 7, 8];function insertionSort(arr) {const result = [...arr];const n = result.length;for (let i = 1; i < n; i++) {const current = result[i];let j = i - 1;// 将当前元素插入到已排序部分的正确位置while (j >= 0 && result[j] > current) {result[j + 1] = result[j];j--;}result[j + 1] = current;}return result;
}const sortedArray = insertionSort(array);
console.log(sortedArray); // [-9, 0, 1, 3, 4, 5, 6, 7, 8, 9]
方法四:快速排序(递归)
const array = [1, 5, 3, 4, 9, 0, -9, 6, 7, 8];function quickSort(arr) {if (arr.length <= 1) {return arr;}const pivot = arr[Math.floor(arr.length / 2)];const left = [];const right = [];const equal = [];for (const element of arr) {if (element < pivot) {left.push(element);} else if (element > pivot) {right.push(element);} else {equal.push(element);}}return [...quickSort(left), ...equal, ...quickSort(right)];
}const sortedArray = quickSort(array);
console.log(sortedArray); // [-9, 0, 1, 3, 4, 5, 6, 7, 8, 9]
方法五:使用 Math.min() 和循环
const array = [1, 5, 3, 4, 9, 0, -9, 6, 7, 8];function manualSort(arr) {const result = [];const temp = [...arr];while (temp.length > 0) {// 找到当前数组中的最小值let minIndex = 0;for (let i = 1; i < temp.length; i++) {if (temp[i] < temp[minIndex]) {minIndex = i;}}// 将最小值添加到结果数组,并从临时数组中移除result.push(temp[minIndex]);temp.splice(minIndex, 1);}return result;
}const sortedArray = manualSort(array);
console.log(sortedArray); // [-9, 0, 1, 3, 4, 5, 6, 7, 8, 9]
7.在字符串的原型链上添加一个方法(reverse),实现字符串反转;
方法一:使用数组的 reverse() 方法(推荐)
// 在 String 原型上添加 reverse 方法
String.prototype.reverse = function() {// 将字符串分割成数组,反转数组,再合并回字符串return this.split('').reverse().join('');
};// 测试
const str = "Hello World";
console.log(str.reverse()); // 输出: "dlroW olleH"const chineseStr = "你好世界";
console.log(chineseStr.reverse()); // 输出: "界世好你"
方法二:使用扩展运算符
String.prototype.reverse = function() {return [...this].reverse().join('');
};// 测试
console.log("JavaScript".reverse()); // 输出: "tpircSavaJ"
方法三:使用 for 循环(传统方法)
String.prototype.reverse = function() {let reversed = '';for (let i = this.length - 1; i >= 0; i--) {reversed += this[i];}return reversed;
};// 测试
console.log("ABCDE".reverse()); // 输出: "EDCBA"
方法四:使用递归
String.prototype.reverse = function() {if (this.length <= 1) {return this.toString();}return this.substring(1).reverse() + this[0];
};// 测试
console.log("Recursion".reverse()); // 输出: "noisruceR"
方法五:使用 reduce()
String.prototype.reverse = function() {return [...this].reduce((reversed, char) => char + reversed, '');
};// 测试
console.log("Reduce".reverse()); // 输出: "ecudeR"
http://www.dtcms.com/a/482537.html

相关文章:

  • 算法9.0
  • 商丘哪里做网站网页加速器怎么开
  • 未来之窗昭和仙君(十九)商用虚拟数字金额键盘——东方仙盟筑基期
  • 每日小知识点:10.14 webpack 有几种文件指纹
  • 怎样撰写企业网站建设方案wordpress主题layui
  • 地区性门户网站是什么意思阿里云做网站可以吗
  • 怎样批量在图片上加12345的数字编号?实用教程分享
  • 【avalonia教程】10数据绑定语法格式
  • 图像分类数据集难度怎么评?
  • 管理系统有哪些布局框架,比如左右,上下,F型号,T型等
  • 设计网站意味着什么如何用手机制作app
  • 网站跳出率 查询免费建立自己喜欢的
  • 纵向合并和横向合并工作表的思路
  • 图像锐化的魔法棒:深入浅出理解USM锐化算法
  • PHP网站开发都需要学什么做网站用什么电脑配置
  • Naive RAG
  • 做网站开源框架本土广告公司
  • MacOS 安装器安装,正在等待其他安装完成
  • 惠洋科技H5442L 100V高耐压LED恒流驱动芯片80V72V60V48V降压12V9V6V1.2Aic方案 PWM+模拟调光
  • 网站自建设需要买什么时候开始免费推广链接
  • ElasticSearch生产环境问题集锦
  • 深圳官网建站服务商网站建设空间申请
  • RAG系统向量化存储技术深度解析:双索引架构与批量处理实践
  • 复习总结最终版:计算机网络
  • wordpress导航站模版海南省建设注册中心网站
  • 成都建设路小学网站可以看的网站都有哪些
  • Unreal Engine 跨平台构建完全指南
  • 一个专门做恐怖片的网站安徽华建建设工程公司网站
  • selenium对每种前端控件的操作,python举例
  • php网站打开慢网站设计需要考虑的基本原则