面试血泪史
不要问我面的是不是架构师。我只是面的低级前端。总结不易点个赞吧。
JS
1. 包装类跟普通类有什么区别?new String() 和String()的区别
答::js 提供了3个包装类 分别是 new String()
,new Number()
,new Boolean()
。由于基础类型不能添加属性和方法,js的包装类的作用是将基础类型包装成一个对象,这样就可以有属性和方法。
tips:当我们对一些基本数据类型的值去调用属性和方法时,浏览器会临时使用包装类将其转换为对象
,然后在调用对象的属性和方法;调用完以后,在将其转换为基本数据类型。
2. promise.then 是怎么实现链式调用的
答: 通过重新return 一个new Promise 来实现链式调用
3. 多调用几次bind 比如bind a bind b 最后this指向谁 为啥
答: 永远指向第一次调用bind时传入的上下文,因为bind之后的调用都是绑定在这个上下文上。
4. v8引擎回收机制简述
答: v8垃圾回收主要通过两个策略:
-
标记清除
-
引用计数
标记清除是js最常用的垃圾回收机制。垃圾回收程序运行的时候,会标记内存中存储的所有变量。然后,它会将所有在上下文中的变量,以及被在上下文中的变量引用的变量的标记去掉。在此之后再被加上标记的变量就是待删除的了,原因是任何在上下文中的变量都访问不到它们了。随后垃圾回收程序做一次内存清理,销毁带标记的所有值并收回它们的内存。
引用计数是对每个值都记录它被引用的次数。声明变量并给它赋一个引用值时,这个值的引用数为 1。如果同一个值又被赋给另一个变量,那么引用数加 1。类似地,如果保存对该值引用的变量被其他值给覆盖了,那么引用数减 1。当一个值的引用数为 0 时,就说明没办法再访问到这个值了,因此可以安全地收回其内存了。垃圾回收程序下次运行的时候就会释放引用数为 0 的值的内存。(以上摘自js红宝书第四版)
5. v8回收算法运行时会阻塞js吗?为什么
答: 会阻塞。
6. 怎么优化垃圾回收机制
答: www.cnblogs.com/chengxs/p/1… 总结就是多用新生代算法
7. 作用域链简述。怎么能获取函数内部变量?
答: 作用域链就是变量向上查找过程。可以通过函数内部return 一个携带函数内部变量的闭包使得外部可以访问函数内部的变量
8. 闭包简述,怎么避免内存泄漏
答: 无论何时声明新函数并将其赋值给变量,都要存储函数定义和闭包,闭包包含在函数创建时作用域中的所有变量,类似于背包,函数定义附带一个小背包,他的包中存储了函数创建时作用域中的所有变量。及时将指针指向null可以避免内存泄漏。
9. class 类可以枚举吗?类 instanceof Function 输出什么?
答: 类的内部所有定义的方法,都是不可枚举的
。类的数据类型就是函数,类本身就指向构造函数
。 代码如下:
class Point {
constructor(x, y) {
// ...
}
toString() {
// ...
}
}
Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
class Fn{}
Fn instanceof Function // true
const a = new Fn()
a instanceof Function // false
webpack
1. webpack 是怎么实现分模块打包的?
答: 可以通过splitChunks
实现。
webpack 中以下三种常见的代码分割方式:
- 入口起点:使用 entry 配置手动地分离代码。
- 动态导入:通过模块的内联函数调用来分离代码。
- 防止重复:使用 splitChunks 去重和分离 chunk。 第一种方式,很简单,只需要在 entry 里配置多个入口即可。
splitChunks 代码拆分
splitChunks: {
// 表示选择哪些 chunks 进行分割,可选值有:async,initial和all
chunks: "async",
// 表示新分离出的chunk必须大于等于minSize,默认为30000,约30kb。
minSize: 30000,
// 表示一个模块至少应被minChunks个chunk所包含才能分割。默认为1。
minChunks: 1,
// 表示按需加载文件时,并行请求的最大数目。默认为5。
maxAsyncRequests: 5,
// 表示加载入口文件时,并行请求的最大数目。默认为3。
maxInitialRequests: 3,
// 表示拆分出的chunk的名称连接符。默认为~。如chunk~vendors.js
automaticNameDelimiter: '~',
// 设置chunk的文件名。默认为true。当为true时,splitChunks基于chunk和cacheGroups的key自动命名。
name: true,
// cacheGroups 下可以可以配置多个组,每个组根据test设置条件,符合test条件的模块,就分配到该组。
// 模块可以被多个组引用,但最终会根据priority来决定打包到哪个组中。默认将所有来自
// node_modules目录的模块打包至vendors组,将两个以上的chunk所共享的模块打包至default组。
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10 // 缓存组优先级
},
//
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true // 可设置是否重用该chunk
}
}
}
通过 cacheGroups,我们可以定义自定义 chunk 组,通过 test 条件对模块进行过滤,符合条件的模块分配到相同的组。
2. webpack4 的tree-shaking是什么?怎么实现的?在什么情况下会失效?为什么?
答: tree-shaking本质是webpack打包时用来舍弃无用的代码。
工作原理: 在ES6以前,我们可以使用CommonJS引入模块:require()
,这种引入是动态的,也意味着我们可以基于条件来导入需要的代码
let module
if(true){
module = require('a')
}else{
module = require('b')
}
CommonJS
规范无法确定在实际运行前需要或者不需要某些模块,所以CommonJS不适合tree-shaking机制
ES6的import语法可以完美使用tree shaking,因为可以在代码不运行的情况下就能分析出不需要的代码。
因为tree shaking只能在静态modules下工作。ECMAScript 6 模块加载是静态的,因此整个依赖树可以被静态地推导出解析语法树。
side effects
是指那些当import的时候会执行一些动作,但是不一定会有任何export
tree shaking
不能自动的识别哪些代码属于side effects
,因此手动指定这些代码显得非常重要。如果所有代码都不包含副作用,我们就可以简单地将该属性标记为false,来告知 webpack,它可以安全地删除未用到的export导出。
总结: ES6 Module引入进行静态分析,故而编译的时候正确判断到底加载了那些模块。再判断那些模块和变量未被使用或者引用,进而删除对应代码。
另外,webpack中可以在项目package.json文件中,添加一个 “sideEffects” 属性,手动指定由副作用的脚本。
3. env 知道吗?是用来干什么的?项目需要单独安装吗?为什么?
env是nodejs里内置的一个对象,可以利用process.env拿到当前项目运行环境的信息。不需要独立安装,因为是nodejs的内置对象。
4. import 和 require 的区别
答:
-
CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
-
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
-
CommonJs 是单个值导出,ES6 Module可以导出多个
-
CommonJs 是动态语法可以写在判断里,ES6 Module 静态语法只能写在顶层
-
CommonJs 的 this 是当前模块,ES6 Module的 this 是 undefined
5. 知道什么是静态分析吗?
答: es modules可以在代码不运行的情况下对代码进行分析,可以知道哪些模块有没有被使用。
6. webpack babel是如何工作的?
答:
-
词法解析
。将字符串形式的代码转换为Tokens(令牌),Tokens 可以视作是一些语法片段组成的数组。
-
语法解析
。把Tokens转换为抽象语法树AST
-
转换阶段
。会对 AST 进行遍历,在这个过程中对节点进行增删查改。Babel 所有插件都是在这个阶段工作, 比如语法转换、代码压缩。
-
输出阶段
。将经过转换的AST通过babel-generator再转换成js代码,过程就是深度优先遍历整个AST,然后构建可以表示转换后代码的字符串。同时这个阶段还会生成Source Map
。
7. webpack plugins的执行时机?
答: 加载文件完成后,输出文件前,不同的plugins有不同的执行时机。
node
1. koa 源码了解过,是怎么实现的?
答: koa通过对http模块的封装,在内部实现了一个context上下文的概念,把res跟req都放在ctx上面,并且对req和res进行优雅的setter/getter处理,调用方式更简单。
洋葱模型通过将中间件数组里的异步方法通过dispatch去递归调用,由于在app.use中去调用next方法时去调用下一个中间件。
洋葱模型实现伪代码
function compose(middlewares){
return function(){
return dispatch(0)
function dispatch(i){
let fn = middlewares[i]
if(!fn) return Promise.resolve()
return Promise.resolve(fn(function next(){
// promise 完成之后在执行下一个
return dispatch(i+1)
}))
}
}
}
2. koa 洋葱模型
答: 见上
3. cdn加速是怎么做的?
答: 简单的说就是缓存+负载均衡
- 浏览器向域名解析服务器发出解析请求,由于CDN 对域名解析过程进行了调整,所以用户端一般得到的是该域名对应的 CNAME 记录,此时浏览器需要再次对获得的 CNAME 域名进行解析才能得到缓存服务器实际的IP 地址。 注:在此过程中,全局负载均衡DNS 解析服务器会根据用户端的源IP 地址,如地理位置(北京还是上海)、接入网类型(电信还是网通)将用户的访问请求定位到离用户路由最短、位置最近、负载最轻的Cache 节点(缓存服务器)上,实现就近定位。定位优先原则可按位置、可按路由、也可按负载等。
- 再次解析后浏览器得到该域名CDN 缓存服务器的实际IP 地址,向缓存服务器发出访问请求。
- 缓存服务器根据浏览器提供的域名,通过Cache 内部专用DNS 解析得到此域名源服务器的真实IP 地址,再由缓存服务器向此真实IP 地址提交访问请求。
- 缓存服务器从真实IP 地址得到内容后,一方面在本地进行保存,以备以后使用,同时把得到的数据发送到客户端浏览器,完成访问的响应过程。
4. cdn源服务器文件修改,负载均衡还有作用吗?
答: cdn一般用来存静态资源。拿网站来说,当用户访问网站时静态资源从cdn加载。cdn向源服务器请求资源并缓存,这个请求过程是周期性的,自动的,称为回源
。 当你更新了一个文件,现在正巧还没到cdn自动更新的时候,如果想让用户马上看到新的就得手动刷cdn,一般cdn控制台都有此选项。
5. 负载均衡有哪些模式
轮询(默认)
每个请求按时间顺序逐一分配
到不同的后端服务器,如果后端服务器down掉,能自动剔除。weight权重
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。权重越高,在被访问的概率越大。ip_hash
如果客户已经访问了某个服务器,当用户再次访问时,会将该请求通过哈希算法,自动定位到该服务器。fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。url_hash(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。
6. 脱离了nginx怎么配置负载均衡
答: Redis,Zookeeper (ps:nt问题)
7. a服务器node服务怎么访问b服务器的脚本
答: (大佬跟我说的)用RPC
RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
8. 经过网关转发下websocket还可以一直保持心跳吗
答: 不能
9. 前端页面设置强缓存,但是有东西更新了。怎么保证用户的页面是最新的
答:
- 静态资源设置协商缓存。
- 在静态资源后面配置版本号,时间戳等。
- (希望大家可以帮忙提供一下更好的方案)
10. node 事件循环
答:
node中的微观,宏观任务
- 常见的 macro-task 比如:setTimeout、setInterval、 setImmediate、script(整体代码)、 I/O 操作等。
- 常见的 micro-task 比如: process.nextTick、new Promise().then(回调)等。
microtask 在事件循环的各个阶段之间执行。
- timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调
- I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调
- idle, prepare 阶段:仅node内部使用
- poll 阶段:获取新的I/O事件, 适当的条件下node将阻塞在这里
- check 阶段:执行 setImmediate() 的回调
- close callbacks 阶段:执行 socket 的 close 事件回调
- timers阶段
timers 阶段会执行 setTimeout 和 setInterval 回调,并且是由 poll 阶段控制的。 同样,在 Node 中定时器指定的时间也不是准确时间,只能是尽快执行。
-
callbacks阶段 此阶段执行某些系统操作的回调,例如 TCP 错误。
-
轮询 poll 阶段
- 计算应该阻塞并 I/O 轮询的时间
- 处理轮询队列 (poll queue) 中的事件
当事件循环进入轮询 (poll) 阶段并且没有任何计时器调度 (timers scheduled) 时,将发生以下两种情况之一:
-
如果轮询队列 (poll queue) 不为空,则事件循环将遍历其回调队列,使其同步执行,直到队列用尽或达到与系统相关的硬限制为止
-
如果轮询队列为空:如果已通过 setImmediate 调度了脚本,则事件循环将结束轮询 poll 阶段,并继续执行 check 阶段以执行那些调度的脚本。如果脚本并没有 setImmediate 设置回调,则事件循环将等待 poll 队列中的回调,然后立即执行它们。
- 检查阶段 check
此阶段允许在轮询 poll 阶段完成后立即执行回调。 如果轮询 poll 阶段处于空闲,并且脚本已使用 setImmediate 进入 check 队列,则事件循环可能会进入 check 阶段,而不是在 poll 阶段等待。
- close callbacks 阶段
setImmediate vs setTimeout
setImmediate
和 setTimeout
相似,但是根据调用时间的不同,它们的行为也不同
setImmediate
设计为在当前轮询 poll 阶段完成后执行脚本。setTimeout
计划在以毫秒为单位的最小阈值过去之后运行脚本。
11. node在require的时候发生了哪些事
答:
- 拿到要加载的文件绝对路径。没有后缀的尝试添加后缀
- 尝试从缓存中读取导出内容。如果缓存有,返回缓存内容。没有,下一步处理
- 新建一个模块实例,并输入进缓存对象
- 尝试加载模块
- 根据文件类型,分类处理
- 如果是js文件,读取到文件内容,拼接自执行函数文本,用vm模块创建沙箱实例加载函数文本,获得导出内容,返回内容
- 如果是json文件,读取到文件内容,用JSON.parse 函数转成js对象,返回内容
- 获取导出返回值。
http
1. http 和 https的区别
答: HTTPS就是将HTTP运行在TLS/SSL的加密安全措施下。
- https需要申请CA证书
- https更安全。运用了加密手段
- https端口443 http是80
2. udp和tcp的区别
答: 见我的另一篇文章TCP/IP
3. http3.0是基于udp的,为什么udp面向无连接还会选择udp?
答: 因为udp高效。而且在应用层解决了udp的不可靠性问题。
4. http3.0怎么解决udp的丢包问题?
答: http3不仅仅只是简单将传输协议替换成了 UDP。还基于 UDP 协议在「应用层」实现了 QUIC 协议。它具有类似 TCP 的连接管理、拥塞窗口、流量控制的网络特性,相当于将不可靠传输的 UDP 协议变成“可靠”的了,所以不用担心数据包丢失的问题。而且, QUIC 协议会保证数据包的可靠性,每个数据包都有一个序号唯一标识。当某个流中的一个数据包丢失了,即使该流的其他数据包到达了,数据也无法被 HTTP/3 读取,直到 QUIC 重传丢失的报文,数据才会交给 HTTP/3。
5. tcp除了你刚刚说的窗口控制,还有哪些控制?
答: 重发控制,流控制,拥塞控制
6. tcp重发机制是基于哪个时间节点
答: 引入两个概念:
- RTT(Round Trip Time):往返时延,也就是数据包从发出去到收到对应 ACK 的时间。RTT 是针对连接的,每一个连接都有各自独立的 RTT。
- RTO(Retransmission Time Out):重传超时,也就是前面说的超时时间。
我一般认为是两倍的RTT。
React
react 面试考点见我的react面试考点文章React面试