前端面试-经典八股

date
Aug 10, 2023
slug
frontend_interview_stale
status
Published
tags
前端
interview
summary
经典八股问题
type
Post

重要的小题目

JS数据类型(八股)
array,string,number,function,object,bool
怎么判断一个数组(八股)
Array.isArray, instance of === Array
==和===区别(八股)
== 判断内存中是不是指向同一个对象,有隐形转换,=== 检查值是否相等没有隐形转换
对于 string、number 等基础类型,== 和 === 是有区别的
不同类型间比较,== 之比较 "转化成同一类型后的值" 看 "值" 是否相等,=== 如果类型不同,其结果就是不等。
对于 Array,Object 等高级类型,== 和 === 是没有区别的,进行 "指针地址" 比较
基础类型与高级类型,== 和 === 是有区别的
  • a)对于 ==,将高级转化为基础类型,进行 "值" 比较
  • b)因为类型不同,=== 结果为 false

定义变量方法(八股)let const var区别(八股)
作用域:let和const有块级作用域,var不存在块级作用域,可以跨块访问,不能跨函数访问
变量提升:var可以,let和const不行
全局:var声明变量是全局变量,挂在window下面,let和const不会
重复声明:var可以 值为最后一次,let和const不行
const:设置初始值

箭头函数和普通函数的区别
  1. 语法更简单
  1. 箭头函数不会创建自己的this,主会从作用域链上一层继承。它会捕获自己在定义时(注意,是定义时,不是调用时)所处的外层执行环境的this,并继承这个this值。
  1. 箭头函数继承而来的this指向永远不变 (上面的例子)
  1. call()/.apply()/.bind()无法改变箭头函数中this的指向
  1. 箭头函数不能作为构造函数使用
  1. 箭头函数没有自己的arguments
7、箭头函数没有原型prototype
 
快速复制一个数组你会用什么方式?
  1. 展开符浅拷贝, Array.from
  1. JSON.parse, JSON.stringify 深
  1. deepclone
解释下深拷贝和浅拷贝(果然问到了,八股)
  1. 浅拷贝和深拷贝都复制了值和地址,都是为了解决引用类型赋值后互相影响的问题。
  1. 但是浅拷贝只进行一层复制,深层次的引用类型还是共享内存地址,原对象和拷贝对象还是会互相影响。
  1. 深拷贝就是无限层级拷贝,深拷贝后的原对象不会和拷贝对象互相影响。

const是定义常量,那可以修改吗?
对于基本类型,不可修改,对于引用类型中的数组对象都是可以修改的
Map和Object区别
  1. Map可以按照键插入的顺序去遍历
  1. Map的key可以是任何值 但是Object的key只能是string
  1. Map有一些自带的方法比如myMap.has(key), myMap.size()

怎么对原型进行修改(比如Array.prototype修改)
func.prototype = ...
Object.setPrototypeOf()
前端的跨域解决方案(跨域+jsonp)为什么会有跨域问题?(同源策略)
协议+域名+端口
1、 通过jsonp跨域。 只能get
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域
  • 现场写代码:水平垂直效果(flex实现)
  • 现场写代码:实现悬浮框效果在右下角(绝对定位实现)
  • 现场写代码:任意写一种排序(写的选择排序)

JS单线程

话术一:
从这个过程中我们看到,在进入一个作用域并开始运行js代码时,js引擎从上至下按顺序执行代码。遇到同步代码直接执行;遇到异步代码,则会在作用域内所有同步代码执行完毕后(也就是js引擎空闲时),将其推入事件队列,再按顺序执行异步事件。
话术二:
js引擎遇到一个异步事件后并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。如果有,那么主线程会从中取出排在第一位的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码...,如此反复,这样就形成了一个无限的循环。这就是这个过程被称为“事件循环(Event Loop)”的原因。
我们只需记住当当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。
promise/await是微任务会先执行,setTimeout/setInterval是宏任务后执行
js有多线程的能力:Web Worker
Promise.all : 虽然promise.all是并行的,但实际是串行的,promise.all的语义是等所有事件都执行完。

new operator

  • 以构造器的prototype属性为原型,创建新对象;
  • 将this(也就是上一句中的新对象)和调用参数传给构造器,执行;
  • 如果构造器没有手动返回对象,则返回第一步创建的新对象,如果有,则舍弃掉第一步创建的新对象,返回手动return的对象。

ES6 module vs commonjs module

In the browser JavaScript ecosystem, the use of JavaScript modules depends on the import and export statements; these statements load and export EMCAScript modules (or ES modules), respectively.
The ES module format is the official standard format to package JavaScript code for reuse and most modern web browsers natively support the modules.
Node.js, however, supports the CommonJS module format by default. CommonJS modules load using require(), and variables and functions export from a CommonJS module with module.exports.

模块联邦

HTTP

http请求码

1**
信息,服务器收到请求,需要请求者继续执行操作
100 Continue
继续。客户端应继续其请求
101 Switching Protocols
切换协议,只能切换到更高的协议
2**
成功,操作成功接受并处理
200 OK
请求成功。一般用于GET与POST
201 Created
已创建。成功请求并创建了新的资源
202 Accepted
已接受。已经接受请求,但未处理完成
203 Non-Authoritative Information
非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本。
204 No Content
无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档。
205 Reset Content
重置内容。服务器处理成功,用户终端应重置文档视图。可通过此返回码清除浏览器的表单域
206 Partial Content
部分内容。服务器成功处理了部分GET请求
3**
重定向,需要进一步的操作已完成请求
300 multiple choices
多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端选择
301 Moved Permanently
永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替。
302 Found
临时移动。资源临时移动,客户端应继续使用原有URI
303 See Other
查看其他地址。与301类似,使用GET和POST请求查看
304 Not Modified
未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期修改的资源。
305 Use Proxy
使用代理。所请求的资源必须通过代理访问
306 Unused
已经被废弃的HTTP状态码
307 Temporary Redirect
临时重定向。与302相似。使用GET请求重定向
4**
客户端错误,请求包含语法错误或无法完成请求
400 Bad Request
客户端请求的语法错误,服务器无法理解
401 Unauthorized
请求要求用户的身份认证
402 payment required
保留 将来使用
403 Forbidden
服务器理解客户端请求,但是拒绝执行
404 Not Found
服务器无法根据客户端的请求找到资源(网页)
405 Method Not Allowed
客户端请求中的方法被禁止
406 Not Acceptable
服务器无法根据客户端请求的内容特性完成请求
407 Proxy Authentication Required
要使用代理授权
408 Request Timeout
服务器等待客户端发送的请求时间过长
409 Conflict
服务器完成客户端PUT请求时可能返回此代码,服务器处理请求时发生了冲突
太多了 直接看 →
5**
服务器错误,服务器再处理请求的过程中发生错误
500 Internal Server Error
服务器内部错误,无法完成请求
501 Not Implemented
服务器不支持请求的功能,无法完成请求
502 Bad Gateway
作为网关或代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效响应
503 Service Unavailable
服务器超载或系统维护
504 Gateway timeout
充当网关或代理的服务器未及时从远端服务器获取请求
505 Http version not supported
不支持请求的http协议版本

缓存

http缓存可以降低服务器压力,节省流量,优化用户体验。

私有缓存

缓存被存储在独立的账户体系下。

共有缓存

在代理服务器或者其他中间服务器

Cache-Control

HTTP中,控制缓存两种字段:Pragma; 另一个是Cache-Control
Pragma 是HTTP 1.0中的字段,逐渐被HTTP 1.1中引入的Cache-Control取代
响应头中的Cache-Control一般有如下取值:
public: 可以被任何对象(客户端,代理服务器等)缓存,即使是通常不可缓存的内容(例如1. 该响应没有max-age或expire消息头 2. 该响应对应请求方法是POST)
private: 响应只能被单个用户缓存,不能作为共享缓存(代理服务器不能缓存他)。私有缓存可以缓存内容比如:对应用户的本地浏览器
no-cache:在发布缓存副本之前,强制要求缓存把请求提交给原始服务器进行验证(协商缓存验证)。
no-store:缓存不应存有关客户端请求或服务器响应的任何内容,即不适用任何缓存。
no-transform:不得对资源进行转变或转换,Content-Encoding、Content-Range、Content-Type等HTTP头不能由代理修改,例如,非透明代理或者如Google's Light Mode可能对图像格式进行转换,以便节省缓存空间或者减少缓慢链路上的流量。no-transform指令不允许这样做。
must-revalidate:一旦资源过期比如超过max-age,在成功想原始服务器验证之前,缓存不能用该资源响应后续请求。
proxy-revalidate:跟must-revalidate一样,仅适用于公共缓存,被私有缓存忽略。
max-age = : 设置缓存存储的最大周期,超过这个时间缓存被认为过期。与Expires相反,时间是相对于请求的时间。
s-maxage =:覆盖max-age或expires头,但仅适用于共享缓存,私有缓存会忽略。
请求头中的Cache-Control 一般有如下取值:
max-age=;
max-stale[=]:表明客户端愿意接收一个已经过期的资源,设置一个可选的秒数,表明不能超时查过给定的时间。
min-fresh=:表示客户端想获取一个能在指定的秒数内保持其最新状态的响应。
no-cache, no-store, no-transform,
only-if-cached:客户端只接收已缓存的响应,并且不要向原始服务器检查是否有更新的拷贝。

跨tab通信

同源页面通信:
  • LocalStorage
非同源:
  • 不可见的iframe作为桥,指定origin忽略同源限制

三次握手,四次挥手

三次握手

notion image
第一次: 客户端发送初始序号x和syn=1请求标志
第二次:服务端发送请求标志syn,发送确认标志ACK,发送自己的序号seq=y,发送客户端的确认序号ack=x+1
第三次:客户端发送ACK确认号,发送自己的序号seq=x+1,发送对方的确认号ack=y+1

四次挥手

notion image
第一次:客户端发出释放FIN=1,自己的序列号seq=u,进入FIN-WAIT-1状态
第二次:服务器收到客户端的后,发出ACK=1确认标志和客户端的确认号ack=u+1,自己的序列号seq=v,进入CLOSE-WAIT状态
第三次:客户端收到服务器确认结果后进入FIN-WAIT-2状态。此时服务器发送释放FIN=1信号,确认标志ACK=1,确认序号ack=u+1,自己序号seq=w,服务器进入LAST-ACK
第四次:客户端收到回复后,发送确认ACK=1,ack=w+1,自己的seq=u+1,客户端进入TIME-WAIT。客户端经过2个最长报文段寿命后,客户端CLOSE;服务器收到确认后,立刻进入CLOSE状态。
(1)为什么连接是三次握手,关闭是四次?
答:四次是因为客户端发送FIN请求释放后,服务器端还可能继续发送数据,所以第一个是先回复客户端的FIN,等发送完所有数据服务器端再发送FIN。
而三次握手是因为客户端发送连接请求后,客户端可以直接发送SYN+ACK报文,而四次挥手是只是先发送ACK报文回应客户端的FIN报文已被接收。
(2)为啥四次挥手要等待2MSL?
答:防止客户端最后一次发送给服务器的确认ACK在网络中丢失,以至于客户端关闭了而服务端未关闭(如果服务端没有收到ACK则会不断发送FIN报文,即客户端不能立马关闭)。
2MSL即一个发送和一个回复的最大时间。
(3)为啥不用两次握手?四次握手?
答:TCP是可靠传输的,面向字节即对每个字节的数据分配一个序号。
1)因为如果两次握手,只有服务器对客户端的起始序列号做出确认,但客户端却没有对服务器的起始序列号做确认,不能保证TCP运输可靠性;
2)而四次握手没必要(第二三步可以合并,提高连接的速度和效率)。
(4)出现很多close_wait咋办?
答:close_wait状态是被动关闭的一方在四次挥手中的第二个步骤,该状态即wait to close,即等待应用程序,主动关闭端关闭socket。

从输入url到页面出来都发生了啥

  1. 用户输入url并回车
  1. 浏览器进程检查url,组装协议,构成完整url
  1. 浏览器进程通过进程间通信把url请求发送给网络进程
  1. 网络进程接受到url请求后检查本地缓存是否缓存了改请求资源,如果有则将该资源返回给浏览器进程
  1. 如果没有,网络进程向web服务器发起http请求(网络请求),请求流程如下
    1. 进行dns解析,获取服务器ip地址
    2. 利用ip地址和服务器建立tcp连接
    3. 构建请求头
    4. 发送请求头
    5. 服务器响应后,网络进程接收响应头和响应信息,并解析响应内容
  1. 网络进程解析响应流程
    1. 检查状态码,如果是301/302,则需要重定向,从Location中自动读取地址,重新进行第4步,如果是200,则继续处理请求
    2. 200响应处理:检查响应类型Content-type,如果是字节流类型,则将该请求提交给下载管理器,该导航流程结束,不再进行后续渲染,如果是html则通知浏览器进程准备渲染进程准备进行渲染。
  1. 准备渲染进程
    1. 浏览器进程检查当前url是否和之前打开的渲染进程根域名是否相同,如果相同,则复用原来的进程,如果不同,则开启新的渲染进程。
  1. 传输数据、更新状态
    1. 渲染进程准备好后,浏览器向渲染进程发起“提交文档”的信息,渲染进程接收到消息和网络进程建立传输数据的“管道”
    2. 渲染进程接收完数据后,向浏览器发送“确认提交”
    3. 浏览器进程接收到确认消息后更新浏览器界面状态:安全、地址栏url、前进后退的历史状态、更新web页面

渲染流程

  1. 将HTML转换为DOM树结构,才能被浏览器理解
  1. 根据CSS计算出DOM树所有节点的样式,遵循CSS层叠和继承的原则
  1. 计算DOM元素的布局信息
  1. 对布局树进行分层,生成分层树
  1. 对每个图层生成绘制列表,并将其提交到合成线程
  1. 合成线程将图层分成图块,并在光栅化线程中将图块转换成位图
  1. 合成线程发送绘制图块命令DrawQuad给浏览器进程
  1. 浏览器进程根据DrawQuad消息生成页面,并显示到显示器上

前端框架比较

Nextjs

  • SSR: 渲染更快
  • turbopack:ISR

Angular

  • 模块化,代码复用性
  • MVVM 视图跟数据模型绑定,双向绑定
  • 规范化可维护性强

Vue

  • 上手简单
  • 用的人多
  • 大型项目可维护性差点儿

SSR(server side rendering) vs CSR(client side rendering)

CSR是浏览器渲染,SSR是服务器渲染

SSR

  1. 服务器收到http请求
  1. 服务器来处理所有事情包括HTML大部分代码
  1. 浏览器收到easily consumed的HTML文件
好处是页面加载更快,SEO友好
坏处是交互性差,用户体验不好,去到新页面要重新渲染整个页面。
解决方案: 为了良好的交互性,我们需要把js发到前端,nextjs允许我们先把首屏需要的HTML发到浏览器,然后再把js发过去,这个过程叫代码分离和脱水(hydration)。这项技术非常复杂且具有技术挑战性所以是一个“缺点”。

CSR

  1. 服务器收到http请求
  1. 服务器发送HTML空壳和js代码到浏览器
  1. 浏览器运行js代码去修改DOM来生成最终的HTML
简单的判断方法是如果page source是完整的html代码的话,那就是SSR。
如果只是一个空壳,相当于是在js里操作dom,那就是CSR。
好处是用户操作体验好
坏处是首屏渲染慢,SEO不能很好去index。

cookie vs localstorage vs session storage

Local storage
Session storage
Cookies
5MB/10MB
5MB
4KB
手动删除
session-based,tab关掉就清除了
based on setting, 每个tab都里的cookie
只客户端读
只客户端读
客户端和服务端都可以读写
没有数据传到server
没有数据传到server
有数据传到server

高流量/大并发

  • 避免高频刷新增大服务器压力 比如5s内只能刷新一次
  • 本质上前端能做的就是减少http请求数量/频率
qps~ 50: 摆
qps~100:数据库缓存(http请求缓存),数据库负载均衡
qps~800:cdn加速,负载均衡
qps~1000: 静态HTML缓存
qps~2000:业务分离,分布式存储

硬件升级

更换CPU,内存,硬盘,网卡等

缓存

CDN加速
内容分发网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。
请求会先走到cdn如果没有缓存,那么会走后端的nginx缓存,nginx没有再到应用服务器,cdn主要缓存静态资源。
HTTP缓存
通过Cache-Control请求头实现。
磁盘缓存: 读取效率较内存低,但是重启后数据不会丢失,代码组件有Guava,Ehcache
服务器有Redis,MemCache
多级缓存:浏览器→ cdn→Nginx→Redis→DB(磁盘文件系统)
一些访问量大,频率较低的数据,可直接定时生成静态HTML页面,供前端访问,

Nginx负载均衡

基于URL等应用信息的负载均衡。

正向代理

代理的是客户端发请求的一方,就是科学上网

反向代理

代理的是服务端处理请求的一方

负载均衡常用算法

轮询:适用于性能相近的服务器集群
加权轮询:请求按权值分配,资源分配更优化。
ip hash:依据发出请求的客户端ip的hash来分配服务器,一定程度上解决了集群部署环境下Session不共享的问题。
Session 不共享问题是说,假设用户已经登录过,此时发出的请求被分配到了 A 服务器,但 A 服务器突然宕机,用户的请求则会被转发到 B 服务器。但由于 Session 不共享,B 无法直接读取用户的登录信息来继续执行其他操作。

网站重构 技术选型 性能优化

重构的原因

技术因素:
  • 新老技术框架更替
  • 安全,性能,代码维护性
产品因素:
  • 业务调整
在重构之前一定弄清
  • 目前的痛点
  • 充分理解业务场景,明确重构的目标
  • 找一些经验丰富的对业务理解通透的师傅参与技术方案设计

方法论

实现上来讲:可读性,维护性,鲁棒
  1. 按照业务逻辑对页面模块进行合理的拆分成页面模块
  1. 公共js库,业务公共逻辑,公共UI组件,状态管理数据模型
  1. 构建工具webpack, CI流程
  1. 其他细节:
  • 注释
  • 业务逻辑方法精简抽象
  • typescript
  • eslint
  • 变量名,文件命名规范
  • 单侧,测试用例
  1. 上线前还要考虑切流灰度方案

性能优化

打包速度慢

  • speed-measure-webpack-plugin,分析webpack总打包耗时以及每个plugin和loader的打包耗时。
  • html-webpack-externals-plugin, 公共库,静态资源放到cdn上
  • terser-webpack-plugin,css-minizer-webpack-plugin,html-minimizer-webpack-plugin
  • 包体积分析: webpack-bundle-analyzer

渲染层优化

  1. 首屏加载速度:cdn,懒加载
  1. 白屏体验优化:骨架屏,加载动画
  1. 大数据列表渲染优化:virtual-scroll-list
  1. api请求优化:cache-control缓存
  1. 动画性能优化:硬件加速,复杂用svg或canvas
  1. dom过载导致页面卡顿

浏览器插件

插件基于web技术构建的,按照规定的脚手架文件格式去编写。
获取渠道:chrome官方插件商店获取安装。也可以从第三方下载但是会被限制,可以用开发者模式安装。
background:所谓的后台,常驻页面,生命周期很长,通常把全局代码,需要启动就运行的代码放在里面。可以通过page指定一张网页或者scripts指定js文件数组,两者只能配一个。
后动态可以用来监听一些浏览器事件:插件程序首次安装或者版本更新,后台页面正在监听事件并且调度,内容脚本或其他插件发送消息,插件中的另一个视图调用runtime.getBackgroundPage
storage:插件安装时设定,提供和localstorage相似的存储功能,但能跟随chrome账号同步。可直接存储对象而不是字符串,插件内容脚本直接访问
tabs:chorme.tabs用于跟浏览器标签页进行交互
alarms: chrome提供的插件api用来制造alert弹窗
content- script:网页上下文中运行的脚本,可以对网页dom进行修改。 分为代码方式注入和生命方式注入。用onMessage和sendMessage跟插件通信。
contextMenus: 浏览器右键菜单
Devtools:可以为chrome的devtools面板新增tab与检查的页面交互,跟插件一样有背景页面,内容脚本和其他项目。可以定制devPanel和sidebar两部分。
notifications:右边弹窗的提示
webRequest: 对浏览器发出的任何http请求进行拦截,
组件消息通信:
  • background跟popup,都有字段可以直接获取到对象
  • background跟内容脚本通过消息通信。
 

© bai xin 2021 - 2024

沪ICP备20011311号-1