写在前面
- 本面试题是本人在工作和找工作中总结出来的面试题,不代表行业通用标准。
- 本文只关注技术相关问题
- 问的问题更偏向于前端深入了解、性能优化、故障定位等。
- 面试题难度大致相当于前端4-5年经验的水平
- 部分与我另一篇博客相同或类似,但毕竟随着成长以及技术的发展重视的东西也在改变。
- 答案仅供参考,不一定正确和完整,有异议或建议可提出修改
缓存
- 怎么设置静态资源缓存时间
1. 设置expires属性
2. 设置cache-control: max-age=毫秒
- 浏览器今天请求了静态文件,如果文件设置一个月缓存,明天再访问,会不会发起网络请求
1. 主流浏览器会根据最近一次访问时间,减去last modify,再除以十,来决定对这个文件保存多久。也即 lastVisitTime + (lastVisitTime - lastModifyTime) / 10。在这个时间之前不会发起网络请求
2. 在这之后会发起带 If-Modified-Since 的http请求。
3. 之所以这么设置,是因为如果文件很久没修改了,说明比较稳定。
4. 查到的答案,未实践,详见https://blog.csdn.net/youbl/article/details/84879670
- 服务端怎么判断要不要返回304
1. 根据etag比对和last modify时间(追问:etag是什么——文件内容哈希)
2. 根据 If-Modified-Since 判断
网络协议
- http2.0有什么新特性
1. 管道复用
2. 头部压缩
3. 服务端推送
4. 二进制帧
- TCP三次握手
详见 https://blog.csdn.net/sysuzjz/article/details/79545321 三次握手部分
- HTTPS交换密钥过程
1. TCP握手(HTTPS基于HTTP,即基于TCP)
2. 客户端预请求,将支持的加密算法、版本等信息发给服务端
3. 服务端将自己的配置、证书发给客户端
4. 客户端检验证书(是否有效,是否篡改,是否吊销等)。提取服务端公钥
5. 客户端生成随机数(对称密钥),用服务端公钥加密发送给服务端
6. 服务端根据自己的私钥解密得到对称密钥
7. 用对称密钥发送finish信息,完成密钥交换
8. 用对称密钥完成传递内容的加密
- HTTPS证书包含什么内容
1. 公钥(Public Key)
2. ISSUER(证书的发布机构)
3. Subject(证书持有者)(含使用域名)
4. 证书有效期
5. 签名
- 如何校验证书是否被篡改
1. 客户端预先下载根证书(权威CA机构的公钥)
2. 用CA公钥解开证书签名,得到指纹(哈希)和指纹算法
3. 用指纹算法对证书内容进行哈希计算
4. 哈希计算出来的结果与指纹进行对比,不一致则为被篡改
- 如何用UDP实现可靠传输
1. 引入序列号, 保证数据顺序;
2. 引入确认应答, 确保对端收到了数据;
3. 引入超时重传, 如果隔一段时间没有应答, 就重发数据
性能相关
脚本在head里加载没完成之前,dom会渲染吗
domready和onload区别
http1.0/1.1和http2.0在优化上有什么区别
link预请求
框架
react和vue有什么区别
react单向数据流与vue双向绑定孰优孰劣
react hooks 相比 class api有什么优缺点
react hooks没法实现class的什么功能
react 生命周期有哪些?(旧版和新版)
vue导航守卫
后端
缓存策略
ssr
可用性
监控哪些类型错误
怎么监控脚本错误
怎么监控资源加载错误
怎么监控接口错误
安全
csrf是什么
csrf如何防御
实现深拷贝(需要考虑value用到同一个对象的问题)
function deepCopy(obj) {
const valueMap = new Map();
function copy(obj) {
if (valueMap.get(obj)) {
return valueMap.get(obj);
}
if (Array.isArray(obj)) {
const temp = [];
for (let i = 0; i < obj.length; i++) {
temp[i] = deepCopy(obj[i]);
}
valueMap.set(obj, temp);
return temp;
}
if (Object.prototype.toString.call(obj) === '[object Object]') {
const temp = {};
Object.keys(obj).forEach(key => {
temp[key] = deepCopy(obj[key]);
})
valueMap.set(obj, temp);
return temp;
}
return obj;
}
const result = copy(obj);
valueMap.clear();
return result;
}
const a = { b: 1 }
const obj = {
a1: a,
a2: a,
}
const copyedObj = deepCopy(obj);
console.log(copyedObj === obj)
按顺序输出:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
编程
批量请求
请实现如下的函数,可以批量请求数据,所有的 URL 地址在 urls 参数中,同时可以通过 max 参数控制请求的并发度,当所有请求结束之后,需要执行 callback 回掉函数。发请求的函数可以直接使用 fetch 即可
function sendRequet(urls: string[], max: number, callback: () => void) {
}
function fetch(url) {
return new Promise();
}
function sendRequet(urls: string[], max: number, callback: () => void) {
if (!urls.length) {
callback();
}
let sendingRequestNum = 0;
let currentUrlIndex = 0;
let hasSendRequestNum = 0;
function checkQueneIsIdleAndSend() {
if (hasSendRequestNum === urls.length) {
callback();
}
if (sendingRequestNum === max) {
return;
}
for (let i = currentUrlIndex; i < currentUrlIndex + max - sendingRequestNum; i++) {
if (i >= urls.length) {
return;
}
sendingRequestNum++;
currentUrlIndex++;
fetch(urls[i]).finally(() => {
sendingRequestNum--;
hasSendRequestNum++;
checkQueneIsIdleAndSend();
})
}
}
checkQueneIsIdleAndSend();
}
连续数字区间
给定一个升序整形数组[0,1,2,4,5,7,13,15,16],找出其中连续出现的数字区间为如下:[“0->2”, “4->5”, “7”, “13”, “15->16”]
function summaryRanges(arr) {
if (!arr.length) {
return [];
}
let left = 0, right = 0;
const result = [];
while(left < arr.length) {
while (right < arr.length - 1 && arr[right + 1] - arr[right] <= 1) {
right++;
}
if (arr[left] === arr[right]) {
result.push(arr[left]);
} else {
result.push(`${arr[left]}->${arr[right]}`);
}
left = right + 1;
right = left;
}
return result;
}
console.log(summaryRanges([0,1,2,4,5,7,13,15,16,16, 18]))
斜45度打印二维矩阵
对于一般的m * n矩阵a
第一条45度斜边:a0
第二条45度斜边:a0, a1
………
最后一条45度斜边:am - 1
例如:
input = [[ 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15]]
output = 1, 2, 6, 3, 7, 11, 4, 8, 12, 5, 9, 13, 10, 14, 15
function logSquare(arr) {
if (!arr || !arr.length || !arr[0].length) {
return [];
}
const cols = arr[0].length,
rows = arr.length;
let col = 0, row = 0;
const result = [];
function loop() {
for (let i = col; i < cols; i++) {
for (j = row; j < rows; j++) {
if (i - j + row < 0) {
continue;
}
result.push(arr[j][i - j + row]);
}
}
}
while (row < rows) {
loop();
row++;
col = cols - 1;
}
return result;
}
字符串转二维数组
var strInput = `
1 2 3
4 5 6
7 8 9
`
var arrResult = [
[1,2,3],
[4,5,6],
[7,8,9]
]
function strToArr(str) {
if (!str) {
return [];
}
return str.split('\n')
.filter(row => row && row.trim())
.map(row => {
return row.trim().split(/\s+/g);
})
}