web前端常见面试题合集
Http
http和http的基本概念
http是一个客户端和服务器请求和应答标准(TCP),用于www服务器传输超文本到本地浏览器的超文本传输协议。
https是以安全为目标的HTTP通道,即http下加入了SSL层进行加密。主要作用是建立了一个信息安全通道,来确保数据的传输有一定的安全性,确保网站的真实性
http和https的区别以及优缺点
- http是超文本传输协议,信息是明文传输的,而https协议要比http协议更安全,https是具有安全性的ssl加密传输协议,可以防止数据在传输过程中不被窃取、改变,确保了数据的安全性以及完整性
- http协议的默认端口是80,https的默认端口则是443
- http链接很简单,是无状态的,https握手阶段比较费时,会导致页面加载时间延长50%,增加10%~20%的耗电
- https的缓存不如http的高效,会增加数据开销
- https协议需要ca证书,费用较高,功能越强大的证书费会越贵
- SSL协议需要绑定ip,不能在同一个ip上绑定多个域名,IPV4资源支持不了这种消耗
https协议的工作原理
客户端在使用https方式与web服务器通信的时候有以下几个步骤
- 客户端在使用https url访问服务器,要求web服务器建立ssl链接
- web服务器接收到客户端发出的请求后,会将网站的证书(证书中包含了公钥),传输给客户端。
- 客户端和web服务器端开始协商SSL链接的安全等级,也就是加密等级。
- 客户端浏览器通过双方协商一致的安全等级,会建立一个会话密钥,然后通过网站的公钥来加密会话密钥,并传输给网站
- web服务器通过自己的私钥解密出会话密钥
- web服务器通过会话密钥加密与客户端的通信
http请求跨域问题
跨域的原理
跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的
同源策略,是浏览器对JavaScript实施的安全限制,只要协议、域名、端口有任何一个不同、都被当做是不同的域
跨域原理,即是通过各种方式,避开浏览器的安全限制
解决方案
jsonp
ajax请求受同源策略影响,允许进行跨域请求,而script标签src属性中链接却可以访问跨域的js脚本,利用这个特性,服务端不再返回json格式的数据,而是返回一段调用某个函数的js代码,在src中进行了调用,这样就实现了跨域功能,
jsonp缺点:jsonp只支持get,因为script标签只能使用get请求,jsonp需要后端配合返回指定格式的数据
步骤:
- 去创建一个script标签
- script的src属性设置接口地址
- 接口参数,必须要带一个自定义函数名,要不然后台无法返回数据
- 通过自定义函数名去接受返回的数据
//动态创建script
var script = document.createElement('script');
// 设置回调函数
function getData(data){
console.log(data)
}
//设置script的src属性,并设置请求地址
script.src = 'http://localhost:80/?callback=getData'
//让script生效
document.body.appendChild(script)
CORS资源共享
浏览器会自动进行CORS通信,实现CORS通信的关键是后端。服务器设置Access-Contorl-Allow-Origin就可以开启CORS。该属性表示那些域名可以跨域访问资源
主要设置以下几个属性
- Access-Control-Allow-Origin //允许跨域的域名
- Access-Control-Allow-Headers //允许的header的类型
- Access-Control-Allow-Methods //跨域允许的请求方式
Nginx反向代理
通过nginx配置有个代理服务器将客户机请求转发给内部网络上的目标服务器,并将服务器上返回的结果返回给客户端
webpack (在Vue.config.js文件中) 配置webpack-dev-server
devServer: {
proxy: {
'/api': {
target: "http://39.98.123.211",
changeOrigin: true, //是否跨域
},
},
},
TCP
TCP三次握手
- 第一次握手:建立链接时,客户端发送syn包到服务器,并进入SYN_SENT状态,等待服务器确认;同步序列编号
- 第二次握手:服务器收到syn包并确认客户的syn,同时也发送一个自己的syn包,即syn+ack包,此时服务器进入SYN_RECV状态;
- 第三次握手:客户端收到服务器的SYN+ACK包,想服务器发送确认包ACK,此包发送完毕,客户端和服务器进入ESTABLISHED(TCO链接成功)状态,完成三次握手
TCP四次挥手
- 客户端进程发出链接释放报文,并且停止发送数据,释放数据报文首部,FIN = 1,其序列号为seq = u(等于前面已经传送过来的数据的最后一个字节的序号加一),此时,客户端进入FIN-WAIT-1(终止等待)状态。TCP规定,FIN报文段即使不懈怠数据,也要消耗一个序号。
- 服务器收到链接释放报文,发出确认报文,ACK=1,ack = u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭的状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受,这个状态还要持续一段时间,也就是close-wait状态持续的时间
- 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最 后的数据)。
- 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
- 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
- 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
TCP/IP如何保证数据包传输的有序可靠?
对字节流分段并进行编号然后通过ACK回复和超时重发这两个机制来保证。
- 为了保证数据包的可靠传递,发送方必须把已发送的数据包保留在缓冲区;
- 并为每个已发送的数据包启动一个超时定时器
- 如在定时器超时之前收到了对方发送的应答信息,则释放该数据包占用的缓冲区
- 否则,重传该数据包,直到收到应答或者重传次数超过规定的最大次数为止。
- 接收方收到数据包后,先进行CRC校验,如果正确则把数据交给上层协议,然后给发送方发送一个累计应答包,表名数据已经收到,如果接收方正好也有数据要发给发送方,应答包也可在数据包中捎带过去
Cookie、sessionStorage、localStorage的区别
相同点
存储在客户端
不同点
- cookie数据大小不能超过4k,sessionStorage和localStorage的存储比cookie大的多,可以达到5M+
- cookie设置的过期时间之前一直有效;localStorage永久存储,浏览器关闭后数据不丢失除非主动删除数据,sessionStorage数据在当前浏览器窗口关闭时会自动删除
- cookie的数据会自动的传递到服务器;sessionStorage和localStorage数据保存在本地
val&&let && const
ES6之前创建变量用的是val,之后创建变量用的是let/const
三者区别
- var定义的变量,没有块的概念,可以跨块访问,不能跨函数访问
let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问
const 用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,并且不能修改。 - var 可以先使用,后声明,因为存在变量提升;let必须先声明后使用
- var 是允许在相同作用域内重复声明同一个变量的,而let和const不允许这一现象。
- 在全局上下文中,基于let声明的全局变量和全局对象GO(window)没有任何关系,var声明的变量会和GO又映射关系
- 会产生暂时性死区:
暂时性死区是浏览器的bug:检测一个未被声明的变量类型时,不会报错,会返回undefined
如:console.log(typeof a) //undefined
而:console.log(typeof a)//未声明之前不能使用
let a - let/const/function会把当前所在的大括号(除函数之外)作为一个全新的块级上下文,应用这个机制,在开发项目的时候,遇到循环事件绑定等类似的需求,无需自己构建闭包来存储,只要基于let的块作用特征即可解决
JS垃圾回收机制
在项目中,如果存在大量不被释放的内存(堆/栈/上下文),页面性能会变得很慢,当某些代码操作不能被合理释放,会造成内存泄露,我们尽可能减少使用闭包,因为它会消耗内存
浏览器垃圾回收机制/内存回收机制:
浏览器的javascript具有自动垃圾回收机制,垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。
- 标记清除:在js中,最常用的垃圾回收机制是标记清除,当变量进入执行环境时,被标记为进入环境,当变量离开执行环境时,会标记为离开环境。垃圾回收器就会销毁那些带标记的值并回收他们所占用的内存空间
谷歌浏览器:查找引用,浏览器不定时去查找当前内存的引用,如果没有被占用了,浏览器会回收他,如果被占用,就不能回收
IE浏览器:引用计数法,当前内存被占用一次,计数累加1次,移除占用就减1,减到0时,浏览器就会回收它
优化手段
内存优化;手动释放:取消内存的占用即可
1. 堆内存:fn=null (null:空指针对象)
2. 栈内存:把上下文中,被外部占用的堆的占用取消即可。
内存泄漏
在js中,常见的内存泄露主要有4种:全局变量、闭包、DOM元素的引用、定时器
JS中this的五种清空
- 作为普通函数执行时,this指向window
- 当函数作为对象的方法被调用时,this就会指向该对象。
- 构造器调用,this指向返回的这个对象。
- 箭头函数 箭头函数的this绑定看的是this所在函数定义在那个对象下,就绑定哪个对象,如果有嵌套的情况,则this绑定最近的一层对象上。
- 基于function.prototype上的apply、call、和bind调用模式,这三个方法都可以显示的指定调用函数this指向。apply接收参数的是数组,call接收参数列表,bind方法通过传入一个对象,返回一个this绑定了传入对象的新函数。这个函数的this指向除了使用new时会被改变,其他情况都不会改变,若为空默认是指向全局对象的window。
介绍节流防抖原理、区别以及应用
节流
事件触发后,规定事件内,事件处理函数不能再次被调用。也就是说在规定时间内,函数只能被调用一次,且是最先被触发调用的那次
防抖
多次触发事件,事件处理函数只能执行一次,并且是在触发操作结束时去执行。也就是说,当一个事件被触发准备执行事件函数前,会等待一定的事件(这事件是码农自己去定义的,比如1秒),如果没有再次被触发,那么就执行,如果触发了,那就本次作废,重新从新触发的事件开始计算,并再次等待1秒,直到能最终执行!
使用场景
节流:滚动加载更多、搜索框的搜索联想功能、高频点击、表单重复提交…
防抖:搜索框搜索输入,并在输入完以后自动搜索、手机号、邮箱验证输入检测、窗口大小resize变化后,再重新渲染
/**
*节流函数 一个函数执行一次后,只有大于设定的执行周期才会执行第二次。有个需要频繁触发的函数,出于优化性能的角度,在规定时间内,只让函数触发的第一次生效,后面的不生效。
* @param fn要被节流的函数
* @param delay规定的时间
*/
function throttle(fn,delay){
//记录上一次函数触发的事件
var lastTime = 0;
return function(){
//记录当前函数触发的事件
var nowTime = Date.now();
if(nowTime - lastTime > delay){
//修正this指向问题
fn.call(this);
//同步执行结束时间
lastTime = nowTime;
}
}
}
document.onscroll = throttle(function(){
console.log('scllor事件被触发了'+Data.now());
},200)
/**
* 防抖函数 一个需要频繁触发的函数,在规定时间内,只让最后一次生效,前面的不生效
* @param fn要被节流的函数
* @param delay规定的时间
*/
function debounce(fn,delay){
//记录上一次的延时器
var timer = null;
return function(){
//清除上一次的延时器
clearTimeout(timer);
//重新设置新的延时器
timer = setTimeout(function(){
//修正this指向问题
fn.apply(this);
},delay)
}
}
document.getElementById('btn').onclick = debounce(function(){
console.log('按钮被点击了'+Data.now());
},1000)