前端面试题详解整理112|用户登录防抖,节流,生命周期,useEffect,进程和线程,客户端访问https的整个流程,

75 阅读22分钟

23. 客户端访问https的整个流程

客户端访问 HTTPS 网站的整个流程可以分为以下步骤:

  1. DNS 解析: 客户端首先通过 DNS 解析将域名解析成对应的 IP 地址。

  2. 建立 TCP 连接: 客户端使用解析到的 IP 地址,通过 TCP 协议与服务器建立连接。这是一个三次握手的过程,包括客户端向服务器发送 SYN 报文,服务器回应 SYN+ACK 报文,客户端再回应 ACK 报文,完成连接的建立。

  3. TLS 握手: 客户端发起 TLS 握手过程,其中包括以下步骤:

    • 客户端发送 ClientHello 报文,其中包含支持的加密算法、协商密钥等信息。
    • 服务器收到 ClientHello 报文后,选择一个加密套件,并发送 ServerHello 报文,确认加密套件。
    • 服务器发送服务器证书,证明自己的身份。
    • 如果需要,服务器还可以发送 ServerKeyExchange 报文,包含协商密钥所需的参数。
    • 客户端验证服务器证书的有效性,并生成随机数,用服务器公钥加密后发送给服务器,用于协商对称密钥。
    • 服务器使用自己的私钥解密客户端发送的随机数,并生成对称密钥,用客户端的公钥加密后发送给客户端。
    • 客户端收到服务器发送的加密后的对称密钥,使用预先协商的对称密钥解密后,生成会话密钥。
    • 客户端和服务器根据协商好的加密套件和会话密钥,开始加密通信。
  4. HTTP 请求: 客户端使用建立好的安全连接,向服务器发送 HTTPS 请求,包括请求报文和请求头信息。

  5. 服务器处理请求: 服务器收到客户端发送的 HTTPS 请求后,根据请求信息处理请求,并生成响应报文和响应头信息。

  6. HTTP 响应: 服务器将处理后的响应报文和响应头信息发送给客户端。

  7. 关闭连接: 客户端和服务器之间的通信完成后,通过 TCP 协议完成四次挥手,包括客户端向服务器发送 FIN 报文,服务器回应 ACK 报文,服务器向客户端发送 FIN 报文,客户端回应 ACK 报文,完成连接的关闭。

以上是客户端访问 HTTPS 网站的基本流程。在整个流程中,通过 TLS 握手过程建立安全连接,确保了客户端与服务器之间的通信是加密的、安全的。

3. 进程和线程的区别

进程(Process)和线程(Thread)是操作系统中管理任务和资源的基本单位,它们之间有以下区别:

  1. 定义:

    • 进程是程序的一次执行过程,是系统中资源分配的最小单位,包括代码、数据和进程控制块等。
    • 线程是进程中的一个实体,是 CPU 调度的最小单位,包括程序计数器、寄存器集合和栈等。
  2. 资源拥有:

    • 进程拥有独立的地址空间、内存、文件描述符、设备和上下文等资源。
    • 线程共享所属进程的地址空间和其他资源,但拥有独立的栈和寄存器。
  3. 切换开销:

    • 进程切换的开销较大,需要保存和恢复大量的上下文信息,如 CPU 寄存器、内存映射等。
    • 线程切换的开销较小,因为线程共享了大部分资源,只需要保存和恢复少量的线程私有信息即可。
  4. 并发性:

    • 进程之间是独立并发执行的,相互之间不受影响,通信和同步需要额外的机制。
    • 线程之间共享同一进程的资源,可以更方便地实现通信和同步,但也容易引发竞态条件和死锁等问题。
  5. 生命周期:

    • 进程的生命周期是相对独立的,进程的创建、运行、等待、结束等操作都是由操作系统负责管理的。
    • 线程的生命周期依赖于所属进程,线程的创建、等待、结束等操作通常由线程自己负责,但进程结束时,其所有线程也会被终止。

总的来说,进程是操作系统中资源分配的基本单位,而线程是操作系统中执行调度的基本单位。进程之间相互独立,线程之间共享资源,通过合理的组合使用,可以更有效地利用系统资源,提高系统的并发性和性能。

3. useEffect详细,以及第二个参数,第一个参数是什么函数?什么叫副作用函数?

在 React 中,useEffect 是一个 Hook,用于在函数组件中执行副作用操作。副作用操作通常包括订阅数据、设置定时器、手动修改 DOM 等,这些操作都是在组件渲染过程中无法在 render 函数中完成的。useEffect 可以让我们在函数组件中进行类似于生命周期函数中的操作。

useEffect 接收一个函数作为第一个参数,这个函数被称为副作用函数。当组件渲染时,React 会记住副作用函数,并在组件更新后执行它。副作用函数可能会产生一些副作用,例如发起网络请求、订阅外部数据源、手动修改 DOM 等。

useEffect 还可以接收一个可选的第二个参数,这个参数是一个依赖数组,用于指定副作用函数依赖的变量。当这些变量发生变化时,React 会重新执行副作用函数;如果依赖数组为空,则副作用函数只在组件第一次渲染和销毁时执行。

下面是 useEffect 的基本用法示例:

import React, { useEffect, useState } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // 副作用函数:在组件渲染后更新文档标题
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

在上面的示例中,useEffect 用于在组件渲染后更新文档标题。由于没有指定第二个参数,因此副作用函数会在组件的每次渲染后都执行,这意味着每次点击按钮后都会更新文档标题。

3. vue的生命周期函数?

Vue.js 组件的生命周期函数是一组钩子函数,用于在组件的不同阶段执行代码。这些生命周期函数可以让你在组件的创建、挂载、更新和销毁等阶段执行特定的操作。以下是 Vue.js 2.x 中常见的生命周期函数:

  1. beforeCreate: 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。此时组件实例还未创建,无法访问 datamethods 等属性。

  2. created: 在实例创建完成后被立即调用。在这个阶段,实例已经完成了数据观测、属性和方法的运算,但是 DOM 元素还未生成,不能进行 DOM 操作。

  3. beforeMount: 在挂载开始之前被调用,相关的 render 函数首次被调用。在这个阶段,Vue 已经将模板编译成 render 函数,但是虚拟 DOM 还未生成。

  4. mounted: el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。在这个阶段,实例已经完成了挂载,可以访问到组件渲染后的 DOM 元素,可以进行 DOM 操作。

  5. beforeUpdate: 在数据更新之前调用,发生在虚拟 DOM 重新渲染和打补丁之前。在这个阶段,虚拟 DOM 和 DOM 还未更新,此时可以获取更新前的状态。

  6. updated: 在数据更新之后调用,发生在虚拟 DOM 重新渲染和打补丁之后。在这个阶段,DOM 已经更新完成,可以执行一些操作,如获取更新后的状态或操作 DOM。

  7. beforeDestroy: 在实例销毁之前调用。在这个阶段,实例仍然完全可用,可以执行一些清理操作,如清除定时器、取消订阅等。

  8. destroyed: 在实例销毁后调用。该钩子被调用时,Vue 实例的所有指令和事件监听器都已被解绑,组件也已被销毁,无法再访问组件的属性和方法。

这些生命周期函数允许你在组件的不同阶段执行特定的操作,例如初始化数据、调用接口获取数据、渲染 DOM、清理定时器、取消订阅等。通过合理地使用这些生命周期函数,可以更好地管理组件的生命周期,提高组件的性能和可维护性。

3. 每个生命周期函数可以做什么?

在 Vue.js 组件中,有一系列的生命周期钩子函数,它们允许你在组件的不同阶段执行代码。以下是 Vue.js 2.x 中常见的生命周期函数及其用途:

  1. beforeCreate:

    • 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
    • 通常在此阶段进行一些初始化操作,例如数据的准备工作。
  2. created:

    • 在实例创建完成后被立即调用。
    • 通常在此阶段进行一些异步操作,例如获取数据、设置定时器、发送网络请求等。
  3. beforeMount:

    • 在挂载开始之前被调用:相关的 render 函数首次被调用。
    • 通常在此阶段进行一些操作,例如修改 DOM。
  4. mounted:

    • el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。
    • 通常在此阶段进行一些 DOM 操作,例如调用第三方库、初始化组件等。
  5. beforeUpdate:

    • 在数据更新之前调用,发生在虚拟 DOM 重新渲染和打补丁之前。
    • 通常在此阶段进行一些操作,例如获取更新前的 DOM 状态。
  6. updated:

    • 在数据更新之后调用,发生在虚拟 DOM 重新渲染和打补丁之后。
    • 通常在此阶段进行一些操作,例如对更新后的 DOM 进行操作,或者调用第三方库。
  7. beforeDestroy:

    • 在实例销毁之前调用。在这一步,实例仍然完全可用。
    • 通常在此阶段进行一些清理操作,例如清除定时器、取消订阅等。
  8. destroyed:

    • 在实例销毁后调用。该钩子被调用时,Vue 实例的所有指令和事件监听器都已被解绑。
    • 通常在此阶段进行一些最终的清理工作,例如释放内存、解绑事件等。

以上是 Vue.js 组件生命周期的主要钩子函数及其用途。通过合理地使用这些生命周期函数,你可以在组件的不同阶段执行特定的操作,以满足项目的需求,并管理组件的生命周期。

3. 求两个单链表的公共节点

要找到两个单链表的公共节点,可以采用以下步骤:

  1. 获取链表长度: 遍历两个链表,分别获取它们的长度。
  2. 将长链表指针先移动到与短链表相同长度的位置: 若链表长度不等,将长链表的指针向后移动(长链表长度 - 短链表长度)个节点,使得长链表和短链表从同一起点开始比较。
  3. 同步移动两个链表指针: 从相同位置开始,同时移动两个链表的指针,直到找到第一个相同的节点,或者遍历到链表末尾。
  4. 返回公共节点或空: 如果找到了相同的节点,则返回该节点;如果遍历完两个链表都没有找到相同的节点,则说明两个链表没有公共节点,返回空。

下面是一个简单的 JavaScript 实现:

class ListNode {
    constructor(val, next = null) {
        this.val = val;
        this.next = next;
    }
}

const getIntersectionNode = function(headA, headB) {
    if (!headA || !headB) {
        return null; // 若有一个链表为空,则直接返回 null
    }

    // 获取链表长度
    let lenA = getLength(headA);
    let lenB = getLength(headB);

    // 将长链表指针移动到与短链表相同长度的位置
    let pA = headA;
    let pB = headB;
    if (lenA > lenB) {
        for (let i = 0; i < lenA - lenB; i++) {
            pA = pA.next;
        }
    } else if (lenA < lenB) {
        for (let i = 0; i < lenB - lenA; i++) {
            pB = pB.next;
        }
    }

    // 同步移动两个链表指针,找到第一个相同的节点
    while (pA !== null && pB !== null && pA !== pB) {
        pA = pA.next;
        pB = pB.next;
    }

    return pA; // 返回公共节点或 null

    // 辅助函数:获取链表长度
    function getLength(head) {
        let len = 0;
        let p = head;
        while (p !== null) {
            len++;
            p = p.next;
        }
        return len;
    }
};

这段代码首先计算两个链表的长度,然后将较长的链表指针移动到与较短链表相同长度的位置,最后同步移动两个链表指针,直到找到第一个相同的节点或者遍历到链表末尾。

3. Promise.all和race,以及输入数组为[],或者没有输入的时候,错误处理?

Promise.allPromise.racePromise 提供的两个静态方法,用于处理多个 Promise 实例的并发执行。

  1. Promise.all

    • 接收一个可迭代的对象(通常是数组),里面的每个元素都是一个 Promise 实例。
    • 当所有 Promise 实例都成功完成时,返回一个新的 Promise 实例,其状态为成功,值是所有 Promise 实例成功返回值组成的数组。
    • 如果其中任何一个 Promise 实例失败(reject),则立即返回一个失败状态的 Promise 实例,其原因是第一个失败的 Promise 实例的原因。
  2. Promise.race

    • 接收一个可迭代的对象(通常是数组),里面的每个元素都是一个 Promise 实例。
    • 返回一个新的 Promise 实例,其状态和值取决于第一个完成的 Promise 实例。
    • 如果其中任何一个 Promise 实例成功(resolve)或失败(reject),则立即返回一个与之相同状态的 Promise 实例。

对于错误处理:

  • 当输入数组为空数组 [] 时,Promise.all 会直接返回一个成功状态的 Promise 实例,其值是一个空数组 []
  • 当输入数组没有元素时,Promise.all 也会直接返回一个成功状态的 Promise 实例,其值是一个空数组 []
  • 对于 Promise.race,同样适用以上规则。

需要注意的是,如果输入数组中存在非 Promise 实例的元素,那么它们会被转换成 Promise 实例,并且立即返回成功状态,其值为该元素。如果有任何一个非 Promise 元素转换失败(例如不可转换为 Promise 实例),则 Promise.all 会立即返回一个失败状态的 Promise 实例,其原因是第一个转换失败的元素的原因。

  1. 反问:技术栈,部门hc

面试反馈:我的能力去一个二线互联网没问题,呜呜呜是我不配了

阿里 银泰一面

  1. 自我介绍
  2. 项目的详细介绍
  3. 防抖节流的区别

防抖(Debounce)和节流(Throttle)是两种常用的优化技术,用于减少高频率事件(如滚动、resize、输入等)的触发次数,提高性能和用户体验。它们的区别在于触发的时间处理方式不同:

  1. 防抖(Debounce): 在事件被触发后,等待一段时间(例如 100ms),如果在这段时间内没有再次触发该事件,才执行事件处理函数。如果在等待时间内再次触发了事件,则重新计时。防抖适用于需要等待一段时间后执行最后一次操作的场景,例如输入框输入时的搜索功能,用户在连续输入时不会频繁触发搜索请求,而是在停止输入后才触发搜索请求。

  2. 节流(Throttle): 在事件被触发后,以固定的时间间隔(例如 100ms)执行事件处理函数,不管这段时间内事件触发了多少次。节流适用于需要在一段时间内最多执行一次操作的场景,例如滚动页面时的图片懒加载,可以在用户滚动页面时每隔一段时间检查一次图片是否需要加载,而不是每次滚动都检查。

简而言之,防抖是等待一段时间后执行最后一次操作,节流是每隔一段时间执行一次操作。两者都可以有效减少事件触发的频率,提高性能,但适用于不同的场景。

  1. 聊项目
  2. 反问

阿里 银泰二面 目前还没挂

1. Vue和React的区别?

Vue 和 React 是当前最流行的前端框架之一,它们有一些相似之处,但也有很多不同之处。以下是它们的主要区别:

  1. 设计理念:

    • Vue:Vue 被设计为渐进式框架,可以逐步应用到项目中,甚至只用于单个页面或组件。Vue 的核心思想是通过简单的 API 和响应式数据绑定来构建用户界面。
    • React:React 是一个用于构建大型应用的 JavaScript 库,专注于组件化和虚拟DOM。它提供了一种声明式的方式来描述用户界面,并提供了组件化的开发模式。
  2. 模板语法:

    • Vue:Vue 使用模板语法,类似于 HTML,将数据绑定到 DOM 上。模板中包含指令、插值表达式等,使得编写模板更加直观易懂。
    • React:React 使用 JSX(JavaScript XML)语法,允许在 JavaScript 代码中直接编写类似于 HTML 的标记结构。JSX 提供了更直接的组件描述方式,但需要编译器将 JSX 转换为普通的 JavaScript。
  3. 组件化:

    • Vue:Vue 提供了非常简单的组件化方案,允许将应用拆分为多个可重用的组件。Vue 组件可以包含模板、脚本和样式,并提供了丰富的组件通信和组合方式。
    • React:React 也支持组件化开发,但更强调函数式组件和组件之间的数据流。React 组件通常只包含 render 方法,数据通过 props 和 state 进行传递。
  4. 状态管理:

    • Vue:Vue 提供了 Vuex 这样的状态管理库,用于管理应用中的状态。Vuex 集中管理所有组件的状态,并提供了丰富的工具和 API 来处理状态变化。
    • React:React 通常使用 Context API 和第三方库(如 Redux)来管理应用的状态。Context API 允许跨组件层级传递数据,而 Redux 提供了可预测的状态管理方案。
  5. 生态系统:

    • Vue:Vue 生态系统相对较小,但也有许多官方和第三方的插件、库和工具,如 Vue Router、Vuex、Vue CLI 等。
    • React:React 生态系统更加庞大和成熟,有大量的社区支持和第三方库可供选择,如 React Router、Redux、Next.js 等。

总的来说,Vue 更注重开发体验和灵活性,适合快速开发小型到中型的应用;而 React 更注重性能和扩展性,适合构建大型、复杂的应用和跨平台应用。选择使用哪个框架取决于项目的需求、团队的技术栈和个人偏好。

  1. 在现在的技术背景下,前端更新迭代很迅速,请问作为前端工程师你应该怎么保持核心竞争力?

4. 方案设计:如何实现安全的登录?

实现安全的登录是保障用户账户信息不被盗用和泄露的关键。以下是一些方案设计的建议:

  1. 使用HTTPS协议: 确保登录页面和用户提交的信息在传输过程中经过加密,防止信息被中间人窃取或篡改。

  2. 密码加密存储: 在服务器端存储用户密码时,应采用加密算法对密码进行加密存储,例如使用哈希函数加盐(salted hash)存储用户密码,防止密码泄露后被破解。

  3. 密码策略: 强制用户设置复杂度较高的密码,包括字母、数字和特殊字符,并且密码长度应足够长,以增加密码破解的难度。同时,建议定期要求用户更改密码,避免长期使用相同的密码。

  4. 多因素身份验证(MFA): 引入多因素身份验证机制,除了传统的用户名和密码登录外,还可以要求用户输入手机验证码、邮箱验证码、指纹识别等其他因素,提高账户安全性。

  5. 防止暴力破解: 限制用户登录失败次数,并采取验证码、延迟响应等措施来防止暴力破解登录密码。

  6. 安全的会话管理: 在用户登录成功后,通过安全的会话管理机制来管理用户的登录状态,确保会话标识符的安全性,防止会话劫持和会话固定等攻击。

  7. 安全的密码找回流程: 提供安全的密码找回流程,例如通过邮箱验证或手机验证来重置密码,确保只有合法用户才能重置密码。

  8. 定期安全审计: 定期对登录系统进行安全审计,包括漏洞扫描、安全代码审查等,及时发现和修复潜在的安全风险。

综上所述,实现安全的登录需要综合考虑传输安全、密码存储安全、身份验证安全、会话管理安全等方面,采取多种措施保障用户账户信息的安全性。

3. 方案设计:类似微博,触底加载,每次加载数据的时候,能无重复的加载数据以及对新插入的内容也能进行加载;而且每个人每个人看到的内容都不一样,如何保证

实现类似微博的触底加载,并保证每次加载数据时无重复且能加载新插入的内容,以及确保每个用户看到的内容都不一样,可以考虑以下方案设计:

  1. 分页加载: 将数据按页分割,每次加载一页数据,当用户触底时加载下一页数据。这样可以避免一次性加载过多数据,提高性能。同时,每页数据的大小应适当,不宜过大。

  2. 数据去重: 在加载数据时,通过标记已加载过的数据,避免重复加载。可以通过记录已加载数据的ID或其他唯一标识来实现去重。

  3. 排序策略: 确保新插入的内容也能被加载,可以在数据的存储和加载过程中使用合适的排序策略。例如,可以按时间戳或其他标识对数据进行排序,确保新插入的内容能够被及时加载。

  4. 个性化推荐: 根据用户的偏好和行为,提供个性化的内容推荐。可以根据用户的历史浏览记录、点赞、评论等行为,采用推荐算法为用户推荐感兴趣的内容,从而确保每个用户看到的内容都不一样。

  5. 缓存策略: 使用适当的缓存策略来提高数据加载的效率和用户体验。可以在客户端和服务器端都使用缓存,减少重复请求和加快数据加载速度。

  6. 实时更新: 确保数据的实时更新,及时推送新插入的内容给用户。可以使用WebSocket等实时通信技术,将新数据推送给在线用户,从而保持用户看到的内容的实时性。

综上所述,通过合理的分页加载、数据去重、排序策略、个性化推荐、缓存策略和实时更新等手段,可以实现类似微博的触底加载,并保证每次加载数据时无重复、能加载新插入的内容,同时确保每个用户看到的内容都不一样。

  1. 如何使用两个队列实现一个栈 要使用两个队列实现一个栈,可以采用以下方法:

  2. 使用队列A和队列B两个队列来实现栈的操作。

  3. 入栈操作时,将元素直接放入队列A中。

  4. 出栈操作时,先将队列A中的元素依次出队并入队到队列B中,直到剩下最后一个元素,然后将该元素出队,即完成出栈操作。

  5. 为了保持栈的结构,再将队列B中的元素依次出队并入队到队列A中,即完成了出栈操作。

  6. 这样就实现了用两个队列模拟栈的操作,其中一个队列用于入栈和出栈操作,另一个队列用于辅助出栈操作。

下面是一个使用两个队列实现栈的示例代码(假设队列的实现已经存在):

class Stack {
    constructor() {
        this.queueA = []; // 队列A用于入栈和出栈操作
        this.queueB = []; // 队列B用于辅助出栈操作
    }

    push(element) {
        this.queueA.push(element); // 元素直接入队到队列A
    }

    pop() {
        if (this.isEmpty()) {
            return null; // 栈为空,返回null
        }
        
        // 将队列A中的元素依次出队并入队到队列B中,直到剩下最后一个元素
        while (this.queueA.length > 1) {
            this.queueB.push(this.queueA.shift());
        }
        
        // 出队最后一个元素,即完成出栈操作
        const poppedElement = this.queueA.shift();

        // 将队列B中的元素依次出队并入队到队列A中,保持栈的结构
        while (this.queueB.length > 0) {
            this.queueA.push(this.queueB.shift());
        }
        
        return poppedElement;
    }

    isEmpty() {
        return this.queueA.length === 0;
    }
}

// 测试
const stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
console.log(stack.pop()); // 输出 3
console.log(stack.pop()); // 输出 2
console.log(stack.pop()); // 输出 1
console.log(stack.pop()); // 输出 null(栈为空)

这个示例中,我们通过两个队列模拟了栈的入栈和出栈操作,并保持了栈的特性。

3. 1000人里有一个阳性,如何快速找到?---二分

在有1000个人的情况下,我们可以使用二分法来快速找到这一个阳性人士。二分法是一种查找算法,适用于有序列表或数组中的元素。在这个问题中,我们可以将1000个人的编号从1到1000按顺序排列,然后开始二分查找。

具体步骤如下:

  1. 初始化搜索范围为1到1000。

  2. 将搜索范围的中间编号与阳性人士的编号进行比较。

  3. 如果中间编号小于阳性人士的编号,则更新搜索范围为中间编号到1000。

  4. 如果中间编号大于阳性人士的编号,则更新搜索范围为1到中间编号。

  5. 不断重复步骤2到4,直到找到阳性人士的编号。

下面是一个使用 JavaScript 实现二分查找的示例代码:

function findPositivePerson() {
    let left = 1;
    let right = 1000;

    while (left < right) {
        let mid = Math.floor((left + right) / 2);
        
        // 这里假设阳性人士的编号为500
        if (mid < 500) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }

    return left; // 找到阳性人士的编号
}

console.log("阳性人士的编号是:" + findPositivePerson());

在这个示例中,假设阳性人士的编号为500,通过二分查找,我们可以快速找到这个阳性人士的编号为500。二分查找的时间复杂度为O(log n),因此可以在很短的时间内找到目标。

  1. 最近在学什么新技术吗?

  2. chatgpt对前端有什么冲击?他不能代替前端,怎么说?哪些地方不如前端工程师?

  3. 反问环节

作者:钱多多哦耶
链接:www.nowcoder.com/discuss/541…
来源:牛客网