2021春招前端面试题及答案
1. get请求与post请求的区别
- get参数通过url传递,post放在request body中。
- get请求在url中传递的参数是有长度限制的,而post没有。
- get比post更不安全,因为参数直接暴露在url中,所以不能用来传递敏感信息。
- get请求只能进行url编码,而post支持多种编码方式
- get请求会浏览器主动cache,而post支持多种编码方式。
- get请求参数会被完整保留在浏览历史记录里,而post中的参数不会被保留。
- get和post本质上就是tcp链接,并无差别。但是由于http的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。
- get产生一个tcp数据包;post产生两个tcp数据包。
2. CDN的概念
CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN的关键技术主要有内容存储和分发技术。
3. Mysql的几种引擎
特性 | InnoDB | MyISAM | MEMORY |
---|---|---|---|
事物安全 | 支持 | 不支持 | 不支持 |
对外建的支持 | 支持 | 不支持 | 不支持 |
存储限制 | 64TB | 有 | 有 |
空间使用 | 高 | 低 | 低 |
内存使用 | 高 | 低 | 高 |
插入数据的速度 | 低 | 高 | 高 |
4. TCP的三次握手与四次挥手
① 三次握手
- 首先 Client 端发送连接请求报文
- Server 段接受连接后回复 ACK 报文,并为这次连接分配资源
- Client 端接收到 ACK 报文后也向 Server 段发生 ACK 报文,并分配资源,这样 TCP 连接就建立了
② 四次挥手
- 服务端申请断开连接即FIN,发送Seq+Ack
- 客户端接收信息返回,表示我已经接收到
- 客户端发送信息表示可以断开连接
- 服务端接受信息,返回数据表示已接受信息
③ 深入讨论
-
为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
建立连接时,ACK和SYN可以放在一个报文里来发送。而关闭连接时,被动关闭方可能还需要发送一些数据后,再发送FIN报文表示同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
-
为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
两个存在的理由:1、无法保证最后发送的ACK报文会一定被对方收到,所以需要重发可能丢失的ACK报文。2、关闭链接一段时间后可能会在相同的IP地址和端口建立新的连接,为了防止旧连接的重复分组在新连接已经终止后再现。2MSL足以让分组最多存活msl秒被丢弃。
-
为什么必须是三次握手,不能用两次握手进行连接?
记住服务器的资源宝贵不能浪费! 如果在断开连接后,第一次握手请求连接的包才到会使服务器打开连接,占用资源而且容易被恶意攻击!防止攻击的方法,缩短服务器等待时间。两次握手容易死锁。如果服务器的应答分组在传输中丢失,将不知道S建立什么样的序列号,C认为连接还未建立成功,将忽略S发来的任何数据分组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。
-
为什么要Time_Wait?
①可靠的终止TCP连接。
②保证让迟来的TCP报文有足够的时间被识别并丢弃
③让网络上的数据包自动消亡,防止旧连接初始了新的连接
5. TCP与UDP的区别
- TCP是面向连接的,udp是无连接的即发送数据前不需要先建立链接。
- TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。 并且因为tcp可靠,面向连接,不会丢失数据因此适合大数据量的交换。
- TCP是面向字节流,UDP面向报文,并且网络出现拥塞不会使得发送速率降低(因此会出现丢包,对实时的应用比如IP电话和视频会议等)。
- TCP只能是1对1的,UDP支持1对1,1对多。
- TCP的首部较大为20字节,而UDP只有8字节。
- TCP是面向连接的可靠性传输,而UDP是不可靠的。
6. ICMP、STMP
① ICMP
ICMP(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议簇的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。
工作过程:
ICMP提供一致易懂的出错报告信息。发送的出错报文返回到发送原数据的设备,因为只有发送设备才是出错报文的逻辑接受者。发送设备随后可根据ICMP报文确定发生错误的类型,并确定如何才能更好地重发失败的数据包。但是ICMP唯一的功能是报告问题而不是纠正错误,纠正错误的任务由发送方完成。我们在网络中经常会使用到ICMP协议,比如我们经常使用的用于检查网络通不通的Ping命令(Linux和Windows中均有,这个“Ping”的过程实际上就是ICMP协议工作的过程。还有其他的网络命令如跟踪路由的Tracert命令也是基于ICMP协议的。
② STMP
SMTP是一种提供可靠且有效的电子邮件传输(传送)的协议。SMTP是建立在FTP文件传输服务上的一种邮件服务,主要用于系统之间的邮件信息传递,并提供有关来信的通知。SMTP独立于特定的传输子系统,且只需要可靠有序的数据流信道支持,SMTP的重要特性之一是其能跨越网络传输邮件,即“SMTP邮件中继”。使用SMTP,可实现相同网络处理进程之间的邮件传输,也可通过中继器或网关实现某处理进程与其他网络之间的邮件传输。
工作过程:
- 建立连接:在这一阶段,SMTP客户请求与服务器的25端口建立一个TCP连接。一旦连接建立,SMTP服务器和客户就开始相互通告自己的域名,同时确认对方的域名。
- 邮件传送:利用命令,SMTP客户将邮件的源地址、目的地址和邮件的具体内容传递给SMTP服务器,SMTP服务器进行相应的响应并接收邮件。
- 连接释放:SMTP客户发出退出命令,服务器在处理命令后进行响应,随后关闭TCP连接。
7. 进程与线程的区别
- 进程是资源分配的最小单位,线程是程序执行的最小单位(资源调度的最小单位)
- 进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
- 线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
- 但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。
8. 解决异步回调的三种方法
- 基于ES6的promise
- Generator
- async/await
9. 同源策略以及跨域问题
① 什么是同源?
所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。
② 什么是跨域?
由浏览器同源策略限制的一类请求场景,当不同地址,不同端口,不同级别,不同协议就会构成跨域。
③ 如何解决跨域?
- 通过jsonp跨域:ajax 请求受同源策略影响,不允许进行跨域请求,而 script 标签 src 属性中的链接却可以访问跨域的js脚本,利用这个特性,服务端不再返回JSON格式的数据,而是返回一段调用某个函数的js代码,在src中进行了调用,这样实现了跨域。
- CORS
- document.domain + iframe跨域
- location.hash + iframe
- window.name + iframe跨域
- postMessage跨域
- nginx代理跨域
- nodejs中间件代理跨域
- WebSocket协议跨域
10. session与cookie的区别
- cookie数据存放在客户的浏览器上,session数据放在服务器上。
- cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,考虑到安全应当使用session。
- session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用COOKIE。
- 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
11. js闭包
闭包的本质就是在一个函数内部创建另一个函数。
闭包有3个特性:
- 函数嵌套函数
- 函数内部可以引用函数外部的参数和变量
- 参数和变量不会被垃圾回收机制回收
12. 前端安全问题
-
xss跨站脚本攻击原理?如何进行?防御手段?
如何进行:XSS是指恶意攻击者利用网站没有对用户提交数据进行转义处理或者过滤不足的缺点,进而添加一些代码,嵌入到web页面中去。使别的用户访问都会执行相应的嵌入代码。从而盗取用户资料、利用用户身份进行某种动作或者对访问者进行病毒侵害的一种攻击方式。
主要原理:过于信任客户端提交的数据。
防御手段:不信任任何客户端提交的数据,只要是客户端提交的数据就应该先进行相应的过滤处理然后方可进行下一步的操作。
-
CSRF跨站请求伪造原理?如何进行?防御手段?
如何进行:当你在某网页登录之后,在没有关闭网页的情况下,收到别人的链接。点击链接,会利用浏览器的cookie把密码改掉。
主要原理:在没有关闭相关网页的情况下,点击其他人发来的CSRF链接,利用客户端的cookie直接向服务器发送请求。
防御手段:
①检测Referer
②Anti-CSRF token机制
③业务上要求用户输入原始密码(简单粗暴),攻击者在不知道原始密码的情况下,无论如何都无法进行CSRF攻击。 -
Sql脚本注入原理?如何进行?防御手段?
如何进行:利用现有应用程序,将(恶意)的SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。
主要原理:通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令
防御手段:
①使用预编译,绑定变量(推荐)。
②检查数据类型。
③过滤特殊字符和语句。
13. ES6新语法
promise,await/async,let、const、块级作用域、箭头函数
14. axios、ajax、fetch
① ajax
$.ajax({
type: 'POST',
url: url,
data: data,
dataType: dataType,
success: function () {},
error: function () {}
});
- 本身是针对MVC的编程,不符合现在前端MVVM的浪潮
- 基于原生的XHR开发,XHR本身的架构不清晰。
- Query整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务)
- 不符合关注分离(Separation of Concerns)的原则
- 配置和调用方式非常混乱,而且基于事件的异步模型不友好。
② axios
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
- 从浏览器中创建 XMLHttpRequest
- 支持 Promise API
- 客户端支持防止CSRF
- 提供了一些并发请求的接口(重要,方便了很多的操作)
- 从 node.js 创建 http 请求
- 拦截请求和响应
- 转换请求和响应数据
- 取消请求
- 自动转换JSON数据
③ fetch
try {
let response = await fetch(url);
let data = response.json();
console.log(data);
} catch(e) {
console.log("Oops, error", e);
}
- 语法简洁,更加语义化
- 基于标准 Promise 实现,支持 async/await
- 同构方便,使用 isomorphic-fetch
- 更加底层,提供的API丰富(request, response)
- 脱离了XHR,是ES规范里新的实现方式
总结:axios既提供了并发的封装,也没有fetch的各种问题,而且体积也较小,当之无愧现在最应该选用的请求的方式。
15. 前端性能优化
- 降低请求量:合并资源,减少HTTP 请求数,minify / gzip 压缩,webP,lazyLoad。
- 加快请求速度:预解析DNS,减少域名数,并行加载,CDN 分发。
- 缓存:HTTP 协议缓存请求,离线缓存 manifest,离线数据缓存localStorage。
- 渲染:JS/CSS优化,加载顺序,服务端渲染,pipeline。
16. typeof与instanceof的区别
① typeof
typeof 是判断参数是什么类型的实例,返回值为说明运算数类型的字符串。
返回值结果:“number”、“string”、“boolean”、“object”、“function”、“undefined”
若参数为引用类型,始终返回“object”,对于Array、null始终返回“object”,所以用typeof来判断参数类型有很大的局限性。
② instanceof
instanceof是用来判断一个对象在其原型链中是否存在一个构造函数的prototype属性
a instanceof b:判断a是否为b的实例,可以用于继承关系中
b是c的父对象,a是c的实例,a instanceof b 与 a instanceof c 结果均为true
17. 深拷贝与浅拷贝
① 深拷贝
// deepClone
function deepClone(obj = {}) {
if (typeof obj !== 'object' || obj == null) {
// obj 是 null 或者不是对象和数组,直接返回
return obj;
}
let res;
if (obj instanceof Array) {
res = [];
} else {
res = {};
}
for (let key in obj) {
// 判断自身中是否包含自身属性
if (obj.hasOwnProperty(key)) {
res[key] = deepClone(obj[key])
}
}
return res;
}
// 验证
o = {a: 1, d: {c: '4'}};
res = deepClone(o);
console.log(res);
console.log(res == o);
②浅拷贝
// 首先定义一个对象
const hero = {
name: 'Batman',
city: 'Gotham'
};
// **********************方法一**********************
const heroEnhancedClone = {
...hero,
name: 'Batman Clone',
realName: 'Bruce Wayne'
};
// 验证
heroEnhancedClone; // { name: 'Batman Clone', city: 'Gotham', realName: 'Bruce Wayne' }
// **********************方法二**********************
const { ...heroClone } = hero;
// 验证
heroClone; // { name: 'Batman', city: 'Gotham' }
hero === heroClone; // => false
// **********************方法三**********************
const hero = {
name: 'Batman',
city: 'Gotham'
};
// 验证
const heroClone = Object.assign({}, hero);
heroClone; // { name: 'Batman', city: 'Gotham' }
hero === heroClone; // => false
18. react的useMemo、useEffect、useCallback
- useCallback:接收一个内联回调函数和一个依赖数组,返回一个记忆版本的回调函数。如果第二个参数没有指定改变值的情况,它只会在第一次时进行该值闭包缓存,不再更新
import React, { useState, useCallback } from 'react'; const [getImageUrl, setImageUrl] = useState<string>(""); const [getUrlLoading, setUrlLoading] = useState<boolean>(false); const handleChange: (info: UploadChangeParam<any>) => void = useCallback(info => { if (info.file.status === "uploading") { setUrlLoading(true); return; } if (info.file.status === "done") { getBase64(info.file.originFileObj, (imageUrl: any) => { setImageUrl(imageUrl); setUrlLoading(false); }); } }, [setImageUrl, setUrlLoading]);
- useMemo:当依赖没有发生变化时,不执行计算,直接返回缓存结果(有点像 vue 的计算属性)。使用useMemo,然后给她传递第二个参数,参数匹配成功,才会执行
import React, { useState, useMemo } from 'react'; const [count, setCount] = useState(0); //第一个参数是要执行的函数 //第二个参数是执行函数依赖的变量组成的数据 //这里只有count发生变化double才会重新计算 const double = useMemo(() => { return count * 2; }, [count]) // 下面为第二个参数满足条件再执行 const double = useMemo(() => { return count * 2; }, [count===3])
- useEffect:可以把 useEffect 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
function Example() { const [count, setCount] = useState(0); useEffect(() => { const id = setInterval(() => { setCount(c => c + 1); }, 1000); return () => clearInterval(id); }, []); return <h1>{count}</h1> }
19. js数组常用方法
push(),pop(),shift(),unshift(),splice(),sort(),reverse(),map(),reduce()等
20. var,let,const 的区别
- const定义的变量不可以修改,而且必须初始化
const b = 2;//正确 // const b;//错误,必须初始化 console.log('函数外const定义b:' + b);//有输出值 // b = 5; // console.log('函数外修改const定义b:' + b);//无法输出
- var定义的变量可以修改,如果不初始化会输出undefined,不会报错
var a = 1; // var a;//不会报错 console.log('函数外var定义a:' + a);//可以输出a=1 function change(){ a = 4; console.log('函数内var定义a:' + a);//可以输出a=4 } change(); console.log('函数调用后var定义a为函数内部修改值:' + a);//可以输出a=4
- let是块级作用域,函数内部使用let定义后,对函数外部无影响
let c = 3; console.log('函数外let定义c:' + c);//输出c=3 function change(){ let c = 6; console.log('函数内let定义c:' + c);//输出c=6 } change(); console.log('函数调用后let定义c不受函数内部定义影响:' + c);//输出c=3