前端面试打怪升级 ---》面经复盘

340 阅读20分钟

前言:小编其实早该写这篇博客了,但是当时面试完第二轮之后面试官让我好好准备一下HR面,可是等了好久都没消息,应该是找到更合适的人选了吧,呜呜呜...... 不过也不能气馁,该复盘还是复盘一下,作为加深个人印象的一次面经复盘以及和掘友们分享一下本次的二轮面试...

1. 自我介绍

2.(直接问项目了,以下问题基本上是项目中相关的提问)小程序项目的preload如何实现的,主要加载的什么

"preload" 是一种预加载技术,用于在小程序启动时提前加载一些资源,以提高
后续页面的加载速度和用户体验。preload 的实现涉及两个方面:app.json 配置
和页面的预加载。

1.  app.json 配置:  
    在 app.json 文件中,可以配置 "preloadRule" 字段来指定需要预加载的资源。
    该字段是一个数组,每个元素表示一个资源的预加载规则。预加载规则包括以下属性:

    -   "type":预加载资源的类型,可以是 "image""video""audio""file" 等。
    -   "url":预加载资源的路径。
    
 以下是一个示例的 app.json 文件,其中定义了两个预加载资源:   
   {
      "pages": [
        "pages/index/index",
        "pages/detail/detail"
      ],
      "preloadRule": [
        {
          "type": "image",
          "url": "images/icon1.png"
        },
        {
          "type": "audio",
          "url": "audio/music.mp3"
        }
      ],
      "window": {
        "backgroundTextStyle": "light",
        "backgroundColor": "#F7F7F7",
        "navigationBarBackgroundColor": "#000000",
        "navigationBarTitleText": "My App",
        "navigationBarTextStyle": "white"
      }
    }
    
 2.  页面的预加载:  
    在需要预加载的页面中,可以使用 `wx.loadPreloadPage()` 方法来手动触发页面的
    预加载。该方法接受一个页面路径作为参数,用于指定需要预加载的页面。
    
   以下是一个示例,在小程序首页的 `onLoad` 生命周期函数中使用 `wx.loadPreloadPage()` 预加载详情页:
   Page({
      onLoad: function () {
        // 预加载详情页
        wx.loadPreloadPage({
          url: 'pages/detail/detail'
        });
      },
      // 其他页面代码...
    })
    

3. promise.all原理是什么,你是怎么实现的

`Promise.all()` 是 JavaScript 中的一个方法,它接受一个由 Promise 对象组成的
数组,并返回一个新的 Promise 对象。这个新的 Promise 对象在传入的所有 Promise 
对象都变为 resolved 状态时才会变为 resolved,否则只要有一个 Promise 对象变为
rejected 状态,就会立即变为 rejected。

`Promise.all()` 方法的原理可以简要描述如下:

1.  创建一个新的 Promise 对象。
2.  遍历传入的 Promise 数组,并监听每个 Promise 对象的状态。
3.  如果所有的 Promise 对象都变为 resolved 状态,将新的 Promise 对象的状态设为 resolved,并将所有 Promise 对象的返回值作为结果值传递给新的 Promise 对象。
4.  如果任何一个 Promise 对象变为 rejected 状态,将新的 Promise 对象的状态设为 rejected,并将第一个被 rejected 的 Promise 对象的错误信息作为结果值传递给新的 Promise 对象。
5.  返回新的 Promise 对象。

下面是一个简单的示例,演示了 `Promise.all()` 的原理:
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);

const promises = [promise1, promise2, promise3];

Promise.all(promises)
  .then(results => {
    console.log(results); // [1, 2, 3]
  })
  .catch(error => {
    console.error(error);
  });
  
在实际应用中,`Promise.all()` 方法常用于同时处理多个异步操作,并在所有操作
完成后执行一些逻辑或获取最终的结果。是一种等待机制的思想。

4. promise是宏任务吗

JavaScript 中,`Promise` 不是宏任务,它是一种用于处理异步操作的语言特性。

宏任务(macro-task)和微任务(micro-task)是 JavaScript 中用于调度和执行异步操作的两种不同的任务队列。

宏任务包括以下几种常见的异步操作:

-   `setTimeout` 和 `setInterval` 定时器
-   I/O 操作
-   页面渲染

微任务包括以下几种常见的异步操作:

-   `Promise` 的 `then` 和 `catch` 方法
-   `MutationObserver` 监听器
-   `queueMicrotask` 函数

5. 项目中天空盒部分代码封装的思想是什么

使用es6的importexport的语法

1.  **模块导出(Export)** :在模块中将需要导出的变量、函数或类标记为导出项。
可以使用`export`关键字来导出单个项,或使用`export default`关键字导出默认项。

2.  **模块导入(Import)** :在需要使用模块的地方,使用`import`语句导入
需要的模块。可以使用`import`关键字导入具名导出项,或使用`import ... from`语法导入默认导出项。

通过使用ES6的模块化语法,可以实现更清晰、可维护的代码结构,并提供更好的代码组织
和重用性。模块化的代码风格使得各个模块之间的依赖关系更加明确,降低了代码耦合度,
提高了代码的可读性和可维护性。

6. 登录模块写了节流,请说说节流防抖的区别

节流(Throttling)和防抖(Debouncing)是两种常用的优化技术,用于控制事件的触发频率,以提高性能和用户体验。它们的主要区别在于如何处理事件的延迟执行。

1.  **节流(Throttling)** :  
    节流是指在一段时间内固定间隔执行某个操作。当事件被触发时,如果在指定的时间间隔内没有再次触发该事件,那么就执行一次操作。如果在时间间隔内多次触发事件,只有第一次触发会执行操作,后续的触发会被忽略,直到过了时间间隔后才能再次触发。

    节流的主要特点是无论事件触发频率多高,操作都会被稳定地执行,但执行的频率有限制。适用于需要控制执行频率的场景,如滚动事件、窗口调整事件等。

    示例场景:当用户持续滚动页面时,每隔一定时间执行一次图片加载操作,避免频繁的图片加载请求。

1.  **防抖(Debouncing)** :  
    防抖是指在事件触发后等待一段时间,如果在这段时间内没有再次触发该事件,那么就执行操作。如果在等待时间内再次触发了该事件,则重新计算等待时间。
     
    防抖的主要特点是只有在事件停止触发一段时间后才执行操作,而在此期间的触发会被忽略。适用于需要等待用户停止操作后再执行操作的场景,如输入框输入事件、搜索框联想功能等。

    示例场景:在用户输入搜索关键字时,等待一段时间后执行搜索操作,以避免在用户还在输入过程中频繁触发搜索请求。
    
总结:

-   节流控制事件的触发频率,固定时间间隔内只执行一次操作。
-   防抖等待事件停止触发一段时间后执行操作,期间的触发会被忽略。
-   节流适用于需要控制执行频率的场景,防抖适用于需要等待事件停止后执行操作的场景。

大家也可以去看看小编另一篇文章,传送门如下:

防抖与节流

7. 说说你对移动端适配的理解

移动端适配是指`将网页或应用程序的布局和样式适配到不同移动设备的屏幕尺寸、分辨率和像素密度上`,
以提供一致的用户体验。由于移动设备的多样性,适配移动端是开发移动应用或响应式网页设计中至关重要的一部分。

以下是对移动端适配的几个关键方面的理解:

1.  **响应式设计(Responsive Design)** :响应式设计是一种通过使用弹性布局、媒体查询和其他技术,使网页在不同设备上以适当的方式进行布局和显示的方法。通过响应式设计,可以根据设备的屏幕尺寸和分辨率,自动调整网页的布局和样式。
1.  **流式布局(Fluid Layout)** :流式布局是一种相对于固定像素宽度的布局方式,它使用比例或百分比来设置元素的宽度和高度。流式布局可以根据屏幕尺寸的变化自动调整元素的大小和位置,以适应不同设备的屏幕。
1.  **媒体查询(Media Queries)** :媒体查询是CSS3中的一种技术,它允许根据不同的媒体类型、特征和条件来应用不同的样式。通过使用媒体查询,可以根据设备的屏幕尺寸、宽度和像素密度等特征,应用不同的样式规则,以适配不同的移动设备。
1.  **视口(Viewport)** :视口是指网页在设备上可见的区域。移动设备的视口通常比桌面设备小,因此需要针对不同的设备调整视口的大小和缩放比例。通过使用视口标签和meta标签中的viewport属性,可以控制网页在移动设备上的视口大小和缩放行为。
1.  **像素密度(Pixel Density)** :移动设备的像素密度通常比传统的桌面设备高,这意味着在相同尺寸的屏幕上,移动设备能够显示更多的像素。在适配移动端时,需要考虑不同设备的像素密度,使用高分辨率图像和矢量图标,以确保图像和图标在不同设备上显示清晰。

综上所述,移动端适配涉及到响应式设计、流式布局、媒体查询、视口设置和像素密度处理等方面。通过综合运用这些技术和方法,可以实现在不同移动设备上提供一致的用户体验,使网页或应用程序在各种移动设备上都能良好地展示和交互。

8. XXX项目难点 (大屏支配方案选取,为什么不用REM,EM等)

9. 清除浮动的方式有哪些

1. 使用`clear属性`: 在父元素的末尾添加一个空的块级元素,并为该元素设置clear属性,如下所示:
    <div style="clear:both;"></div>
    
2. 使用`overflow属性`: 为父元素添加overflow属性,可以触发BFC(块级格式化上下文),从而清除浮动。
.parent {
    overflow: hidden;
}

3. 使用`clearfix技巧`: 在CSS中定义一个clearfix类,将该类应用于父元素,如下所示:
.clearfix::after {
    content: ""; `注意这里`
    display: block;
    clear: both;
}
<div class="parent clearfix">
    <!-- 子元素浮动内容 -->
</div>

10. CSS哪些属性可以继承


以下是一些常见的可继承属性:
1.  **文本相关属性:**

    -   `color`
    -   `font-family`
    -   `font-size` (注意:`font-size` 的百分比值会相对于父元素的字体大小)
    -   `font-style`
    -   `font-variant`
    -   `font-weight`
    -   `letter-spacing`
    -   `line-height`
    -   `text-align`
    -   `text-indent`
    -   `text-transform`
    -   `visibility`
    -   `white-space`
    -   `word-spacing`

1.  **列表相关属性:**

    -   `list-style`
    -   `list-style-image`
    -   `list-style-position`
    -   `list-style-type`

1.  **表格相关属性:**

    -   `border-collapse`
    -   `border-spacing`
    -   `caption-side`
    -   `empty-cells`
    -   `table-layout`

1.  **光标相关属性:**

    -   `cursor`


### 不可继承属性

以下是一些常见的不可继承属性:

1.  **盒模型属性:**

    -   `margin`
    -   `padding`
    -   `border`
    -   `width`
    -   `height`

1.  **定位属性:**

    -   `position`
    -   `top`
    -   `right`
    -   `bottom`
    -   `left`
    -   `z-index`

1.  **布局及显示属性:**

    -   `display`
    -   `float`
    -   `clear`
    -   `overflow`

1.  **背景属性:**

    -   `background`
    -   `background-color`
    -   `background-image`
    -   `background-repeat`
    -   `background-position`
    -   `background-size`

1.  **其他:**

    -   `opacity`
    -   `box-shadow`
    -   `transition`
    -   `transform`

11. HTTP缓存

HTTP缓存是Web开发中优化性能、减少服务器负载和提高用户体验的重要技术。通过缓存,
浏览器或中间代理服务器可以存储资源的副本,以便在后续请求中重复使用,而无需每次
都从源服务器获取资源。以下是对HTTP缓存的一些关键概念和机制的详细理解:

### 缓存的好处

-   **减少延迟**:从缓存中读取资源比从服务器重新获取要快得多。
-   **减轻服务器负载**:减少了服务器处理请求的次数。
-   **节省带宽**:避免了重复传输相同的数据。

### 缓存控制机制

#### 1. HTTP头字段

-   **Cache-Control**: 是最重要的缓存控制头字段,允许设置各种缓存指令。

    -   `no-cache`: 强制要求缓存再验证资源的有效性(与服务器重新验证)。
    -   `no-store`: 禁止缓存任何响应内容。
    -   `max-age=<seconds>`: 指定资源能够被缓存的最大时间,单位为秒。
    -   `public`: 表示响应可以被任何缓存存储(例如浏览器和CDN)。
    -   `private`: 表示响应仅针对单个用户缓存,不应在共享缓存中存储。
    -   `must-revalidate`: 一旦资源过期,不允许使用旧的缓存,必须向服务器重新验证。

-   **Expires**: 指定资源的过期时间,是一个绝对时间点。它已经被`Cache-Control: max-age`所取代,但仍然广泛使用。

-   **ETag**: 是资源的实体标签,用于标识资源的版本。服务器通过ETag来验证缓存的资源是否发生了变化。

-   **Last-Modified**: 指示资源上次修改的时间。用于比较缓存中的资源是否仍然有效。

#### 2. 客户端缓存机制

-   **If-None-Match**: 浏览器通过这个头字段发送先前获取资源的ETag值,服务器会用它来检查资源是否发生变化。
-   **If-Modified-Since**: 浏览器通过这个头字段发送先前获取资源的Last-Modified值,服务器会用它来检查资源是否自那时起发生了修改。
 
### `缓存策略(重点)` 

-   **`强缓存`**: 浏览器直接使用缓存数据,不与服务器通信。主要依赖于`Cache-Control: max-age``Expires`头字段。只要缓存未过期,浏览器就会直接使用缓存。
    
-   **`协商缓存`**: 当强缓存失效时,浏览器会向服务器发送请求验证缓存是否仍然有效。
    使用`ETag``Last-Modified`进行验证。如果资源未改变,服务器返回304 Not Modified
    状态码,浏览器继续使用缓存。

### 实际应用

-   **静态资源缓存**: 常见于CSSJavaScript、图像等不经常变化的文件,通常设置较长的缓存时间。
-   **API响应缓存**: 对于一些不频繁变化的API响应,可以设置短暂的缓存时间以减少服务器压力。
-   **版本化缓存**: 使用文件名或URL中的版本号来管理缓存。例如,`style.v1.css`更新为`style.v2.css`,使得新的版本能够立即生效而不受缓存影响。

### 常见问题

-   **缓存过期**: 配置不当可能导致缓存过期时间过短或过长。
-   **缓存一致性**: 当资源变化时,确保缓存及时更新。
-   **隐私和安全**: 某些敏感数据不应被缓存,避免泄露用户隐私。

12. 状态码

HTTP状态码是指在客户端向服务器发送请求后,服务器返回的标识请求处理结果的3位数字代码。
这些状态码根据其所表示的意义被分为不同的类别,例如表示成功、重定向、客户端错误和服务器错误等。
以下是一些常见的HTTP状态码及其含义:

### 1xx 信息性状态码

-   **100 Continue**: 表示服务器已经接收到请求的首部,客户端应该继续发送请求的主体部分。
-   **101 Switching Protocols**: 表示服务器已经理解并接受客户端的协议变更请求。

### 2xx 成功状态码

-   **200 OK**: 请求成功。通常用于GETPOST请求。
-   **201 Created**: 请求已经被成功处理,并且创建了新的资源。
-   **204 No Content**: 服务器成功处理了请求,但没有返回任何内容。

### 3xx 重定向状态码

-   **301 Moved Permanently**: 资源被永久移动到其他位置,客户端应该使用新的URL重新发起请求。
-   **302 Found**: 资源暂时移动到其他位置,客户端应该使用新的URL重新发起请求。
-   **304 Not Modified**: 资源未被修改,客户端可以使用缓存的版本。

### 4xx 客户端错误状态码

-   **400 Bad Request**: 服务器无法理解客户端发送的请求。
-   **401 Unauthorized**: 请求要求身份验证。
-   **403 Forbidden**: 服务器拒绝请求访问。
-   **404 Not Found**: 请求的资源不存在。

### 5xx 服务器错误状态码

-   **500 Internal Server Error**: 服务器遇到了一个意外的错误,无法完成请求。
-   **502 Bad Gateway**: 作为网关或代理的服务器从上游服务器接收到无效响应。
-   **503 Service Unavailable**: 服务器暂时无法处理请求,通常是由于过载或维护造成的。

13. 浏览器缓存

浏览器存储(Browser Storage)是指在用户的浏览器中保存数据的各种技术和机制。
这些技术允许在客户端存储数据,以便在页面刷新或在多个页面之间保持数据的持久性。
以下是几种常见的浏览器存储方式:

1. **Cookie**:
-   **用途**:主要用于会话管理、用户偏好设置和追踪用户行为。
-   **特点**:
    -   小数据存储(通常4KB左右)。
    -   数据会自动随每个HTTP请求发送到服务器。
    -   具备过期时间。
    -   支持跨域访问(如果设置了相关属性)。
-   **缺点**:安全性较低,易被拦截和篡改,不适合存储敏感信息。
// 设置Cookie
document.cookie = "username=JohnDoe; expires=Fri, 31 Dec 2024 23:59:59 GMT; path=/";
// 获取Cookie
function getCookie(name) {
    let cookies = document.cookie.split(';');
    for (let i = 0; i < cookies.length; i++) {
        let cookie = cookies[i].trim();
        if (cookie.startsWith(name + '=')) {
            return cookie.substring(name.length + 1);
        }
    }
    return "";
}

2. **LocalStorage**:
-   **用途**:用于长期存储不涉及敏感信息的数据。
-   **特点**:
    -   基于键值对存储。
    -   数据没有过期时间,除非手动删除。
    -   每个域名下的数据存储上限通常为5MB。
    -   数据仅在同一域名下的所有页面可访问。
// 存储数据
localStorage.setItem('username', 'JohnDoe');
// 获取数据
let username = localStorage.getItem('username');
// 删除数据
localStorage.removeItem('username');
// 清空所有数据
localStorage.clear();
    
3. **SessionStorage**:
-   **用途**:用于会话级别的数据存储,即数据在页面会话结束后(例如,标签页关闭)自动清除。
-   **特点**:
    -   基于键值对存储。
    -   数据仅在当前会话期间有效。
    -   每个域名下的每个标签页/窗口的数据彼此独立。
    -   存储上限通常为5MB。
 // 存储数据
sessionStorage.setItem('username', 'JohnDoe');
// 获取数据
let username = sessionStorage.getItem('username');
// 删除数据
sessionStorage.removeItem('username');
// 清空所有数据
sessionStorage.clear();

4. **IndexedDB**:
-   **用途**:用于存储大量结构化数据,包括文件和二进制数据。
-   **特点**:
    -   基于对象存储。
    -   支持复杂查询和事务。
    -   数据存储量大,通常为数百MB到数GB。
    -   异步API,操作需要通过回调函数或Promise处理。
  // 打开数据库
let request = indexedDB.open('myDatabase', 1);
// 处理数据库升级
request.onupgradeneeded = function(event) {
    let db = event.target.result;
    let objectStore = db.createObjectStore('users', { keyPath: 'id' });
};

// 处理成功打开数据库
request.onsuccess = function(event) {
    let db = event.target.result;
    let transaction = db.transaction(['users'], 'readwrite');
    let objectStore = transaction.objectStore('users');
    // 添加数据
    let addUserRequest = objectStore.add({ id: 1, username: 'JohnDoe' });
    // 处理添加数据成功
    addUserRequest.onsuccess = function() {
        console.log('User added successfully');
    };
};
 

14. ES6新增特性有哪些

1.  **块级作用域(Block Scoping)** :

    -   `let` 和 `const` 关键字:用于声明块级作用域的变量,取代了函数级作用域的 `var`let x = 10;
    const y = 20;

2.  **箭头函数(Arrow Functions)** :
    -   更简洁的函数表达语法,并且不绑定自己的 `this`const add = (a, b) => a + b;

3.  **模板字符串(Template Strings)** :
    -   使用反引号(``)定义多行字符串和嵌入表达式(`${expression}`)。
    let name = 'John';
    let message = `Hello, ${name}!`;


4.  **解构赋值(Destructuring Assignment)** :
    -   从数组或对象中提取值到变量中。
    let [a, b] = [1, 2];
    let {name, age} = {name: 'John', age: 30};

5.  **默认参数(Default Parameters)** :
    -   函数参数可以有默认值。
    function multiply(a, b = 1) {
        return a * b;
    }

6.  **rest参数(Rest Parameters)** :
    -   将不定数量的参数表示为一个数组。
    function sum(...numbers) {
        return numbers.reduce((acc, curr) => acc + curr, 0);
    }

7.  **展开运算符(Spread Operator)** :
    -   展开数组或对象。
    let arr = [1, 2, 3];
    let newArr = [...arr, 4, 5];

8.  **类(Classes)** :
    -   类的语法糖,基于原型的继承更容易理解和使用。
    class Person {
        constructor(name) {
            this.name = name;
        }
        greet() {
            console.log(`Hello, my name is ${this.name}`);
        }
    }

9.  **模块(Modules)** :
    -   使用 `export` 和 `import` 关键字导出和导入模块。
    // module.js
    export const pi = 3.14;
    export function square(x) {
        return x * x;
    }
    // main.js
    import { pi, square } from './module.js';

10.  **符号(Symbols)** :

    -   一种新的基本类型,用于创建独一无二的标识符。
    let symbol = Symbol('description');

11.  **迭代器(Iterators)和生成器(Generators)** :
    -   自定义迭代行为和生成序列的函数。
    function* generator() {
        yield 1;
        yield 2;
        yield 3;
    }
    let gen = generator();
    console.log(gen.next().value); // 1

12.  **MapSet数据结构**:
    -   新的集合类型。
    let map = new Map();
    map.set('key', 'value');
    let set = new Set();
    set.add(1);

13.  **Promise**:
    -   用于处理异步操作的对象。
    let promise = new Promise((resolve, reject) => {
        // some asynchronous operation
        if (success) {
            resolve(result);
        } else {
            reject(error);
        }
    });
    

15. TS的理解

TypeScript(简称TS)是一种开源编程语言,它是JavaScript的超集,添加了静态类型,可以在开发阶段发现并修复潜在的问题。TypeScript在编译时会被转换成纯JavaScript代码,因此可以在任何支持JavaScript的环境中运行。

### TypeScript的关键特性

1.**静态类型**
-   TypeScript允许你在编写代码时为变量、函数参数和返回值等指定类型。类型可以是基本类型
    (如`number``string``boolean`,`通常是小写`)或复杂类型(如接口、联合类型、元组等)。
    let age: number = 25;
    let name: string = "Alice";    
 
2.  **接口和类型别名**
    -   接口用于定义对象的结构,类型别名可以给类型起一个易记的名字。
    interface Person {
        name: string;
        age: number;
    }

    let person: Person = {
        name: "Bob",
        age: 30
    };

    type ID = string | number;


3.  **类和模块**
    -   TypeScript增强了对面向对象编程的支持,提供了类、接口、继承、访问控制符(public、private、protected)等特性。
    class Animal {
        private name: string;

        constructor(name: string) {
            this.name = name;
        }

        public move(distance: number): void {
            console.log(`${this.name} moved ${distance} meters.`);
        }
    }
    

4.  **泛型**
    -   泛型使得类型可以作为参数进行传递,从而提高代码的复用性和灵活性。
    function identity<T>(arg: T): T {
        return arg;
    }

    let output = identity<string>("Hello");

5.  **枚举**
    -   枚举是一种特殊的类型,用于定义一组命名常量。
    enum Color {
        Red,
        Green,
        Blue
    }

    let c: Color = Color.Green;


6.  **类型推断**
    -   TypeScript具有强大的类型推断能力,可以根据上下文自动推断出变量的类型,减少显式类型注释的需求。
    let message = "Hello, World!"; // TypeScript推断message为string类型


7.  **类型兼容性**
    -   TypeScript采用结构性类型系统,这意味着只要两个类型具有相同的结构,它们就被认为是兼容的。

    interface Point {
        x: number;
        y: number;
    }

    function logPoint(point: Point) {
        console.log(`x: ${point.x}, y: ${point.y}`);
    }

    let point = { x: 10, y: 20 };
    logPoint(point); // 结构匹配,合法
    
TypeScript的编译过程
TypeScript代码需要经过编译(transpile)转化为JavaScript代码,以便在浏览器或Node.js环境中运行。
TypeScript编译器(`tsc`)负责这个过程,可以通过命令行或配置文件(`tsconfig.json`)指定编译选项。
// tsconfig.json 示例
{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "strict": true,
    "outDir": "./dist"
  },
  "include": ["src/**/*"]
}


16. TS的范型是什么(上述15中提到了,我面试时没提到上面的泛型,被追问了)

17. 手写代码题:写一下防抖和节流(节流的三种写法)的代码,再写一下TS的版本

18. call,apply和bind的区别(上面的手写代码题中使用了,被追问了)

19. options请求有了解过吗?(有的面试官可能会这么问:### 5.post请求为什么会多发送一次option请求),小编统一总结一下吧

`POST` 请求前发送的 `OPTIONS` 请求实际上是 HTTP 的一种特性,称为“预检请求”(Preflight request)。
这主要发生在跨域请求(CORS, Cross-Origin Resource Sharing)的场景中,尤其是当请求涉及一些可能
不太安全的方法(如 `PUT``DELETE``POST`)或使用了一些自定义的 HTTP 头部时。

预检请求的目的是检查服务器是否允许来自不同源(域、协议或端口)的请求进行某些操作。这样做可以
确保客户端在发送实际请求之前,先得到服务器的明确许可。

以下是 `OPTIONS` 预检请求的主要特点和原因:

1.  **安全性**:HTTP 协议中的某些方法(如 `GET``HEAD``POST`)被认为是“安全”的,因为它们
不会导致服务器上资源的状态发生变化。但是,其他方法(如 `PUT``DELETE` 等)可能会导致资源的创建、
修改或删除。因此,在发送这些“非安全”请求之前,浏览器会先发送一个 `OPTIONS` 请求来询问服务器
是否允许这样的操作。

2.  **自定义头部**:如果请求中包含了某些自定义的 HTTP 头部,浏览器也会发送 `OPTIONS` 请求来询问服务器是否接受这些头部。

3.  **CORS 配置**:服务器在响应 `OPTIONS` 请求时,可以通过 `Access-Control-Allow-Methods``Access-Control-Allow-Headers` 等头部来告诉浏览器它允许哪些方法和头部。如果服务器的响应中
包含了这些头部,并且允许了客户端想要执行的操作,那么浏览器才会继续发送实际的 `POST` 请求。

总之,`OPTIONS` 预检请求是浏览器和服务器之间的一种协商机制,用于确保跨域请求的安全性和合规性。
当浏览器认为有必要进行这种检查时,它就会在发送实际请求之前先发送一个 `OPTIONS` 请求。

最后就是一些反问环节之类的,谈了一下公司的技术栈等等,超级喜欢该公司的,主要是横向对比了一下最近拿下的几家公司,我感觉面试官很强,他也让我好好准备一下后续的HR面,但是不知道为什么后来没有联系我,已经一周了呜呜呜...,小编还是太菜,还得练...,考完期末考试再去约面试找实习吧,大二下还有好多考试...