前端面试题总结
第一部分(原文:知乎乎友)
1、HTTP协议基础
介绍:HTTP为超文本传输协议,工作于客户端-服务端架构上,基于TCP/IP协议进行数据传递,默认端口号80。该协议是一个无连接、无状态的请求/响应协议。
-
客户端请求消息
四大组成部分:请求行、请求头部、空行和请求数据。
首行包括请求方法、请求URI、协议版本。
-
服务器响应消息
四大组成部分:状态行、消息报头、空行和响应正文。
首行包括HTTP版本、状态码、状态描述。
-
HTTP状态码
状态码由三个十进制数字组成, 第一个数字定义了状态码类型:1表示信息,需要请求者继续执行操作;2表示成功;3表示重定向,需要进一步操作以完成请求;4表示客户端错误;5表示服务器错误。
常见状态码有:
200请求成功 301永久重定向,资源被永久转移到其他URI,会返回新的URI,今后新请求应使用新URI 302临时重定向,客户端应继续使用原有URI 304所请求的资源未修改,服务器不返回任何资源 401用户身份验证未提供或未通过 403服务器拒绝执行此请求 404请求的资源不存在 500内部服务器错误
2、MVVM
- MVVM:Model-View-ViewModel的缩写
- Model:数据模型
- View:用户操作界面
- ViewModel:视图数据层。ViewModel通过双向数据绑定将View和Model层连接了起来,开发人员不用手动操作Dom元素,View和Mode会自动双向同步,开发者只需要关注业务逻辑即可。
3、常见优化性能方法
- 尽量减少HTTP请求。如使用打包工具合并资源、CSS Sprite、静态资源使用缓存
- 压缩请求资源体积。如使用webpack进行压缩、服务端开启gzip压缩等
- 使用CDN减少响应时间
- 减少DNS查询次数。如减少主机名、缓存DNS等
- 将js脚本尽量放在页面底部,将css样式表放在顶部
- 减少回流与重绘
- 减少DOM元素数量和DOM操作
4、图片懒加载
-
第一步:将img标签的src链接设为同一张图片(比如某空白图片)
-
第二步:后给img标签设置自定义属性(比如 data-src),并将真正的图片地址存储在其中
-
当js监听到该图片元素进入可视窗口时(获取img节点距离浏览器顶部的距离,如果小于或等于浏览器窗口的可视高度即进入),再将自定义属性中的地址存储到src属性中,达到懒加载的效果
优点:不仅可以减轻服务器压力,也可以提高用户体验
5、CSRF(Cross-site request forgery)跨站请求伪造
-
释义:可以理解为攻击者盗用了用户的身份,以用户的名义发送了恶意请求。通常攻击者会诱导受害者进入第三方网站,在第三方网站中,利用受害者在被攻击网站已经获取的注册凭证,向被攻击网站发送跨站请求。这样便绕过被攻击网站后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
-
防御方式:同源检测、Token验证等
6、浏览器输入URL后过程
- 查看缓存,如果有缓存且新鲜直接提供给客户端,否则与服务器进行验证
- DNS解析、查找该域名对应的IP地址,重定向(301)、发出第二个GET请求
- 域名解析,查找域名的IP地址。按照浏览器缓存,本机缓存,hosts文件,路由器缓存,DNS服务器递归搜索的顺序查找;
- 打开一个socket与目标IP请求建立TCP链接,发起三次握手;
- 浏览器向服务器发送HTTP请求;
- 服务器处理并响应HTTP请求,发回响应报文;
- 浏览器解析HTML文档,构建DOM、CSS-DOM树,并根据它们构建渲染树。这个过程中还会下载资源,执行js脚本;
- 在解析过程中逐步显示页面。
7、如何将一个数组打乱
- 方法1:sort方法使用了插入排序(目标长度小于10)和快排,元素之间的比较远小于n(n-1)/2,因此有些元素间没有随机交换的可能,使得该方法不够随机。
// An highlighted block
arr.sort(() => Math.random() - 0.5);
- 方法2:Fisher-Yates Shuffle,复杂度为O(n)。从后向前遍历,不断将当前元素与随机位置的元素(除去已遍历的部分)进行交换。
// An highlighted block
function shuffle(arr) {
let m = arr.length;
while (m > 1){
let index = Math.floor(Math.random() * m--);
[arr[m] , arr[index]] = [arr[index] , arr[m]]
}
return arr;
}
8、__proto__与prototype
9、for in、Object.keys、Object.getOwnProperty的区别
-
for in用于遍历对象的可枚举、自有与继承自原型的属性;
-
Object.keys() 返回一个数组,为对象的可枚举、自有属性;
-
Object.getOwnProperty返回一个数组,为对象的可枚举与不可枚举、自有属性;
10、window.requestAnimationFrame方法
-
接受一个回调函数作为参数,回调会在浏览器下次重绘之前执行。如果想在下次重绘之前继续更新下一帧动画,回调函数自身须再次调用window.requestAnimationFrame
-
每次调用都会得到一个id,可以用window.cancelAnimationFrame(id)来停止,避免无限循环调用
-
回调函数执行次数通常每秒60次,但大多浏览器中执行次数与浏览器屏幕刷新次数匹配
11、Event Loop & 宏任务微任务
JavaScript是单线程语言,单线程意味着所有任务需要排队执行,可将任务分为同步任务和异步任务两种。
同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕才能执行后一个任务
异步任务是指不进入主线程而进入任务队列的任务,只有任务队列通知主线程某个异步任务可执行,该任务才会进入主线程执行。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。它们的具体运行机制为:
- 所有同步任务都在主线程上执行,形成一个执行栈
- 还存在一个任务队列,只要异步任务有了结果,就在任务队列中放置一个事件;当执行栈所有同步任务完成,系统就会读取任务队列,将事件对应的异步任务加入执行栈执行
- 这个过程是循环不断的,所以整个运行机制称为Event Loop(事件循环)
任务又能进一步细分为宏任务和微任务,宏任务如主代码块,setTimeout等,微任务如Promise等。每执行一个宏任务后,会执行完所有微任务,再从下一个宏任务开始。因此以下代码会按序号输出:
setTimeout(() => console.log(4))
new Promise(resolve => {
resolve()
console.log(1)
}).then(() => {
console.log(3)
})
console.log(2)
12、重绘与回流
Render Tree(渲染树): 浏览器使用流式布局,把HTML解析成DOM,CSS解析成CSSOM,两者合并就产生了Render Tree渲染树。一旦Render Tree构建完毕后,浏览器就可以根据它来绘制页面
回流:当Render Tree中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
- 会导致回流的操作如页面首次渲染,浏览器窗口大小改变,元素尺寸、位置、内容等变化,添加或删除可见DOM元素,激活CSS伪类等
- 会导致回流的属性和方法如clientWidth、offsetWidth、scrollWidth等
重绘:当页面中元素只是样式改变,并不影响它在文档流中的位置时,浏览器将新样式赋予元素并重新绘制,这个过程则称为重。
总结:
- 回流必将引起重绘,重绘不一定会引起回流
- 页面布局和几何属性改变时就需要回流,回流比重绘代价更高
- 现代浏览器会维护一个队列,将多次的变动“攒着”,等积累一定数量的变动再一次性执行,但比如取offsetWidth这样的属性时,浏览器为了给精确的值会立即刷新队列。
避免重绘与回流的方法:
- 避免频繁操作样式,最好一次性重写style属性,或者将样式定义为class并一次性更改class属性
- 避免频繁操作DOM,尽量使用离线的DOM,比如创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加回文档中
- 可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘
- 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来
- 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流
13、防抖与节流
应用场景:在绑定scroll、resize这类高频度触发事件时,它被触发的频次非常高,间隔很近,常会用到防抖和节流两种技巧
防抖(debounce):是当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次。如果设定的时间到来之前,又一次触发了事件,就重新开始延时
// 简单的防抖函数
function debounce(func, wait) {
// 定时器变量
var timeout;
return function() {
// 每次触发时先清除定时器
clearTimeout(timeout);
// 指定 xx ms 后触发真正想进行的操作
timeout = setTimeout(func, wait);
};
};
// 实际想绑定在事件上的handler
function realFunc(){
console.log("Success");
}
// 每次高频事件都会取消前一次的超时调用,只有当高频事件停止,最后一次事件触发的超时调用才能在delay时间后执行
window.addEventListener('scroll',debounce(realFunc,500));
节流(throttle):不管事件触发多频繁,都会保证在规定时间内只执行一次事件处理函数
// 简单的节流函数
var throttle = function(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function handle() {
console.log("Success");
}
window.addEventListener('scroll', throttle(handle, 1000));
14、HTTP缓存
HTTP缓存:浏览器第一次向服务器发起HTTP请求后,服务器会返回请求的资源,并在响应头中添加一些有关缓存的字段。之后浏览器再次请求该资源时,就可以视情况使用强缓存或协商缓存。
强缓存:指浏览器直接从本地缓存中获取数据,不与服务器进行交互
控制强缓存的字段有:
- Cache-Control。如设置max-age=10,则表示缓存的存在时间为10秒;
- Expires。指定了文件的过期时间,如果有max-age则会忽略;
协商缓存:指浏览器发送请求到服务器,服务器判断是否可使用本地缓存,如果可以使用则服务器返回304。
控制协商缓存的字段有:
- Last-Modified / If-Modified-Since。记录文件最近修改时间,前者是响应首部字段,后者是请求首部字段;
- Etag / If-None-Match。根据实体内容生成一段hash字符串,前者是响应首部字段,后者是请求首部字段。优先级高于Last-Modified。
15、跨域
跨域:指一个域下的脚本试图去请求另一个域下的资源,由浏览器同源策略导致。只要协议、域名、端口有任何一个不同,就会被视为不同的域。
同源策略限制了不同域:Cookie、LocalStorage和IndexDB无法读取、DOM和js对象无法获取,Ajax请求发送不出去。
两种常见跨域方法:
- jsonp跨域:原理是利用HTML中一些标签的src获取资源没有跨域限制,例如通过动态的创建script标签,去请求一个带参网址以实现跨域通信,该方法只能实现get请求。
<script>
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参并指定回调执行函数为onBack
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack';
document.head.appendChild(script);
// 回调执行函数
function onBack(res) { alert(JSON.stringify(res)); }
</script>
- CORS跨域资源共享:实现CORS通信的关键是服务器实现CORS接口。浏览器将CORS请求分为简单请求和非简单请求两类:
-
简单请求:当请求方法是head、get、post三种之一,以及HTTP头信息不超出几个特定的字段就是简单请求。对于简单请求,浏览器会自动在头信息中添加一个Origin字段,如果服务器回应的头信息没有Access-Control-Allow-Origin字段,则表示源不在服务器许可范围内,触发错误。
-
非简单请求:会在正式通信前增加一次HTTP查询请求,称为预检请求。浏览器先询问服务器,当前域名是否在服务器许可名单之中,以及可以使用的字段,只有得到肯定答复,才会发出正式的XMLHttpRequest请求,否则触发错误。预检请求头信息包括Origin、Access-Control-Request-Method、Access-Control-Request-Headers几个特殊字段。
16、使用原生Ajax
Ajax:异步的js和XML,能在不重新加载整个页面的情况下与服务器交换数据并更新部分网页内容,XMLHttpRequest是Ajax的基础。
使用原生Ajax有三个步骤:
// a.创建一个XMLHttpRequest对象:
var xhr = new XMLHttpRequest();
// b.配置请求与发送(get或post):
xhr.open("GET","/try/ajax/ajax_demo.txt",true); //true代表异步
xhr.send();
或
xhr.open("POST","/try/ajax/demo_post2.php",true);
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send("fname=Zhou&lname=minghang");
//c.监听变化执行回调函数:
xhr.onreadystatechange=function()
{
if (xhr.readyState==4 && xhr.status==200){
document.getElementById("myDiv").innerHTML = xhr.responseText;
}else{
console.log('失败了');
}
}
readyState:存有XMLHttpRequest状态,0~4分别表示:未初始化、连接已建立、请求已接收、请求处理中、请求已完成。
status:存有HTTP状态码,responseText与responseXML则用于获取响应数据。
17、浏览器及其内核
18、Doctype作用?标准模式与兼容模式各有什么区别?
-
!DOCTYPE声明位于位于HTML文档中的第一行,处于html 标签之前。告知浏览器的解析器用什么文档标准解析这个文档。DOCTYPE不存在或格式不正确会导致文档以兼容模式呈现
-
标准模式vs兼容模式
- 标准模式的排版和JS运作模式都是以该浏览器支持的最高标准运行
- 在兼容模式中,页面以宽松的向后兼容的方式显示,模拟老式浏览器的行为以防止站点无法工作
19、html5有哪些新特性、移除了那些元素?如何处理HTML5新标签的浏览器兼容问题?如何区分 HTML 和HTML5?
- 新增元素
-
绘画 canvas
-
用于媒介回放的 video 和 audio 元素
-
本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
-
sessionStorage 的数据在浏览器关闭后自动删除
-
语意化更好的内容元素,比如 article、footer、header、nav、section
-
表单控件,calendar、date、time、email、url、search
-
新的技术webworker, websockt, Geolocation
-
2. 移除元素
-
纯表现的元素:basefont,big,center,font, s,strike,tt,u
-
对可用性产生负面影响的元素:frame,frameset,noframes
3. 处理HTML5新标签的浏览器兼容问题???
-
IE8/IE7/IE6支持通过document.createElement方法产生的标签,可以利用这一特性让这些浏览器支持HTML5新标签
20、 cookies,sessionStorage 和 localStorage 的区别?
- sessionStorage和localStorage的存储空间更大
- sessionStorage和localStorage有各自独立的存储空间
- sessionStorage和localStorage有更多丰富易用的接口
- cookie在浏览器和服务器间来回传递。 sessionStorage和localStorage不会
cookie和localStorage的区别有:
-
cookie可以由服务端和js读写(如果设置了HttpOnly的话js无法读),localStorage只能是js读写
-
cookie会附带在HTTP请求头里,而localStorage不会
-
cookie可设置过期时间,而localStorage不能
-
同域名的http和https共享cookie(设置了Secure的除外)但是不共享localStorage
-
cookie的接口没有localStorage方便,localStorage直接写key-value,而cookie需要自己构造符合要求的格式
因为cookie会附带在HTTP请求头里,如果太大会影响传输性能,所以容量限制比较小
21、如何实现浏览器内多个标签页之间的通信?参考博客
- websocket通讯
- 定时器setInterval+cookie
- 使用localstorage
- html5浏览器的新特性SharedWorker