前端面试习题收集

1. React相对于传统的框架来说其优势是什么?
框架本身就是为了更好更方便而诞生的,而框架最具优势的就是各种使用便捷,接口规范的插件
插件是框架的灵魂。其本质的好处就是以框架规范代码,使项目工程化,提高可维护性。
(1)虚拟dom,可以减少dom的操作,减少dom的操作就是提高浏览器的渲染性能
(2)学会了jsx这个语法,你不尽可以使用react来写web,同时可以使用react-native来写android或者IOS应用,还可以
react-native-desktop来写桌面应用。
(3)react提供了组件化的开发,大大提供了开发效率,提高了可维护性
(4)react提倡使用不可变数据,这样可以大大节约了脏检查的消耗
(5)react提倡使用flux来进行组件之间的数据传输,叫做单向数据流。单向数据流的好处就是
各种变化都是可预计的,可控制的。
2.  React如何做数据管理?介绍下Redux? React Component的声明周期? 说下React-Router?
2.1  react常见的传值有三种:父组件向子组件传值,子组件向父组件传值,兄弟组件之间传值
父组件向子组件传值通常是使用props来进行
子组件向父组件传值方式:父组件中写好state和处理该state的函数,同时将函数名通过props属性传递给子组件,子组件调用父组件的函数就会引发state的变化
兄弟组件之间传值是通过父组件来进行的,把数据挂载在父组件中,之后载传递给子组件
基于Redux的数据管理,Redux就是为了让state变得可预测
基于dva的React项目开发的数据管理
3. http协议的缓存机制?
浏览器的缓存分为两类:强缓存和协商缓存
所谓的强缓存就是本地缓存,当请求资源的时候直接从本地缓存中调取资源,
而协商缓存则是由服务器来确定缓存资源是否可用
决定强缓存的两个重要字段是cathe-control和expires,expires的值为一个绝对时间的GMT格式的时间字符串,如果请求时间在expires之前则缓存可用,否则就会向服务器发送请求
cathe-control:max-age=number,该number是一个设定的有效期,根据第一次获取资源的时间加上该有效期就能计算出该资源失效的时间,如果请求时间在失效时间之前则则说明缓存依然可用
否则就要重新发送请求
协商缓存对应的是两组信息:Etag/if-none-match,last-modified/if-modified-since
last-modified / if-modified-since的值都是GMT格式的字符串,在第二次请求的资源时header头部就会携带if-modified-since值就是上次请求时返回的last-modified值,
服务器再次受到请求后,会根据传过来的if-modified-since和资源的最后修改时间对比,如果相同就直接返回304Notmodified,那么客户端就直接从缓存中获取数据,但是如果不同,那么就把新的资源返回给客户端。
Etag/if-none-match是服务器生成的每个资源的唯一标示符,一旦资源发生变化Etag就会发生变化,其判断过程与modified类似。
Etag主要用来解决两个问题:
一些服务器不能精确获取到最后文件修改的时间
某些文件的修改频率过于频繁,比如说是在秒一下的时间修改,但是if-modified-since的精确粒度是s,所以需要使用到Etag。
如果Etag和last-modified同时存在的话,那么优先检测ETag。
4. 前端性能优化的方法?
减少http请求:可以合并图片,合并css和js文件,图片较多的时候可以使用lazyload
尽量使用class来修改属性而不是使用style来修改节点样式
尽量减少对dom的操作
尽量使用json来进行数据交换
使用CDN加速
使用浏览器缓存
适量的减少cookie传输
避免使用css表达式,使用link标签来代替@import
谨慎使用with,避免使用eval Function函数,减少作用域链查找。
使用css3代码代替js动画
缓存dom节点查找的结果
避免使用空的href和src,因为它们会阻塞页面其它资源的下载进程
5. 前端攻击xss和csrf?防御的方法。
CSRF:(Cross-stite request forgery),跨站请求伪造
CSRF可以用来做写什么:攻击者可以盗用你的身份,以你的名义来发送恶意的请求,最终造成的问题就是个人隐私的泄露和财产安全
要完成一次csrf需要完成两个步骤:
用户已经登录安全网站A,并且生成了cookie
用户在安全网站A去访问危险网站B。
csrf攻击就是源于web的隐私身份验证机制。但是web身份验证机制可以验证某个请求是否是源自于某个用户的浏览器,但是却无法确定该请求时用户批准发送的。
常见的攻击:
假如某博客网站发布留言的请求是 get: http://www.blog.com/message?content=留言内容。
现在我登录了此博客网站A,然后又访问了另外一个网站B,B网站直接跳转到:http://www.blog.com/message?content=嘿嘿嘿。那么你就在A网站自动的留言了一条“嘿嘿嘿”这样的内容。
所以说一切操作资源的请求,都不应该是GET请求。
防御方法:验证码,referer check检查请求是否是来自于合法的源,设置请求token

XSS:corss site scripting(跨站脚本攻击),就是将恶意的js代码插入到用户浏览的页面上,当用户浏览该页面的时候,恶意代码就会执行,从而达到获取cookie,会话劫持等目的
XSS的攻击方式:反射性攻击,存储型攻击
反射性攻击:发出请求时xss代码就出现在URl中,随内容一起发给服务器,服务器解析后随响应数据一起返回给浏览器,
最后浏览器解析xss代码,这个过程像一个反射,所以称为反射性攻击
存储型攻击:xss代码会存储在服务器的数据库中,等到下次请求目标页面时就不用再提交xss代码,而是直接从数据库中获取数据,这样用户就会执行xss代码。
基于dom的跨站脚本攻击:当用户能够通过交互修改浏览器页面中dom并显示在浏览器上时就会出现这种漏洞。
一种不安全的方式就是从document.location, document.referer中获取数据。
防御方法:过滤用户输入,对不可信的输出进行编码
6. 前端中常见的算法有哪些?各自的时间复杂度都分别是多少?
(1)冒泡排序
原理:比较两个相邻的元素,将值比较大的元素交换至右侧
思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。第一趟:比较第一个数和第二个数,将小数放前,大数放后,之后在比较第二个数和第三个数,同样是小数放前,大数放后。重复这一步骤,直到全部排序完成。
时间复杂度:O(n^2)
空间复杂度:O(1)
(2)选择排序
原理:每一次都从待排序记录中选出最小的元素,顺序放在已拍好序列的最后,直到全部序列排序完成。
思路:给定数组arr,第一次排序找出最小的数据,将其与arr[1]交换,第二次在待排序数据arr[2] ~ arr[n]中选出最小的数据,将其余arr[2]进行交换,以此类推,直到全部的排序完成。
时间复杂度:O(n^2)
空间复杂度:O(1)
(3)快速排序:
思路:一般是基于递归实现的,首先选定一个的值称为枢纽,实际中经常使用数组的第一个元素,基于这个值将数组分为两个部分,
较小的分在左边,较大的分在右边。这样一轮下来这个枢纽的位置就处于中间位置,对两个子数组重复上述过程,直到每个数组都只有一个元素。
时间复杂度:O(nlogn)
空间复杂度:O(nlogn)
(4)插入排序:
原理:插入排序的基本操作就是将一个数据插入到已经排好序的有序数组中,从而得到一个新的,个数加1的有序数组。
思路: 初始时,只考虑数组下标0处的元素,只有一个元素显然是有序的。
之后第一次对下标1的元素进行排序,保证数组[0,1]上的元素是有序的
第二次对下标2的元素进行排序,保证数组[0,2]上的元素是有序的
第n-1次对下标n-1的元素进行排序,保证数组[0,n-1]上的元素是有序的,这样整个数组就成为了有序数组了。
时间复杂度: N(n^2)
空间复杂度:O(1)
最好时间复杂度情况:如果插入的序列是完全有序的,那么插入排序只需要比较n次即可
(5)堆排序:
原理:堆排序是使用堆这个数据进行排序,堆排序是一个选择排序。堆分为大顶堆和小顶堆。
基本思路:将待排序序列构造成大顶堆,此时整个系列的最大值就是堆顶的根节点,将其余末尾元素进行交换,此时末尾元素就是最大值了。
然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
时间复杂度:它的最好,最坏,平均时间复杂度都是O(nlogn)
最快时间复杂度的情况:列表是正序或者逆序的时候,每次都会得到比前一次少一个记录的子序列
空间复杂度:O(1)
7. 请说一下JavaScript中的继承方式?
(1)原型链继承:主要的实现步骤是SubType.prototype = new SuperType(),原型链继承最重要的问题来自于引用类型,所有实例都共享应用类型的原型属性,一个实例对其作出改变,那么所有的实例都会发生变化。
(2)借用构造函数,这种技术的基础就是在子类型的构造函数内部调用超类型构造函数。
function subType() {
SuperType.call(this);
}
借用构造函数有一个很大的优势就是可以向父类型的构造函数传递参数,但是问题就是所有的函数都在方法中定义,函数复用无从谈起。
(3)组合继承:使用原型链实现对原型属性和方法的继承,使用构造函数来实现对实例属性的继承。
(4)原型式继承:借助原型可以基于原有的对象创建新的对象,同时还不必因此创建自定义类型
可以使用Object.create()来实现原型式继承
(5)寄生式继承:创建一个仅用于封装继承过程的函数,该函数可以在函数内部已某种方式来增强对象,最后就像是真的它做了所有工作一样返回对象
function createAnther(original) {
var clone = object(original);
clone.sayHi = function() {
alert("Hi");
}
return clone;
}
  (6)寄生组合式继承:它主要是用来解决组合继承无论什么情况下都要调用两次超类型的构造函数。
其基本思路:不必为了指定子类型的原型而调用超类型的构造函数,我们所需的不过是超类型原型的一个副本。
8. 请谈一下闭包及其使用场景?
闭包就是指有权访问另一个函数作用域的函数。创建闭包常见的方式就是在一个函数内部创建另一个函数。
闭包实际上就是延长了作用域链。当外部函数返回后,其执行环境的作用域链就会被销毁,但是它的活动对象仍然保存在内存中,直到匿名函数被销毁后,外部函数的活动对象才会被销毁。
闭包的使用场景:
(1)设计私有的方法和变量,这在做框架的时候体现很明显,有些方法和属性只是在运算逻辑过程中使用,我们不希望用户可以访问到这些数据,所以可以提供一个闭包来提供方法获取属性
(2)封装相关的功能集
(3)将函数关联到对象的实例方法
9. 浏览器事件代理的原理?
现代浏览器通常都支持标准事件流,而事件流分为三个阶段:事件捕获,目标阶段,事件冒泡。通常情况下事件的处理程序都是在冒泡阶段执行的。
事件代理就是原本应当是子元素绑定的事件却由父元素来绑定代替执行。利用就是事件冒泡的机制,利用事件委托可以大量节省内存占用,减少事件注册。
10. cookie,session,localStorage,sessionStorage之间的区别?
cookie和session都是用来跟踪浏览器用户身份的回话方式。
区别:
(1)cookie保存在客户端,但是session保存在服务器
(2)cookie只能保存字符串类型,但是session可以保存任何类型数据
(3)cookie存储大小最多4kb,但是session却没有限制
(4)安全性,cookie的安全性较低,session的安全性较高。这是因为sessionid是加密的,并且当用户再次登录的时候前一次的sessionId就失效了,所以在短时间内攻破加密的session是不易的。
(5)常用场景:cookie常用来判断用户是否登录,保存用户名和密码,保存上次登录的时间。
session是用来保存每个用户的专用信息,比如说购物车,用户登录信息。
cookie机制:如果没有在浏览器中设置cookie的过期时间,那么当浏览器关闭后该cookie就会消失。如果在浏览器中设置了过期时间,那么cookie就会保存在硬盘中,关闭浏览器后cookie依然存在,直到过期时间才会消失。
session机制:当服务器接收到建立session对象时,首先会检查客户端请求中是否包含sessionId。如果有则根据该sessionId返回session对象,如果没有sessionId,服务器会创建新的session对象,并将sessionId在本次响应中返回给客户端。
sessionId一般是使用cookie来存储的,在交互中按照浏览器规则sessionId发送给服务器。
webStorage是专门来克服cookie所带来的一些限制,当数据不需要被发送到服务器的时候,就将其保存在客户端。
localStorage和sessionStorage之间的区别:
(1)生命周期不同,localStorage是永久的,一旦保存在客户端,除非用户自己删除数据,否则该数据永远有效。sessionStorage是基于回话的存储,数据只有在同一个回话页面之间传递,如果将当前页面关闭或者关闭浏览器那么的话
sessionStorage数据就会被销毁
cookie和webStorage之间的区别:
(1)数据限制:cookie一般只能最多保存4kb的数据,但是webStorage却可以保存5mb的数据
(2)cookie是专用来在客户端和服务器交互的时候随着http header传到服务器,但是webStorage则是永久的保存在客户端,不会与服务器发生任何的交集
(3)cookie不好操作,因为没有提供专门操作cookie的api,所以需要开发者自己封装,但是webStorage都提供了一系列友好的api。
(4)cookie的安全性较差,由于webStorage是存储于客户端所以安全性基本没有什么问题。
11. 说一下webSocket,并且说一说长轮询和短轮询,长连接,短连接。
websocket是一种协议,主要作用是允许服务器和客户端进行全双工的通信。http协议是一种无状态的协议,服务器不具有识别客户端的能力,所以需要借助session或者cookie来识别客户端,才能与特定的客户端进行通信。
websocket协议可以向服务器端发送文本和二进制数据,并且使用组件的协议,WebSocket协议用ws表示。此外,还有wss协议,表示加密的WebSocket协议,对应HTTPs协议。
在客户端创建socket后,可以通过onerror,onclose,onmessage,onopen四个事件来对socket进行响应。
长轮询与短轮询:
短轮询:客户端按照一定时间定时给服务器发送请求查询是否有数据,服务器接收到请求后不管是否有数据变化都直接响应http请求
长轮询: 客户端会主动发送请求查询是否有数据,服务器接收到请求后会检查如果有数据就立即响应请求,如果没有数据就停留一段时间,如果该段时间过后还是没有请求那么就直接返回空的响应。
长轮询的缺点是server 没有数据到达时,http连接会停留一段时间,这会造成服务器资源浪费;
短轮询的缺点更加明显:每一次请求都要都是完整的http请求,会携带大量无用的数据段,大量的浪费了带宽和服务器资源,并且消息交互的实时性较差。
总的来说长轮询和短轮询都不适合用于客户端人数太多的情况。
短连接就是典型的建立连接-》发送数据--》断开连接。一次发送后立即断开连接。
长连接就是多个http请求都复用一个tcp连接,这样可以减少每次建立连接和断开连接的消耗。http1.1中设置了connection:keep-alive就是表示开启长连接
12. webpack的原理?
webpack打包原理:
(1)一切皆模块,不仅仅js文件是一个模块,css,html等都可以视为一个模块。这样的话就意味着可以把事务分割成更小的易于管理的片段,从而达到重复利用等目的。
(2)按需加载:webpack提供了很多特性来分割代码然后生成多个bundle文件,然后异步记载部分代码实现按需加载
(3)将所有依赖打包成一个bundle.js文件,通过分割代码成单元片段并按需加载。
13. 对前端框架的认识和理解?
目前前端框架主要由三大框架:vue,react,angularJs
https://cn.vuejs.org/v2/guide/comparison.html
14. 请说一下你理解的前端开发流程?
我理解的前端开发流程是这样的
(1)首先美工根据客户的需求设计出客户满意的效果图
(2)美工设计好的效果图,一般是psd文件给前端工程师,前端工程师根据效果来考虑如何实现,并开始切图
(3)之后使用html,css,js等技术来实现效果图的页面,同时跟后台人员交流后台数据的获取接口,这需要跟后台工作人员合作好
(4)最终前端完成页面交给后台,由后台人员填充后台数据,就基本上完成了一个流程

15. 请谈一下你对javaScript中的this关键字的理解?
this在js通常情况下值得是window对象,当this处于某对对象调用的方法中的时候,this总是指向调用当前函数的对象
this在构造函数中默认指向创建的新对象,并且为新的对象添加属性和方法,最后默认返回该对象
16. 请谈一下null和undefined之间的区别?
null表示没有对象,即该处不应该有值,典型的用法:
(1)作为对象原型链的终点
(2)作为函数的参数,表示该函数的参数不是对象
undefined表示缺少值,即该处应当有一个值但是没有定义,典型的用法:
(1)变量被声明了,但是没有赋值
(2)调用函数时,应该提供的参数没有提供
(3)对象没有赋值的属性,该属性的值为undefined
(4)函数没有返回值的时候,默认就返回undefined
17. css单位中的rem和em有什么区别?
两者都是相对长度单位,em相对的当前对象的文本的字体尺寸,而rem相对的是html根目录的字体大小
当我们修改rem的时候整个网页的字体大小都会发生变化。
18. 原生javaScript的方法有哪些?
(1)toFixed(),指定小数位数的数字,最后返回的是字符串
(2)toprecision(),用于将数字精确到指定长度,方法接收一个参数
(3)toString()
(4)valueof()
(5)hasOwnProperty
(6)isPropertyOf()
19. 如何理解html的语义化?
(1)HTML语义化就是为了让页面的内容结构化,便于浏览器和搜索引擎解析
(2)在没有css的情况下页面也以一种文档格式呈现,并且是易于阅读的
(3)便于阅读源码的人对网站分块,便于网站的后期维护
(4)搜索引擎的爬虫也是依赖于标记来确定各个关键字和上下文的权重,利于SEO
20. 如果去除事件的监听?
btn.addEventListener("click",someFunciton); 
    btn.removeEventListener("click",someFunciton);
21. JavaScript 和其他语言的面向对象的区别?
所谓的面向对象思维就是将解决问题的焦点放在解决问题的所需对象上
面向对象有三大特性:继承,封装,多态
首先封装不用说了,js和其它语言都一样,js是不支持多态的,在js中继承就是自己没有的功能拿来自己用了就是继承,而在其它语言中继承是有明确的父子关系的。
22. js创建对象的方法有哪些?
(1)工厂模式:用函数来封装以特定接口创建对象的细节,但是问题是对象识别
(2)构造函数模式:构造函数模式可以将它的实例表示为一种特定的类型,构造函数的问题是每个方法都要在实例上创建一遍
(3)原型模式:原型模式最大的问题就是引用类型的值,如果一个实例对应用类型的值做出来改变,那么所有的实例的引用类型值都会发生变化。这是极不科学的。
(4)组合使用构造函数模式和原型模式:构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性
(5)动态原型模式:把所有的信息都封装在构造函数中,在构造函数中初始化原型,
if (typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
在构造函数中书写上面的语句。
(6)寄生构造函数模式:除了使用new操作符和把操作的包装函数称为构造函数外与工厂模式没有什么不同的。
23. 请谈一下你对前端这个行业的看法?
前端开发它的工作包括了切图,写样式,图片切换效果,页面交互设计,对网站前端性能进行优化,
可实现代码的可维护性,组件的复用性和分层语义。现在前端工程师能做的工作越来越多,框架多如牛毛,选择一款适合的框架可以使开发变得便捷。
可以说前端现在处于迅速发展的阶段,但是高级的前端工程师比较少,由于前端入门比较容易,并且现在的培训机构很多,造成了前端初级人员很多的情况。
24. 对自己未来的规划?
我会一直在前端这条路上走下去,基本上我的规划是这样的如果我能加入贵公司,前三年是我积极增长知识和经验的时间,我会投入百分百的精力到工作中去,
到了第四年就算是个有经验的工程师了,这个时候我会向多方向发展,因为前端涉及的知识很多,包括后端通信,设计与交互,浏览器和网络优化,框架选型等并且逐渐的提升自己的名气,这段时间我技术2~3年的时间,
之后就应该是一个高级工程师了,这个时候我更希望做的是架构设计,项目管理等事情。这就是我的的职业规划。
25. 如何选择前端框架?
选择前端框架主要从两个方面来看,首先是框架的本领,之后是团队的本领。
框架的本领要看框架功能是否齐全,社区活跃度如何,文档是否齐全,是否及时更新,是否容易上手。框架的选择不求最好,但求最合适。
26. 你的优点和缺点是什么?
优点:我的适应能力和学习能力较强,做事认真细致,有良好的责任心和团队合作精神。
缺点:由于我经验少,所以拿到一份任务总是思考的少,总是急于完成任务,为此还我们实习的组长就还亲自找我谈话,告诉我说拿到任务首先要思考实现的技术是什么,做到胸有成竹后才能
动手去做,因为在开发中80%的时间其实是在修补漏洞,如果起始地基没有打捞的话后续的维护工作将会很难,甚至要重新再写一遍。
27. 你有什么想问我的?
你觉得这份工作所需的能力而言,我还有哪些不具备?
我想了解一下贵公司内部的培训机制和学习机制?
关于这个职位,你认为最重要的工作内容是什么?
这个职位在未来几年的职业发展是怎么样的?
28. 如何给元素添加事件?
第一种方式dom0级事件处理方式---直接将一个函数赋值给事件处理程序属性,就是onclick,onmousedown等这类
第二种方式是使用dom2级事件处理方式,addEventListener(name, function, boolean),主流浏览器都支持该函数,ie8以上
第三种方式就是IE的事件处理程序,attachEvent('on**', function)
29. Header头Set-cookie:http-only是干什么用的?
如果cookie中设置了httponly属性,那么通过js脚本将无法读取到cookie信息,这样能有效的防止xss攻击,窃取cookie内容
30. css BFC的原理?
BFC是块级格式化上下文,它是一个独立的区域,它决定了其内部元素如何布局及其与周围元素的关系
BFC的规则如下:
BFC在页面中就是一个独立的容器,容器里面的子元素是不会影响到外面的元素
BFC计算高度时,浮动元素参与计算
BFC区域不会与浮动元素重叠
属于同一个BFC内部的两个box的margin会发生重叠
如何生成BFC?
float属性不为none
overflow不为visible
position为absolute或者fixed
display的为inline-block, table-cell, table-caption, flex, inline-flex
31. 排序算法的稳定性与不稳定性?
冒泡排序,插入排序,归并排序,基数排序是稳定的,剩下的排序都是不稳定的。
32. 原型与原型链,它们各自有什么特点?
任何时候,只要创建了一个函数,就会为这个函数创建一个prototype属性,该属性指向该函数的原型对象。当通过构造函数创建了一个实例之后,该实例内部的指针就会指向构造函数的原型对象。
在chorme,safari和firefox中每一个对象都支持一个属性__proto__指向构造函数的原型对象。每当代码读取某个对象的属性时,会执行一次搜索,目标是给定的属性名。搜索首先从实例开始,如果在实例中找到了对应的属性名就直接返回属性值,
如果没有找到就继续搜索原型对象,在原型对象中找到了对应的属性名就返回对应的属性值,如果没有找到就返回undefined,这也是多个实例共享原型所保存的属性和方法的基本原理。
原型对象具有动态性,因此我们对原型对象的任何修改都能够立即从实例上反映出来。
原型链是作为继承的主要方法,我们之前提到实例中的__proto__指向了构造函数的原型对象,如果原型对象也等于另一个类型的实例,那么原型对象中也包含着一个指向另一个构造函数的指针,这样层层递进就构成了实例和原型的链条,这就是原型链的基本概念。
33. javaScript中的深拷贝和浅拷贝之间的区别?
浅拷贝就是只是复制对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,
深拷贝是创建了一个与源对象一模一样的对象,新旧对象不会共享同一块内存,修改新对象不会影响到源对象
浅拷贝的实现方式:第一种方式是使用for in循环,进行简单的复制
第二种方式是使用Object.assign(),可以实现浅拷贝
深拷贝的实现方式:
第一种方式:使用JSON.parse(JSON.stringify(obj)),可以实现一部分的深拷贝
第二种方式:递归实现拷贝
34. 三次握手和四次挥手?
第一次握手:客户端A将标志位SYN置为1,随机产生一个值为seq=J(J的取值范围为=1234567)的数据包到服务器,客户端A进入SYN_SENT状态,等待服务端B确认;


    第二次握手:服务端B收到数据包后由标志位SYN=1知道客户端A请求建立连接,服务端B将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给客户端A以确认连接请求,服务端B进入SYN_RCVD状态。


    第三次握手:客户端A收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务端B,服务端B检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端A和服务端B进入ESTABLISHED状态,完成三次握手,随后客户端A与服务端B之间可以开始传输数据了。
三次握手的目的是为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误
ack是确认序号,ACK是标志位,只有标志位有效时,确认序号字段才有效ack= seq+1
四次挥手:
第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
        第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
        第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
        第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
35. 实现一个 compose 函数,它接受任意多个函数作为参数(这些函数都只接受一个参数),然后 compose 返回的也是一个函数,达到以下的效果:
const operate = compose(div2, mul3, add1, add1)
operate(0) // => 相当于 div2(mul3(add1(add1(0))))
operate(2) // => 相当于 div2(mul3(add1(add1(2))))
-----------------------------------------------------------------------------
function compose() {
const len  = arguments.length;
const args =  arguments;
return function() {
let result = arguments[0];// arguments是一个对象,不能直接赋值,我总是以为是只有一个参数就直接赋值了,结果返回的是一个字符串类型
for(let i = len - 1 ; i >=0; i--) {
let fun = args[i];
result = fun(result);
}
return result;
}
}


function div2() {
return arguments[0] + 1;// 我犯错了,居然把尽管参数只有一个,但是也不能直接将arguments和数值相加,应当是取出数值后再加
}
function mul3() {
return arguments[0] + 2;
}
function add1() {
return arguments[0] + 3;
}


const operate = compose(div2, mul3, add1, add1);
console.log(operate(0));
36. 实现一个函数:fun(1).add(1).min(2).num // 最终输出为 -2
function fun() {
        let obj = {};
        obj['num'] = parseInt(arguments[0]) - 4;
        return obj;
    }
    Object.prototype.add = function() {
        this.num = parseInt(arguments[0]) + this.num;
        return this;


    }
    Object.prototype.min = function() {
        this.num = Math.min(parseInt(arguments[0]), this.num)
        return this;
    }


    var num = fun(1).add(1).min(2).num;
    console.log(num);// -2
37. 要实现将数字每三个分为一组的功能:/\B(?=(?:\d{3}+\b))/
38.es6箭头函数的this指向?
箭头函数中的this总是绑定定义时所在的作用域,而不是指向运行时所在的作用域
function Timer() {
  this.s1 = 0;
  this.s2 = 0;
  // 箭头函数
  setInterval(() => this.s1++, 1000);
  // 普通函数
  setInterval(function () {
this.s2++;
  }, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100); // 3
setTimeout(() => console.log('s2: ', timer.s2), 3100); // 0
上面代码中,Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this绑定定义时所在的作用域(即Timer函数),后者的this指向运行时所在
的作用域(即全局对象)。所以,3100毫秒之后,timer.s1被更新了3次,而timer.s2一次都没更新。
39. https客户端与服务器端连接的原理?
https的作用是为了客户端和服务器端之间通讯的安全性,实际上https是工作在SSL上的http协议,添加了SSL/TLS握手以及数据传输加密,所有的传输内容都经过加密,对称加密采用对称加密,
非对称加密的密钥用服务器方的整数进行了非对称加密。
SSL握手协议的四个阶段:
(1)建立安全能力,有客户端发起,向服务器发送Client hello消息,其中包含SSl的版本,客户端随机数(用于生成密钥),会话号,加密算法清单,压缩算法清单
服务器返回Serverhello消息,其中包含SSL版本,服务器随机数(用于生成密钥),会话号,选择加密的案案发,选择的算法,总之就是先互相打个招呼
(2)服务器认证与密钥交换
在这一阶段服务器是唯一的消息发送方,总共分为三步:
第一步:证书,服务器将服务器数字证书以及整个CA证书链发给客户端,客户端由此获取服务器公钥,客户端还可以在这一步选择是否验证服务器是否可信,不验证的话就直接跳过验证证书这一步,直接通讯
第二部是证书请求,服务器请求客户端数字证书,客户端认证在SSL中是可选的,因此这一步也是可选的
第三部是服务器握手完成,发送这个消息后,服务器就等待客户端响应。
(3)客户端认证与密钥交换
本阶段客户端是唯一的消息发送方,总共也是分为三步:
第一步客户端将客户端数字证书发送给服务器,里面包含了客户端的公钥,服务器可以检验客户端证书的合法性,以决定是否继续
第二步就是密钥交换,客户端生成预主密码,用服务器的公钥加密,然后发送给服务器。在这一步也间接验证了服务器,服务器必须拥有与证书对应的私钥才能解密预主密码”。
第三步证书验证,客户端还需要向服务器验证自己是真正的客户端(数字证书只能证明客户端拥有合法的公钥,但是不能证明它就是客户端,拥有与公钥对应的密钥才是关键)。
因此客户端已经把加密的预主密码,客户端含有签名的随机数和自己的证书发送给服务器。服务器通过验证CA的有效性合法性,随机数等等。若通过服务器将用自己的私钥解开加密的预主密码,然后执行一系列步骤来产生主通讯密码。
(4)完成,在这个阶段客户端和服务器都独立的生成相同的主通讯密码,也就是所谓的对称密钥。SSL握手之后就进入会话阶段,客户端和服务器使用握手协议中生成的对称密钥进行加密和解密以保证通信的安全
https很好的解决了http的三个缺点(被监听,被篡改,被伪装),
防监听:数据是加密的,所以监听得到的是密文,没有密钥是看不懂的
防篡改:https对数据做了摘要,篡改数据会被感知到,篡改了数据也是无用的
防伪装:没有证书就认为是非法,证书是由第三方颁布的,很难伪造。
40. 常见的css布局方式有哪些?
弹性布局:总体宽度和所有栏的值都以em单位来编写
固定布局:页面中所有的单位都是使用px来写的
流体布局:页面中总体宽度和所有栏的值都是以百分比来编写的
混合布局:对于前面介绍的几个布局任意的组合
绝对定位布局:页面中的元素大多使用浮动定位,来实现页面效果。
41. https有哪些薄弱的地方容易被攻击?
SSL挟持就是SSL证书欺骗攻击,攻击者为了获取https传输的明文数据,需要先将自己接入到浏览器和目标网站之间(中间人),在传输的过程中,替换目标网站发给浏览器的证书,之后解密传输的数据
SSLStrip攻击也需要将攻击者作为中间人,之后将https访问替换为http返回给客户端,由于http传输是没有加密的,所以就可以直接获取用户访问的数据
第三类攻击完全就是软件厂商在设计的时候忽略了安全问题
42. TCP Socket连接的原理?
建立socket连接分为三个步骤:服务器监听,客户端请求,连接确认
服务器监听:服务器端套接字并不定位具体的客户端套接字,而是出于等待连接的状态,实时监控网络状态,等待连接
客户端请求:客户端的套接字发出连接请求,要连接的目标就是服务器端的套接字
连接确认:当服务器端套接字收到客户端套接字的请求之后,会响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。
43. 前端工程师的职责是什么?
首先应该是使用负责产品的页面开发和页面制作
然后是熟悉w3c标准和各大浏览器在前端开发中的差异性,能够处理这些差异性
负责相关产品产品的需求,提供可行的前端架构
与产品,后台开发人员保持良好的沟通,能够快速的理解各方需求,落实开发工作
了解服务器端的相关工作,在交互体验和设计方面有自己的见解
44. 对前后端交互有什么认识?
首先与后端交互的就是ajax和json,ajax是一种前端交互的方式,json是一种描述前后端数据交互的特定格式。
同时还要了解一些http的知识,明白get和post,明白什么是header
最后麻烦的还是接口,要根据不同的业务来制定不同的接口。
45. 常用的前端工具有哪些?
前端自动化构建工具:gulp,webpack
css预处理器:sass,less,stylus
javaScript模板引擎:ejs
chorme调试工具,chorme插件
46. 类似于微信和微博的扫码登录是如何实现的?
我们就以微信登录为例、
(1)用户A访问微信网页版,微信服务器为该会话生成一个唯一的id
(2)用户打开微信扫描该二维码,并确认是否登录
(3)用户点击确认之后,手机上的微信客户端会将微信账号和扫描得到的id一起提交到服务器
(4)服务器将这个id和用户A的账号绑定,并通知网页版微信,这个id对应的微信账号就是用户A的账号,网页版微信就会加载用户A的信息
47. flex布局的应用场景?
flex布局主要应用于移动端。
常用的场景有元素水平垂直居中,等分布局,多列自适应布局,悬挂布局等
48. webpack常用的插件?
(1)uglifyJsPlugin,用来解析/压缩/美化所有的js文件
(2)autoprefixer,自动补全css3前缀
(3)自动生成html,html-webpack-plugin
(4)拷贝资源,copy-webpack-plugin
(5)全局挂载插件,ProvidePlugin
49. 对AMD和CMD和CommonJs的认识?
前端模块化带来的好处不仅仅是代码模块那么简单,版本更新,多人协作等,一份模块配置清单,管理整个项目的代码和关系,
为项目代码带来了高度的逻辑分离和思维的提升,另外模块平台统一入口也是迟早的事情。
AMD/CMD/CommonJs都是js模块化开发的标准,目前对应的实现分别是RequireJS/Seajs/nodeJS
CommonJs主要针对的服务器,Amd、CMD主要针对的是浏览器端。
服务器端一般采用同步加载机制,也就是如果需要加载某一个模块,服务器便停下来,等待它加载再执行,浏览器端为了保证效率,一般采用异步加载
对于依赖的模块,AMD是提前执行,CMD是延迟执行。
CMD推崇依赖就近,amd推崇依赖前置
// CMD
define(function(require, exports, module) { 
var a = require('./a')  
a.doSomething()  
// 此处略去 100 行  
var b = require('./b') // 依赖可以就近书写  
b.doSomething()   // ... 
})
// AMD
define(['./a', './b'], function(a, b) {  // 依赖必须一开始就写好
a.doSomething()
// 此处略去 100 行
b.doSomething()
...
}) 
大家都叫sea.js为懒加载,也就是as lazy as possible,对加载的模块是提前读取并不加载,需要时再加载
AMD的API默认是一个多用,比如说require在AMD里面就分为全局require和局部require。而cmd的api严格区分,推崇职责单一,每个APi都比较纯粹。
AMD成为异步模块定义,AMD规范定义了一个全局变量define的函数:define(id?dependencies?factory),id为字符串类型,表示模块标识,dependencies为当前模块依赖的数组字面量,factory是一个需要进行实例化的函数或者对象
AMD的require()分为全局require和局部require,局部的require需要在AMD模式中的define工厂函数中传入require,而全局的require像define一样。
requireJs是一个前端的模块化管理的工具库,遵循AMD规范。其基本思想就是通过一个函数来讲所有需要的或者所依赖的模块都装载进来,然后返回一个新的函数或者模块。
CMD叫做通用模块定义,在cmd中一个模块就是一个文件,define(factory),factory可以是一个函数也可以是一个对象或者字符串。require是factory的第一个参数,它是接受模块标识的唯一参数,用于或者其他模块提供的接口,
exports是factory的第二个参数,主要用来对外提供模块接口,module是factory的第三个参数,为一个对象上面存储了一些与当前模块相关联的属性与方法。
sae.js遵循CMD规范,seasj.use()用来在页面中加载一个或者多个模块。
50. 前端跨域的方法有哪些?
(1)document.domain + iframe,只有在主域相同的时候才使用该方法
在www.a.com/a.html中:
document.domain = 'a.com'
var ifr = document.createElement('iframe');
ifr.src = 'http://www.script.a.com/b.html';
ifr.display = none;
document.body.appedChild(ifr);
ifr.onload = function() {
var doc = ifr.contentDocument ||ifr.contentWindow.document;
// 在这里操作doc,就是b.html
ifr.onload = null;
}
在www.script.a.com/b.html中:
document.domain = 'a.com'
(2)jsonp
script标签是不受同源策略的限制的
function loadScript(url, func) {
var head = document.getElementByTagName('head')[0];
var script = document.createElement('script');
script.src = url;
script.onload = script.onreadystatechange = function() {
if(!this.readyState || this.readyState == 'loaded' || this.readyState = 'complete') {
func();
script.onload = script.onreadystatechange = null;
}
}
head.insertBefore(script, 0);
}
(3)location.hash + iframe
原理是利用location.hash来进行传值
假设域名a.com下的文件cs1.html要和cnblogs.com域名下的cs2.html传递信息
1)cs1.html首先会创建一个隐藏的iframe,iframe的src指向cnblogs域名下的cs2.html
2)cs2.html响应请求之后在通过修改cs1.html的hash值来进行传递信息
3)同时在cs1.html文件中设置一个定时器,隔一段时间就检查location.hash是否发生了变化,如果发生了变化就直接获取hash值
(4)window.name + iframe
window.name的美妙之处在于name值在不同的页面加载之后依旧存在,甚至可以支持非常长的name值
(5)postMessage
在a.com下的index.html中:
<iframe id='lfr' src = 'b.com/index.html'></iframe>
<script>
var ifr = document.createElement('iframe');
var targetOrigin = 'http://b.com';
ifr.contentWindow.postMessage('I was there', targetOrigin)
</script>
在b.com/index.html中:
<script>
window.addEventListener('message', function(event) {
if(event.origin = 'http://a.com') {
alert(event.data);
alert(event.source);
}

})
</script>
(6)CORS
(7)webSockets
只有在支持web socket协议的服务器上才能正常工作。
var socket = new WebSockt('ws://www.baidu.com');//http->ws; https->wss
socket.send('hello WebSockt');
socket.onmessage = function(event){
var data = event.data;
}
51.前端如何调试bug?
在前端开发中常用的调试技术就是console,可以把自己感到疑惑的地方打印出来,看看结果是否正确,一般使用console就可以解决问题
js断点调试:即在浏览器开发者工具中为js代码添加断点,让js代码执行到某一特定位置就停住,方便开发者对该处代码段的分析和逻辑处理。
js断点调试主要分为两部分:一部分是直接在开发者工具里面source调试,另一部分是debugger调试,两者的功能是一样的。
Dom断点调试:在dom元素上添加断点,进而达到调试的目的。
当节点内部子节点发生变化时断点(Break on subtree modifications)
当节点属性发生变化时断点(Break on attribute modifications)
当节点被移出时断点(Break on node removal)
xhr breakPoints,专为异步而生的断点调试功能
事件监听器断点
52. 一个数组中有1000个数字,如何找出第K大的数字?
解法一:首先对该数组进行降序排序,然后返回第k个元素就行了
解法二:利用选择排序,k次选择你后即可得到第k大的数字
解法一:
function getArr(num) {
           let result = [];
           for(let i = 0; i< num;i++) {
               let temp = Math.floor(Math.random() * num);
               result.push(temp);
           }
           return result;
        }
        function getNum(index) {
            const count = 100;
            let arr = getArr(count);
            arr = arr.sort(function(a,b) {
                return b - a;
            })
            console.log(arr);
            return arr[index-1];
        }
        var result = getNum(2);
        console.log(result);
解法二:
function getArr(num) {
            let result = [];
            for(let i = 0; i < num; i++) {
                let temp = Math.floor(Math.random() * num);
                result.push(temp);
            }
            return result;
        }
        function getIndex(index) {
            const count = 100;
            let arr = getArr(count);
            let maxIndex = 0;
            let result;
            for(let  i = 0; i < count - 1; i++) {
                maxIndex = i;
                for(let j = i + 1; j < count; j++) {
                    if(arr[j] > arr[maxIndex]) {
                        maxIndex = j;
                    }
                }
                let temp = arr[maxIndex];
                arr[maxIndex] = arr[i];
                arr[i] = temp;
                if( i === index -1) {
                    result = arr[i];
                    console.log(arr);
                    break;
                }
            }
            return result;
        }


        let result = getIndex(2);
        console.log(result);
53.css如何实现动画?
主要使用的是css3中的animation属性
@keyframes是用来创建动画的,在@ketyframes中规定新的css样式,就能创建由当前样式逐渐改为新样式的动画效果
animation动画的属性:
animation-name:@keyframes动画的名称
animation-duration: 动画一个周期所花费的秒
animation-timing-function: 动画的速度曲线
animation-delay:动画何时开始
animation-play-state:动画是否正在运行或者暂停
animation-direction: 动画是否在下一个周期逆向的播放
animation-iteration-count: 动画播放的次数
在一个元素的css属性中规定了这些就可以实现一个动画。
54. es6箭头函数的作用?
es6的箭头函数主要是用来解决this指针的问题。因为箭头函数的this绑定的是定义时的作用域,而不是运行时的作用域,因此this指针都不会出现指向其它对象的问题,大大的解决了我们开发中的this作用域问题
55. 用过哪些前端框架,评价一下优劣势?
前端框架:vue,AngularJs,,react,BootStrap,zepto,quick ui
quick ui表格组件一直深受用户好评,拥有良好的性能和兼容,新版本不但让表格样式更加美观,还扩展了大量的功能特性。
56. 常见的设计模式有哪些?
(1)单例模式:如果一个类只能创建一个实例,那么这个类就叫做单列类,这种模式就叫做单例模式
(2)简单工厂
(3)工厂模式
(4)抽象工厂
(5)代理模式
(6)命令模式
57. 对es6的理解?
(1)let和const命令
let声明的变量只在其所在的代码块内有用,for循环的计数器很适合使用let命令
let声明的变量不存在变量提升,必须先声明后使用,
let声明的变量存在暂时性死区,这是因为es6明确规定,如果在区块中存在let和const,那么一开始就形成了封闭性作用域,凡是在声明前使用这些变量就会报错。
在块级作用域内声明的函数都是采用函数表达式的形式,不能采用函数声明语句
const保证的是变量所指向的地址不可改变,而不是对象本身,对于复合型数据来说,可以增加或者删除属性或者方法,只要地址不改变就可以了,但是不能为该对象重新赋值,这样的话就改变了地址。
(2)变量的解构赋值
ES6允许按照一定模式,从数组和对象中获取值,对变量进行赋值,这种方法称为解构赋值
如果解构不成功,变量的值就是undefined
解构赋值允许有默认值,如果一个数组成员严格等于undefined的话,那么默认值就会生效
对象的解构赋值属性的次序是没有要求的,但是需要与属性同名
(3)模板字符串
(4)map和set数据结构
(5)箭头函数
(6)Promise对象
58. 为什么做前端?
首先这是我自己选择的一份职业,因为当时正是选择后面人生的时候,如果准备考研就要好好准备考研,如果不准备就要选择一个就业方向。我当时上网查找了我和我相关的一些职业,发现当时网上对前端的评价都不错,
大部分人都认为前端的发展情景很好,是一个不错的职业,所以我就选择了前端。同时看着自己写出来的一个个页面就很有成就感,然后慢慢的就想写的更漂亮,交互效果越多,就开始努力学习css和js,随着页面越来越复杂,
    就需要学更多的东西,追求更高的代码质量,就是这样的良性循环促使自己在前端道路上一直走下去。
59. 什么是AJax?其原理是什么?
ajax就是异步的javaScript和xml,其原理简单来说就是使用XMLHTTPRequest对象向服务器发起请求,从服务器获取数据,然后用javaScript操作dom来局部更新页面。
60. 了解web worker?
webworker是运行在后台的javaScript,不会影响页面的性能,可以用来解决主线程阻塞的问题,处理一些比较耗时的计算
postMessage是用来向主线程发送消息,onmessage用来接收消息,terminate()用来终止一个worker的执行
61. 实现vue双向数据绑定的原理?
vue采用Object.defineProperty()来劫持各个属性的getter和setter,在数据变动时发布消息给订阅者,触发相应的监听回调。
vue.js最核心的两个功能就是:响应式的数据绑定系统和组件系统。
实现响应式的数据绑定系统我们可以该任务分为三步:
(1)输入框和文本节点和data中的数据绑定
要实现该功能需要使用到DoucmentFragment,vue在编译时,会将过载目标的所有子节点都劫持(真的是劫持,通过 append 方法,DOM 中的节点会被自动删除到documentFragment中,经过一番处理后,再将DocumentFragment整体返回插入挂载目标
劫持子节点后,会解析所有子节点的属性,根据是否为v-model和文本节点,将data中的值绑定到对应的节点中。
(2)输入框内容发生变化时,data中的数据同步变化,实现view---》model
当我们在输入框输入数据时,首先会触发input事件,在相应的时间处理程序中,我们将获得输入框的value赋值给vm实例的text属性,我们会利用defineProperty将data中的text设置为vm的访问器属性
(3)当data中的数据发生变化的时候,文本节点中的内容同步变化,实现model--》view
这里才用到了订阅/发布模式,当text属性发生变化之后,要发布通知,触发订阅者的update()方法,实现视图更新。
实现步骤:
(1)创建一个数据监听器,利用Object.defineProperty()方法对所有属性的getter和setter,进行监听。并且创建一个可以容纳订阅者的消息订阅器Dep,订阅器Dep主要负责收集订阅者,然后在属性变化的时候执行对应订阅者的更新函数
(2)创建订阅者Watcher,订阅者主要有一个update()方法,可以用来实现当属性发生变化时,对应的视图也发生更新
(3)实现一个解析器来解析和绑定工作。解析器的实现步骤:首先解析模板指令,并替换模板数据,初始化视图,之后将模板指令对应的节点绑定到对应的更新函数,初始化对应的订阅器
首先会将所有带操作的dom节点都提取出来,放入到一个fragment片段中,待dom处理完成之后再一次性的插入到dom中。然后会遍历每一个节点,针对有指令的节点进行特殊处理。节点一般分为两大类:元素节点和文本节点。对于文本节点而言,其主要是完成model---》view的单向数据绑定。我们会设置一个针对{{}}的正则表达式,如果对应的节点符合,
那么将获取对应的属性值,更新到页面中,同时创建该节点的订阅器。而对于元素节点而言,又分位两大类:input元素和事件元素,对于事件元素我们是获取其对应事件类型,然后针对该节点调用AddEventListener方法为该节点添加对应的事件处理函数。而对于input元素而言,首先会获取对应的v-model的值,更新到页面中,之后创建该input元素的
订阅器对象,然后为该input元素添加input或者change方法当input内容发生变化的时候,会实时的更新到数据源,实现视图和数据源同时变化。
这样就实现了Vue的双向数据绑定。
62. 虚拟dom的原理?
相比较于原生对象,原生的js对象处理起来更快,更简单。我们可以使用js对象轻易的表示出dom树上的结构和属性信息。当状态变化的时候,重新渲染这个js的对象结构。
然后用新渲染的对象树与旧的树进行对比,记录两棵树的差异,差异的地方就是页面真正的dom操作,将它们应用在真正的dom树上,页面就更新了。Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。
所谓的virtual dom算法:
(1)用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
(2)当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
(3)把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了
63.对比两棵树的差异才是最关键的部分,这里就是虚拟dom的diff算法
一般来说,两棵树之间的diff算法是O(n^3),但是前端很少跨级移动dom元素,所以只对同一层的元素进行比较
(1)深度优先遍历,记录差异
实际代码中,会对新旧两棵树进行一个深度优先的遍历,每遍历到一个节点就把该节点与新的节点进行对比,如果有差异的话,就记录到一个对象中。
(2)差异类型
节点的差异很多,在vue中我们定义了几种差异类型:
var replace = 0;
var reorder = 1;
var props = 2;
var text = 3;
(3)列表的对比算法
现在知道了新旧的顺序,问题就是字符串的最小编辑距离问题,常用的解决方法就是Levenshtein Distance,通过动态规划求解,时间复杂度为 O(M * N)。但是我们并不需要真的达到最小的操作,我们只需要优化一些比较常见的移动情况,牺牲一定DOM操作,让算法时间复杂度达到线性的(O(max(M, N))。
(4)将差异应用到真正的dom树上
我们可以对dom树进行深度优先遍历,遍历的时候从步骤二中生成的patches对象中找出当前遍历的节点差异,然后进行dom操作
diff算法有以下三大策略:
(1)dom节点跨层级的移动发生频率很低,所以一般只会对同层级的节点进行比较
(2)拥有相同类的两个组件生成类似的树形结构,拥有不同类的两个组件生成不同的树形结构
(3)对于同一层级的一组子节点,通过唯一id进行区分,就是key值。
节点的比较分为两类:节点类型不同,节点类型相同,属性不同。
节点类型不同:
当在树中的同一位置输出不同的类型的节点,react会删除前面的节点,然后创建新的节点。删除节点就意味着彻底销毁该节点,如果该节点有子节点,那么所有的子节点都会被删除。
当在同一位置遇到不同的组件时,react会销毁第一个组件,并把新的组件的子节点都添加到新的组件上。
节点类型相同:
react会对属性进行重设来实现节点的转换
对于列表节点主要是依赖每个节点的key值就可以对节点做唯一的标识,完成一般的操作
Vue的diff实现:
首先会设计oldStart,oldEnd和newStart,newEnd两队指针,分别指向新旧dom的起点和终点。
整个diff分为两个部分,第一部分是循环,循环内部是一个分支逻辑,每次循环都会进入一个分支,每次循环会处理一个节点,处理之后将节点标记为已处理(新旧dom树中都要进行标记)。
标记的方法有两种,当节点正好处在指针处时就移动指针将其排除在未处理列表之外,否则的话就将节点设置为undefined。
第二部分循环结束之后,如果newVdom中还有为处理的节点,就是新增的节点做新增处理,如果oldVdom中有未处理的节点,则表示这些是需要删除的节点,相应的在dom树中删除。
在第一部分循环中首先要处理头部尾部头尾的节点,这样待处理的节点少,减少了待操作的处理范围,性能也得到提升。
64. vuex
65.前端开发中常用的图片格式?
一般来说我们使用的图片就是jpg,png和gif三种图片,png又分为png-8,png-24,png-32,它们的压缩方式都是无损压缩,gif的压缩方式也是无损压缩,通常用来实现一些小的动画效果,jpg的压缩方式是有损压缩,压缩图片时会把图像分为8*8的栅格,然后对每个栅格都进行压缩处理。
有必要了解一下矢量图和位图:矢量图就是一种缩放,旋转不失真的图像格式,无最小单位。存储的文教较小,很难表现出色彩丰富逼真图像效果。常用的图片格式为AI,CDR,RAW等。位图是一种缩放,旋转会失真的图片格式,最小单位是像素,图片色彩比较丰富,常用的图片格式为png,jpg,gif,bmp等。
同时还要了解一下有损压缩和无损压缩:有损压缩就是对图像本身的改变,在保存图像时保留了较多的亮度信息,而将色相和周围的像素进行合并,合并的比例不同,压缩的比例也不同,由于信息量减少了,压缩比可以很高,图片质量也会相应的下降。
无损压缩就是对文件本身的压缩,和其它数据文件的压缩一样,是对文件的数据存储方式进行优化,采用某种算法表示重复的数据信息,文件可以完全还原,不会影响文件内容。
总的来说,每种图片格式都有自己的优缺点,没有最好的图片格式可以适应所有的开发场景,下面是一些总结:
打印图形:
TIFF是打印行业专业人士的最佳和唯一选择
web图形:
png,jpg,gif是三种最友好的图片格式,当文件较小时,需要上传图片,使用jpeg也是可以的
pc和mac兼容:
使用jpeg是最好的
logo和艺术线条:
jpeg是最坏的选择,它往往会增加模糊的文字,线条和边缘。
剪贴画:
gif是最好的图片格式,它只是用很少的色彩和精确的线条和形状。
66. 移动端性能优化有哪些?
加载优化,对于移动端的网页来说,加载过程是最耗时的过程,可能会占到总耗时的80%的时间,因此是优化的重点。
(1)减少http请求,首次加载同时请求书不能超过4个
1.1 合并css,js文件
1.2 合并小图片,使用css精灵
(2)缓存,使用缓存可以减少向服务器请求的次数,节省加载时间,所以所有静态资源都要在服务器进行设置缓存,尽量使用常cathe
2.1 缓存一些可缓存的资源
2.2 使用唱cathe
2.3 使用外联式css和js
(3)压缩HTML,css,js
3.1 启用gzip
3.2 压缩
(4)按需加载,将首屏不需要的资源和本页面不用的资源等到需要使用的时候再加载
4.1 lazyload
4.2 滚屏加载
(5)首屏加载,首屏的快速显示可以大大提升用户对页面速度的感知,因此尽量针对首屏加载进行优化
(6)异步加载第三方资源,
(7)减少cookie,cookie会影响页面的加载速度,所以静态资源域名下不使用cookie
(8)避免重定向,重定向会影响加载速度,所以服务器应当正确设置避免重定向
脚本优化:
脚本处理不当也会影响页面的加载和渲染。
可以避免使用空的src和href,空的src和href会影响当前页面的速度
Script等中多次重置图片大小,多次重设图片大小会引发图片的多次重绘,影响性能。
使用touchstart,touchmove来代替click事件
尽量使用事件代理,避免批量绑定事件
减少重绘和回流:
(1)、避免不必要的Dom操作
(2)、尽量改变Class而不是Style,使用classList代替className
(3)、避免使用document.write
(4)、减少drawImage
css优化:
尽量避免在HTML标签中使用style属性
避免使用css表达式
移除空的css规则
不滥用float,float在渲染时计算比较大,尽量减少使用
标准化各种浏览器前缀
不声明过多的font-size,过多的font-size会引发css树的效率
渲染优化:
1.HTML使用viewport
2.动画优化:
使用css3代替js动画
合理使用requestAnimationFrame动画来代替setTimeout
适当使用canvas动画
3.GPU加速(CSS3 transitions、CSS3 3D transforms、Opacity、Canvas、WebGL、Video)
4. 高频事件优化:
增加响应变化的时间间隔,减少重绘次数
使用requestAnimationFrame监听帧变化,使得在正确时间进行渲染
67. 箭头函数的应用场景?
箭头函数适合应用于无复杂逻辑或者无副作用的纯函数场景下
箭头函数的最大作用就是this的绑定,this指向的是函数定义时的作用域,而不是运行时的作用域,可以大大减少作用域的识别度
另外箭头函数的另一大好处就是简洁。如果将箭头函数应用于多层函数嵌套中,那么就会影响函数的作用域范围的识别度,不建议在这种情况下使用箭头函数。
68. Promise的特点和实现方式?
所谓的Promise就是一个容器,里面保存着将来要执行的事件的结果。从语法上来说,Promise就是一个对象,从它可以获取异步操作的消息。
Promise对象有两个特点:
(1)对象的状态不受外界的影响。promise对象代表一个异步操作,有三种状态:pending(进行中),resolved(已完成),rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,其它任何操作都无法改变这个状态。
(2)一旦状态改变,就不会再变,任何时候都可以获得这个结果。Promise对象的改变只有两种可能,就是从Pending到resolved,或者是从Pending到rejected。只要这两种情况发生,状态就你凝固了,不会再改变了,会一直保持这个结果。
就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果
Promise对象也有缺点,就是无法取消Promise,一旦创建了它就会执行,无法中间取消。另外就是如果不设置回调函数,Promise内部抛出的错误,不会反应到外部来。
    实现方式:
ES6规定Promise就是一个构造函数,用来生成promise实例
const promise = new Promise(function(resolve, reject) {
  // ... some code


  if (/* 异步操作成功 */){
resolve(value);
  } else {
reject(error);
  }
});
(2)Promise创建后会立即执行,然后then方法中指定的回调函数,会在当前脚本所有同步任务执行完后再执行。
    调用resolve和reject并不会终结函数的执行,后面的语句依然可以执行,一般情况下最好在它们前面加一个return语句,这样就不会有意外了。
(3)Promise.prototype.then
then的作用就是为Promise实例添加状态改变时的回调函数。then方法返回的是一个新的Promise实例,所以可以链式调用
(4)Promise.prototype.catch
catch用于指定发生错误时的回调函数。如果Promise的状态已经变成了resolved,那么再抛出错误是无效的。因为Promise的状态一旦确定,就不会再发生改变。
Promise对象的错误有冒泡的性质,会一直向后传递,直到被捕获为止。
一般建议在Promise对象后面跟一个catch方法,之后在跟then执行回调函数,Promise对象抛出的错误不会传递到外部,外层代码不会有任何反应。
(5)Promise.prototype.finally()
finally方法用于指定不管Promise对象最后状态如何,都会执行的操作,它是ES2018引入的标准。
finally方法的回调函数是不接受任何参数的,所以就意味着finally是与状态无关的,不依赖于Promise的执行结果
(6)Promise.all方法用于将多个Promise实例包装成一个Promise实例。
Promise.all()方法接收一个数组作为参数,该数组元素都是Promise的实例。
const p = Promise([p1,p2,p3]);p的状态由p1,p2,p3来决定,只有p1,p2,p3的状态都变成resolved的时候,p的状态才会变成resolved,此时p1,p2,p3的返回值组成一个数组,传递给p的返回值。
但是只要p1,p2,p3中有一个的状态变成rejected,那么p的状态就会变成rejected,此时第一个reject的实例的返回值会传递给p的回调函数。
(7)Promise.race()
Promise.race()也是将多个Promise实例包装成一个Promise实例。只有参数数组中的某一个值发生了变化,那么返回值就会发生改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
(8)Promise.resolve()
Promise.resolve()方法将现有对象转换成Promise对象。
参数如果是一个对象且有then方法,那么Promise.then会将这个对象转为Promise对象,然后立即执行then方法
如果参数是一个原始值,那么Promise.resolve()会将其变成一个新的Promise对象,状态为resolved,并且立即执行回调函数。
如果不带任何的参数那么就直接返回一个resolved状态的Promise对象
(9)Promise.reject()
该方法会返回一个新的Promise实例,该实例的状态时rejected
69. forEach的特殊之处:
var test = [{
        name: 'qiang.liu',
        address: 'shanghai'
    }, {
        name: 'echo',
        address: 'beijing'
    },{
        name: 'huahua',
        address: 'qiangjiao'
    }];
    test.forEach(function(item) {
        item = {name: 'xxx'}
    });
    console.log(test);
    // forEach不能直接修改每一个item项,即使修改了也不会发生任何改变
    test.forEach(function(item,index,arr){
        arr[index] = {name: 'xxx'};
    });
    // 但是可以通过数组的索引来修改原始的数组
    console.log(test);

test.forEach(item => item.name = 'jack')
    console.log(test);
// 如果数组项是一个对象,那么修改该对象的某一个属性是可以的,上面的语句操作之后,所以子项的name属性就都变成了jack
var result = test.map(function(item) {
        return item.name = 'jack';
    });// ['jack','jack','jack']
// return语句是一个赋值语句时,返回的是赋值语句右边的值,也相当于整个赋值语句的返回值
70. Async/Await的使用场景?
首先了解一下基本的用法:
async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
async函数有多种使用形式:函数声明,函数表达式,对象的方法,class方法,箭头函数。
async函数返回一个Promise对象,async函数内部return语句返回的值,会成为then方法回调函数的参数。
async函数内部的所有异步操作都执行完成之后,才会执行then方法指定的回调函数。async返回的Promise对象,必须等到内部所有await后面的promise对象都执行完,才会发生状态改变。
await命令:
一般来首await后面是一个Promise对象,如果不是也会将其转换为立即resolve的Promise对象。
如果await后面的promise对象的状态变为reject,那么reject的参数会被catch方法的回调函数接收到
只要有一个await后面的Promise对象的状态变成reject,那么整个async函数就会中断执行。
处理错误的方法一种方式是将语句放在trycatch代码块中,另一种方法就是在await后面的Promise对象后添加catch方法用来处理可能的错误事件
await命令只能用在async函数中,如果用在普通函数中,就会报错
await后面的表达式有以下几种情况:
1.如果是一个Promise对象,并且其以值x被resolved,那么就返回x
2.如果是一个Promise对象,并且抛出异常,那么eject的参数会被catch方法的回调函数接收到
3.如果是一个普通值,那么就会将其转换为立即resolve的Promise对象,并且返回表达式的值
总的来说,async/await就是用来处理异步逻辑的,在前端开发中可以用其来处理异步流程。
71. Iterator(迭代器),Generator(生成器)的用法?
迭代器是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。
Iterator提供三种作用:
首先是为各种数据结构提供统一的访问机制
其次是使得数据结构的成员能够按照某种次序排列
第三ES6创造了一种新的遍历命令:for....of命令,Iterator接口就是主要供For....of来使用
Iterator的遍历过程是这样的:
(1)创建一个指针对象,指向当前数据结构的起始位置。
(2)第一次调用指针对象的next()方法,可以获得当前数据结构的第一个成员
(3)第二次调用指针对象的next()方法,可以获得当前对象的第二个成员
(4)不断调用指针的的next(),直到指向数据结构的结束位置。
每调用一次next方法,就会返回数据结构的当前成员信息,具体来说包含value和done两个数据,value是当前成员的值,done是一个布尔值,表示遍历是否结束。
一种数据结构只要部署了Iterator接口,就认为其是可遍历的。
ES6规定默认的Iterator接口部署在数据结构的Symbol.iterator属性,Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。
ES6的有些数据原生就部署了Iterator接口,这些数据结构是:Array,map,set,string,nodelist对象,调用这个接口,就会返回一个遍历器对象。其它没有数据结构的iterator接口需要自己在Symbol.iterator属性上部署。
调用Iterator的接口的应用场合:
(1)解构赋值,对数组和set结构进行解构赋值,会默认调用Symbol.iterator方法
let set = new Set().add('a').add('b').add('c');
let [x,y] = set;
// x='a',y='b'
let [first,...rest] = set;
// first='a',rest=['b','c'];
(2)扩展运算符
扩展运算符默认也会调用Iterator接口
var str = 'hello'
[...str];//['h','e','l','l','o']
(3)由于数组遍历调用了遍历器接口,所以凡是使用到数组遍历的地方都用到了遍历器接口
字符串的iterator接口,字符串也是一个类似数组的对象,也原生拥有Iterator接口
var str = 'hi'
var iterator = str[Symbol.iterator]();
iterator.next();
iterator.next();
iterator.next();
iterator.next();
for .....of循环
ES6引入了for...of循环,作为遍历数据结构统一的方法。
数组的for...of循环只会返回数字索引的属性。

Generator为生成器,执行Generator函数会返回一个迭代器对象,Generator函数不仅仅是状态机,还是一个遍历器生成函数。返回的遍历器对象,可以依次调用Generator对象的每一个状态。
调用Generator函数后,返回的是一个指向内部状态的指针,必须调用next()方法才能使指针移向下一个状态。每次调用next()方法函数都是从上一次停下来的地方或者函数头部进行开始执行,
直到遇到下一个yield或者return语句停止。
yield表达式:
yield表达式就是暂停标志,yield后面的表达式,只有当调用next()方法,内部指针指向当前语句时才会执行。
yield表达式相比return语句有位置记忆的功能,下次执行会从本次yield语句开始执行,并且一个函数中一般只能有一个return,但是generator函数中却可以有多个yield
yield表达式只能用在generator函数中,如果用在其他地方会出错。
yield表达式如果要用在另一个表达式中,则必须包含在圆括号中。
next方法的参数:
yield表达式本身是没有任何返回值的或者时候一直返回undefined。next()方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。
function* foo(x){
var  y = 2 * (yield( x+ 1));
var z = yield( y / 3);
return (x+y+z);
}
var b = foo(5);
b.next();// { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
第二次调用next()方法是传入参数为12,为上一个yield表达式的值,所以y就是24,那么z就是8.
Generator.prototype.throw,generator函数返回的遍历器对象都有一个throw方法,可以在函数体外抛出错误,在函数体内捕获。
Generator.prototype.return,可以返回指定的值并且终结遍历Generator函数。
72. ES6 的 Class 继承和 ES5 有什么不同?
ES5的继承主要是通过原型或者构造函数来实现的
class可以通过extends来实现继承,子类必须在constructor方法中调用super方法,否则新建实例报错。因为子类没有自己的this对象,而是继承了父类的this对象,然后对其进行加工。如果不调用super方法,子类得不到this对象。
super关键字即可以当做函数来使用也可以当做对象来使用。super作为函数时,表示父类的构造函数,super作为对象使用时,在普通方法中指的是父类的原型对象,在静态方法中指向父类。
类的prototype和__proto__属性:
(1)子类的__proto__属性,表示构造函数的继承,总是指向父类。


         (2)子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
实例的__proto__属性:
子类实例的__proto__属性的__proto__指向父类实例的__proto__属性。
73. es6的模块化解决了哪些问题?如何进行编译的?
前端模块化主要是解决两个问题:命名空间冲突和文件依赖管理。
ES6新增了两个命令export和import,export用来规定模块的对外接口,import用来输入其它模块提供的功能。
export不仅仅可以导出函数,还可以导出对象,类,字符串等等
export default用来指定默认输出
import为加载模块的命令
74. node.js的事件驱动模型?
在事件驱动的模型中,每一个io工作都会被添加到事件队列中,线程循环的处理队列上的工作任务,当执行过程中遇到堵塞比如说读取文件时,线程不会停下来等待结果,而是留下一个处理结果的回调函数,转而继续执行队列中的
下一个任务。这个传递到队列中的回调函数在堵塞任何运行结束后才会被线程调用。
75. 为什么说node是非阻塞io的运行环境?
非阻塞型IO:就是多线程读写文件,不用等待文件完成。nodejs是非阻塞io,处理线程要不断处理大量用户请求,处理线程要做的就是检查消息队列。如果有事件发生(io线程发来的、程序启动事件、等等),就调用回调。没有事件,就继续等。
76. 单向数据绑定和双向数据绑定?
在react中是单向数据绑定,在vue和angular中是双向数据绑定。
单向数据绑定的实现思路:
所有数据只有一份
一旦数据发生变化,就去更新页面,但是没有(页面--》data)
如果用户在页面上做了变动,那么就是手动收集起来(双向是自动的),合并到原有的数据中。
双向数据绑定 =单向数据绑定 + ui事件监听
双向数据绑定给人最大的优越感就是方便,当数据发生变化的时候,页面自动更新。但是缺点就是你不知道data什么时候发生改变了,也不知道是谁变了,变化后也不会通知你。
单向数据绑定的好处是所有的状态变化是可以被跟踪记录的,状态变化通过手动调用通知。没有暗箱操作。缺点就是代码量会相应的上升,数据流转过程变成,从而出现很多类似的样板代码。
77. ES5 Object常用的方法有哪些?
(1) Object.create(),第一个参数是要继承的原型,第二个参数是对象的属性描述符
   (2)Object.keys(),返回一个由给定对象的所有可枚举自身属性的属性名组成的数组
   (3)Object.getPrototypeOf():返回指定参数的原型
   (4)object.hasOwnProperty(),用来确定某个对象是否有指定名称的属性
   (5)Object.getOwnPropertyNames(),返回对象自己属性的名称,不包括从原型链继承的属性
   (6)Object.defineProperties(),在一个对象上添加或者修改一个或者多个属性,并返回该对象
   (7)Object.defineProperty(),将属性添加到对象,或者修改现有属性的特性
 78. HTML Socket通信及原理?
 79.检测给出的一段字符串是否是合法的IP地址?
首先我们应该明白合法的IP地址:(1~255).(0~255).(0~255).(0~255)
function isIpLegal(str) {
        // 检测是否为空
        if(str == null) {
            return false;
        }
        //检测传入的字符串的长度,最短的为7为,最长的为15为
        if(str.length < 7 || str.length > 15) {
            return false;
        }
        // 检测其是否是正常的四段IP地址
        var arr = str.split(".");
        if(arr.length !== 4) {
            return false;
        }
        // 检测每个字符是否是在0到9之间
        for(let i = 0; i < arr.length; i++) {
            if(arr[i].length > 1 && arr[i].charAt(0) === '0') {
                return false;
            }
            for(let j = 0; j < arr[i].length; j++) {
                if(arr[i].charAt(j) < '0' || arr[i].charAt(j) > '9'){
                    return false;
                }
            }
        }
        // 检测每一段是否在0~255之间
        for(let i = 0; i <arr.length; i++) {
            let item = parseInt(arr[i]);
            if(i === 0) {
                if(item < 1 || item > 255) {
                    return false;
                }
            } else if(item < 0 || item > 255) {
                return false;
            }
        }
        // 最后返回true
        return true;
    }
80.严格模式
为什么使用严格模式?
(1)为了消除代码中的一些不安全之处,保证代码运行的安全
(2)消除js代码中的一些不合理,不严谨之处,减少一些怪异行为
(3)提高编译器的效率,增加速度
严格模式做出来哪些改变?
(1)全局变量必须显示声明,未声明直接调用会报错
(2)禁止this指向全局变量,在严格模式下this是undefined
(3)禁止删除变量,只有configurable为true的对象属性才能删除
(4)对象不能有重名的属性
(5)函数不能有重名的参数
(6)不允许对arguments赋值,arguments不在追踪参数的变化,禁止使用Arguments.callee
(7)禁止使用八进制表达式
81.node中的定时器:
var a  = new Promise(function(resolve,reject) {
        console.log(1);
        setTimeout(() => console.log(2), 0);
        console.log(3);
        console.log(4);
        resolve(true);
    })
    a.then(() => {
        console.log(5)
    })
    console.log(6);
// 1,3,4,6,5,2
//这是因为Promise是立即执行的,所以一旦定义了Promise,那么就会立即执行器里面的同步任务,接下来才会执行异步任务
//异步任务又分为两类:追加在本轮循环的异步任务和追加在次轮循环的异步任务。
// process.nextTick()和Promise的回调函数最佳在本轮循环,而setTimeout,setTinterval,setImmediate追加在次轮循环
process.nextTick()是所有异步任何里面执行最快的,执行完所有的同步任务之后就会执行它
而promise的回调函数会添加到异步任务的微任务队列。
事件循环的六个阶段:timers,io,idle,poll,check,close callbacks
timers是定时器阶段,处理setTimeout,setInterval的回调函数
IO callbacks,除了setTimeout,setTinterval,setImmediate的回调函数其它回调函数基本上都会在本阶段执行
check阶段执行setImmediate的回调函数
最后是关闭请求的回调函数
82. javaScript实现异步的方法?
es6之前主要是回调函数和事件监听
回调函数:一般是需要在一个耗时操作之后执行某个操作时可以调用回调函数,比如说读取文件,异步获取数据等等,回调函数的优点是简单容易理解和部署,缺点是不利于代码的阅读和维护,代码之间高度耦合
事件监听:采用事件驱动模型,比如jquery中的on函数,优点是可以绑定多个事件,每个事件可以指定多个回调函数,缺点是整个程序变成事件驱动模型,运行流程不清晰
es6主要是promise,使用Promise对象可以用同步操作的流程写法来表达异步操作,避免了层层嵌套的异步回调,代码也更加清晰易懂,方便维护。
es7主要是async/await
83. 常见的前端自动化构建工具有哪些?webpack的工作原理是什么?
常见的前端自动化构建工具有webpack,gulp,grunt
webpack的工作原理:
webpack的核心概念:
entry,一个可执行模块或者库的入口文件
chunk,多个文件组成的一个代码块
loader文件转换器,比如说将es6转换为es5,将Sass转换为css
plugin插件,用于扩展webpack的功能
webpack的构件流程:
1.解析webpack配置参数,合并从shell传入和webpack.config.js文件里配置的参数,产生最后的配置结果
2.注册所有配置的插件,好让插件监听webpack构建生命周期的事件节点,以做出对应的反应
3.从配置的入口文件开始解析文件构建AST语法树,找出每个文件所依赖的文件递归下去
4.在解析文件递归的过程中根据文件类型和loader配置找出合适的loader用来对文件进行转换
5.递归完后得到每个文件的最终结果,根据entry配置生成代码块chunk
6.输出chunk代码到文件系统
84. html5  websocket通信及其原理?
websocket是html5新引入的一种通信协议,其主要是用来解决服务器端和客户端双向通讯的问题的。使用websocket协议服务器可以随时的向客户端推送消息,以保证前后端状态统一。
在websocket之前实现服务器端消息推送到客户端,主要使用ajax,iframe等等
websocket的构造函数接收一个url地址,第二个参数为协议名
事件:连接打开时调用onopen方法,接收到后台消息调用onmessage,关闭连接调用onclose,出现异常用onerror来捕获异常
方法:send(data),是用来发送数据的,close()关闭连接。
实现:webSocket是类似socket的TCP长连接的通讯模式,一旦webSocket连接建立之后,后续数据都以帧序列的形式传递,在客户端断开连接之前,不需要重新发起连接请求,极大的节省了网络资源的消耗,并且实时性更加显著。
连接的重要字段:
upgrade: websocket,
connection: upgrade
sec-websocket-key是客户端发送的一个base64编码的密文,要求服务器端必须返回一个对应加密的Sec-webSocket-Accept应答,否则就会报错
85. 实现链表的反转:
const Node = function(value, next) {
        this.value = value;
        this.next = next;
    }
    let NodeList = function(head) {
        this.head  = head;
    }
    Node.prototype.toString = function() {
        console.log(this.head);
        let n = this.head;
        while(n.next) {
            console.log(n.next);
            n = n.next;
        }
    }


    function reverse(nodelist) {
        let head = nodelist.head;
        if(head === undefined || !head.next === undefined) {
            return;
        }
        let p,q,r;
        p = head;
        q = p.next;
        head.next = undefined;
        while(q) {
            r = q.next;
            q.next = p;
            p = q;
            q = r;
        }
        nodelist.head = p;
    }


    var arr = [0,1,2,3,4,5];
    let nodelist = new NodeList();
    nodelist.head = new Node(arr[0]);
    let cur = nodelist.head;
    for(let i= 1;i < arr.length; i++) {
        let temp = new Node(arr[i]);
        cur.next = temp;
        cur = temp;
    }
    cur.next = undefined;
    console.log(nodelist);
    reverse(nodelist);
    console.log(nodelist);
86. 手写一个Promise?
具体的可以参考https://www.cnblogs.com/xianyulaodi/p/6256399.html来编写
87. Number()函数的转换规则:
如果是boolean值,那么true和false分别转换为1和0
如果是null就返回0
如果是undefined就返回NaN
如果是字符串,且字符串中只包含数字则返回对应的数字,如果是十六进制,就返回十进制数,其它的就返回NaN
88. var a = 1;function a() {};cosnole.log(a);
输出的是a,因为函数和变量提升,先声明函数之后在变量赋值,同名的变量a覆盖了之前声明的函数,所以是1
89. javaScript中的事件处理机制?
(1)事件流,dom2级事件规定事件流包括三个阶段:事件捕获,目标阶段,事件冒泡
(2)事件处理程序:
dom0级事件处理程序:onclick,onmousedown类似的事件,以这种方式添加的事件处理程序会在事件流的冒泡阶段处理
dom2级事件处理程序:DOM2事件通过addEventListener和removeEventListener管理,addEventListener()的第三个参数是布尔值,如果为false,则表示在冒泡阶段处理事件,如果为true则表示在捕获阶段处理该程序
ie事件处理程序:attachEvent,detachEvent,都是接受两个参数,分别是事件名称和时间处理函数。事件名称都是on+click/press等
(3)事件对象
只有在事件处理程序期间,event对象才会存在,一旦事件处理结束,那么event对象就会被销毁
ie的事件对象是window的一个属性,所以可以通过window.event来读取
获取目标元素:event.target ||event.srcElement
阻止事件冒泡:event.stopPropagation() ||event.cancelBubble= true
阻止事件默认行为:event.preventDefault() ||event.returnValue = false;
(4) 事件委托
由于事件冒泡机制,点击子元素,也会触发父元素的点击事件,那么我们完全可以将子元素点击要做的事件写在父元素的事件里面,这就是事件委托。
90. javaScript中的异步队列?
函数调用栈和任务队列
在javaScript中有一个主线程和调用栈(call-stack)。在对一个调用栈的task处理的时候,其它任务都要等着。当在执行过程中遇到类似setTimeout的异步操作时,就会交给浏览器的其它模块进行处理。当到达指定的延时后
task会被放入到任务队列中,一般不同的异步任务会被放入到不同的任务队列中。等到调用栈中所有task执行完毕后,接着去执行任务队列中的task(回调函数)。任务队列中存放的是回调函数。
91. 单线程和多线程之间的区别?
每个运行的程序至少有一个线程,这个线程主线程。主线程在程序启动时创建,执行main函数,只有一个主线程的程序,成为单线程程序。拥有多个线程的程序,成为多线程程序。
单线程:只有一个线程,代码顺序执行,容易出现代码阻塞
多线程:有多个线程,线程间独立运行,可以有效地避免代码阻塞,提供程序的运行性能
92. dns的查询过程?
(1)首先会查看本地的host文件,如果有对应的域名和IP关系,就直接拿来使用
(2)如果在hosts文件中没有找到,就想本地dns服务器进行查询,本地dns服务器会查看自己的缓存记录,如果有这条记录就直接返回,如果没有就向根dns服务器进行查询
(3)根dns服务器会告诉本地dns服务器对应的域服务器的地址,
(4)本地dns服务器向域服务器进行查询,如果是com域服务器,.com与服务器也不会直接返回对应的关系,而是给出域名的解析服务器的地址
(5)本地dns服务器向域名的解析服务器进行查询,返回一个IP和域名的对应关系,本地dns服务器将其返回给客户端并且存入自己的缓存中,以防下次访问。
dns查询方式有两种:递归查询,迭代查询
递归查询就是由局部dns服务器向根服务器进行查询,之后再由根服务器一级一级的向下查询,最后得到查询结果
迭代查询是局部服务器不自己向其他dns服务器进行查询,而是将所有可以解析的dns服务器地址返回给客户端,由客户端再向这些dns服务器进行查询,直到找到结果为止。
93. TCP的重传机制?
TCP要保证所有的数据都可以到达,所以必须要有重传机制
超时重传机制:就是发送端等待接收端的ack,直到发送端超时之后,再发送一个包,直到收到接收端的ack为止。
快速重传机制:这个机制不以时间为驱动,而是以数据来重传。如果接收端接收包没有连续到达,就ack最后可能丢了的那个包,如果发送端连续收到接收端3次相同的ack,就重传。、
比如:如果发送方发出了1,2,3,4,5份数据,第一份先到送了,于是就ack回2,结果2因为某些原因没收到,3到达了,于是还是ack回2,后面的4和5都到了,但是还是ack回2,因为2还是没有收到,于是发送端收到了三个ack=2的确认,知道了2还没有到,于是就马上重转2。然后,接收端收到了2,此时因为3,4,5都收到了,于是ack回6。
对应的时间算法:
(1)往返时延的自适应算法
记录每一次报文发出的时间,已经收到对应的确认报文的时间,时间差就是往返时延,将各个报文段的往返时延样本加权平均,就得出报文段的平均往返时延RTT
(2)karn算法
报文段每重传一次,就将重传时间增大一些:新的重传时间=r*(旧的重传时间);系数r 的典型值是2 。
94. IP数据报的格式?
IP协议提供不可靠无连接的数据报传输协议
ip数据报分为报头区和数据区两大部分
ip数据报各字段功能如下:
(1)版本号,表示ip数据报使用的ip协议版本
(2)首部长度,指出整个报头的长度
(3)服务类型,用于规定数据报的处理格式,服务类型字段的8位分成了5个子域。分别是优先权,短延迟位,高吞吐量位,高可靠位,保留位。优先权越大,表示该数据报优先权越高。
在与用户直接交互时,一般使用短延时;对大量数据进行传输的时候,可以选用高吞吐量;对数据报要传输控制信息的应用,一般选用高可靠性。
(4)总长度:整个ip数据报的长度包括首部和数据的总长度
(5)生存时间:表示数据报可以在网络中传输的最长时间,实际应用中把生存时间设置为了数据报可以经过的最大路由数
(6)上层协议标识,IP协议可以承载各种上层协议
(7)源地址
(8)目标地址
(9)校验和,用于协议头数据有效性的校验,发送数据时,将ip数据报的校验和字段置位0;把首部看成以16位为单位的数字组成,依次进行二进制反码求和;把得到的结果存入校验和字段中。
在接收数据时,计算数据报的校验和相对简单,就是把首部看成以16位为单位的数字组成,依次进行二进制反码求和,包括校验和字段;检查计算出的校验和是否为0,如果等于0就说明校验和是正确的,否则就是错误的。
95. ARP协议的工作过程?
ARP---地址解析协议,是根据ip地址获取物理地址的一种TCP/IP协议。主机发送消息时将包含目标IP地址的ARP请求广播到网络上的所有主机,并接收返回消息,以此确定目标的物理地址。收到请求之后,将ip地址和物理地址存入本机ARP缓存中并保留一段时间,
下次请求时可以直接查询ARP缓存以节约资源。
工作过程:
主机A的ip地址为192.168.1.1,主机B的IP地址为192.168.1.2,
(1)根据主机A上面的ARP缓存内容,IP确定用于访问的主机的ip地址为192.168.1.2,然后主机A在其ARp缓存中查找主机B的MAC地址
(2)如果没有找到,主机A会将IP地址和MAC地址都包括在ARP请求中,本地网络上的每台主机都会收到ARP请求,并且检查是否与自己的IP地址匹配,
如果主机发现与自己的IP地址不匹配,那么将丢弃ARP请求
(3)主机B发现与自己的IP地址相同,则将主机A的IP地址和MAc地址映射添加到本地缓存中
(4)主机B将包含其MAC地址的ARP回复消息直接发送给主机A
(5)当主机A收到主机B发来的ARP回复消息之后,会-用主机B的ip地址和AMC地址映射更新ARP缓存。之后将可以直接通信了。
96. TCP/IP五成模型?各层的协议类型?
TCP/IP五成模型:物理层,数据链路层,网络层,传输层,应用层
OSI七层模型:物理层,数据链路层,网络层,传输层,会话层,表示层,应用层。
应用层协议:http,FTP(文件传输协议),SMTP(简单邮件传输协议)
传输层协议:TCP,udp
网络层协议:ip协议,ARP协议,RAPP协议,BGP(边界网关协议)
数据链路层:MAC(媒体接入控制)
物理层
97. http状态码301和302之间的区别?
301:move Permanently,请求的资源被永久的移动到新的位置,并且将来任何对此资源的引用都将使用本响应返回的几个URI之一
302: Found,请求的资源临时从不同的URI响应请求,由于这样的重定向是临时的,所以客户端应当继续向原有地址发送信息
从字面上看301是永久重定向,而302是临时重定向
区别是302是暂时的重定向,搜索引擎会抓取新的内容而保留旧的地址,因为服务器返回302,搜索引擎认为新的网址是暂时的
301是永久的重定向,搜索引擎在抓取新的内容的同时也将旧的网址替换为新的重定向后的网址
98.浏览器和服务器是如何判断是否应该跨域的,针对CORS?
浏览器和服务器的合作判断规则如下:
(1)浏览器先根据同源策略对前端页面和后台交互地址做匹配,若同源,则直接发送数据请求;若不同源,则发送跨域请求。


(2)服务器解析程序收到浏览器跨域请求后,根据自身配置返回对应文件头。若未配置过任何允许跨域,则文件头里不包含Access-Control-Allow-origin字段,若配置过域名,则返回Access-Control-Allow-origin+ 对应配置规则里的域名的方式。


            (3)浏览器根据接受到的http文件头里的Access-Control-Allow-origin字段做匹配,若无该字段,说明不允许跨域;若有该字段,则对字段内容和当前域名做比对,如果同源,则说明可以跨域,浏览器发送该请求;若不同源,则说明该域名不可跨域,不发送请求
CORS常见的配置项:
Access-control-Allow-origin,允许的域名,只能是通配符或者单域名
Access-control-Allow-methods,允许跨域请求的http方法
Access-Control-Allow-Headers
Access-control-Allow-Credentials,如果为true,表示请求中可以包含cookie信息
99. 如果有多个ajax请求,我希望可以按照某种顺序依次执行,有什么办法?
可以创建一个ajax数组,在函数内部执行第一个数组,在请求返回之后将该ajax函数删除,之后判断数组的长,如果长度大于0,表示还有请求没有完成,继续执行ajax函数。
或者使用Promise对象来进行。
100. 重绘和重排
重绘:一个元素外观的改变所触发的浏览器行为,比如说修改背景颜色,visibility等属性。浏览器会根据元素的新属性进行重新绘制,使元素呈现新的外观。
重排:当元素的尺寸发生变化的时候,浏览器会使渲染树中受到影响的部分失效,而重新构造这部分渲染树,完成后浏览器会重新绘制受影响的部分到屏幕中。
所以重排必定引起重绘,但是重绘不一定引起重排。
避免的方法:
(1)直接操作className,而不是直接操作style属性
(2)使用documentFragment来触发一次的重绘和重排
(3)使用innerHTML来替代多次的dom写入操作,
(4)使用replaceChild来引发一次回流和重绘
(5)对dom的查询结果进行缓存
(6)尽量避免使用document.write()
101. dom0级与dom2级之间的区别?
dom0级定义的事件都是on+事件名,而dom2级定义的事件有两个方法:addEventListener,removeEventListenr
它们都有三个参数:第一个参数是事件名(如click);
             第二个参数是事件处理程序函数;
               第三个参数如果是true则表示在捕获阶段调用,为false表示在冒泡阶段调用。
    addEventListener():可以为元素添加多个事件处理程序,触发时会按照添加顺序依次调用。
    removeEventListener():不能移除匿名添加的函数。
102. vertical-align属性什么时候有效?
vertical-align是用来设置元素的垂直对齐方式的,
当父元素的inline-block/block的line-height设置后,子元素(inline-block/inline)的vertical-align才会有效。
当父元素没有设置line-height时,兄弟元素都设置vertical-align:middle,则居中对齐
103. 栈和队列的比较?
栈和队列都是线性表,只不过栈的插入和删除操作都是在表尾进行操作的(在栈中称为栈顶),而队列是只允许在表尾插入数据,在表头删除数据
不同点:
(1)栈是先进后出,队列是先进先出
(2)删除数据的位置不同,栈是在表尾进行删除数据,而队列是在表头进行删除数据
(3)应用场景不同:栈常用于括号问题的求解,深度优先遍历等等,而队列一般用于计算机中各种资源的管理,广度优先遍历等等
相同点:
(1)都是线性结构
(2)插入和删除的时间复杂度都是O(1),在空间复杂度上两者也是一样的
(3)插入操作都限定在表尾完成
104. 用两个栈来实现队列,或者用两个队列来实现栈
用两个栈来实现队列?
只要有元素来,就默认插入到第一个栈中,如果2不为空,并且要出队,那么就直接弹出即可,如果栈2为空,那么从栈1中逐个弹出压入,那么就完整的实现了先进先出的功能。
用两个队列来实现栈?
只要有元素来默认就插入到第一个队列中,删除元素的时候,将第一个队列中要删除的元素的前面的元素都出队压入到第二个队列中,那么就可以在原队列中弹出唯一的元素了。
105. 正则表达式
https://www.cnblogs.com/moqing/archive/2016/07/13/5665126.html
106. css样式优先原则?
原则一:由于继承发生样式冲突时,最近祖先获胜
原则二:继承样式和直接指定的样式发生冲突时,直接指定的样式获胜
原则三:直接指定的样式发生冲突时,样式权值高者获胜
原则四:样式权值相同时,后者获胜
原则五:!important的样式属性不被覆盖
107.[0] == 0,// true
这是因为使用Number()对[0]进行转换的过程中,会调用对象的toString()方法---“0”,一转换就成为了0.所以是true
108.inline和block之间的区别?为什么img是inline,但是还可以设置宽高?
inline是行内元素,不可以设置宽度和高度,padding和margin设置水平方向是有效的,设置垂直方向是无效的。而block是块级元素,可以设置宽度和高度,margin和padding设置水平和垂直方向都会有效。
关于img可以设置宽高的问题,就涉及到了替换元素和非替换元素,替换元素是浏览器根据元素的标签和属性,来决定元素的具体显示内容,比如img,input,textarea,select等。而非替换元素是其内容直接显示给用户端,大多数都是非替换元素,比如说p,div,section。
替换元素一般都有内在尺寸,所以具有width和height,可以设定。
109.为什么有了ip地址之后,还需要mac地址?
mac地址又称为物理地址,硬件地址,用于定义网络设备的位置,mac地址是固定的,由网卡来决定。
ip地址专注于网络层,将数据报从一个网络转发到另一个网络,而mac地址专注于数据链路层,将一个数据帧从一个节点传送到相同链路的另一个节点。
为什么在有了ip地址之后换需要mac地址,这是因为在信息传递的时候,需要知道的其实是两个地址,终点地址和下一条的地址。ip地址在本质上就是终点地址,它在跳过路由器的时候是不会改变的。而mac地址是下一条的地址,每跳过一次路由器都会改变,mac地址起到了记录下一条信息的作用。
110. 能说出来两种对于javaScript工程师很重要的编程范式吗?
javaScript是一种多范式的编程语言,它即支持面向过程编程,也支持面向对象编程,还支持函数式编程。javaScript所支持的面向对象编程包括原型继承。
所谓的编程范式是指计算机编程的基本风格和典范模式。
111. TCP的协议头?
TCP的协议头最长为60bytes,相关字段的解释:
(1)TCP源端口:发送方应用程序对应的端口
(2)TCP目的端口:这个端口指明报文接收计算机上的应用程序的端口
(3)TCP序列号(SN):序列号标识了TCP报文中第一个byte在对应方向的传输中对应的字节序号。
(4)TCP应答号(ACK Number):表示报文发送期望接收的字节序列
(5)头长:表示tcp头的长度
(6)窗口大小(window size):表示从Ack Number开始还可以接收多少byte的数据量
(7)检验位(CheckSum)
(8)优先指针:表示当前数据是急需被处理的
112.调用栈:
调用栈其实就是一种解析器去处理程序的机制,它是栈数据结构。它能追踪子程序的运行状态。


(1)当脚本要调用一个函数时,解析器把该函数添加到栈中并且执行这个函数。并形成一个栈帧


(2)任何被这个函数调用的函数会进一步添加到调用栈中,形成另一个栈帧,并且运行到它们被上个程序调用的位置。


(3)当执行完这个函数后,如果它没有调用其他函数,则它会从调用栈中推出。然后调用栈继续运行其他部门。


(4) 异步函数的回调函数一般都会被添加到运行队列里面,如settimeout会在响应的时间后把回调函数放入队列中,队列里的函数需要等栈为空时才会被推入栈中执行。如果队列中有其他函数,需要等队列前面的函数被堆入调用栈中之后才会运行。


Firebug和Chrome提供了console.trace()来显示函数堆栈。
113. TCP和UDP之间的不同?
TCP是传输控制协议,是面向连接的协议,而UDP是用户数据报协议,是一个非连接的协议。
TCP提供可靠的服务,也就是说,通过Tcp连接传送的数据,无差错,不丢失,不失真且有序,而UDP则是尽最大努力交付,不保证可靠交付。
TCP是面向字节流的,而UDP是面向报文的
Tcp的逻辑通信信道是全双工的可靠信道,而UDP则是不可靠信道
每一个TCP连接都是只能点到点的,而UDP支持一对一,一对多,多对多的交互。
114. bind和call,apply之间的区别,以及bind的实现
bind()方法会返回执行上下文被改变的函数而不会立即执行,但是call和apply则是立即执行马上得到结果。
Function.prototype.bind = function(context) {
if(thpeof this !== "function") {
throw new Error('调用者必须是一个函数');
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var FNOP = function() {};
var bound = function() {
// 当作为构造函数时,this指向实例,self指向绑定函数,
self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)));
}
FNOP.prototype = this.prorotype;
bound.prototype = new FNOP();
return bound;

}
115. ES6中关于数组和对象新增加的方法有哪些?
(1)数组的扩展: 
Array.of(),总是返回参数值返回的数组,如果没有参数就返回一个空数组
Array.from(),是将类数组对象转换成真正的数组
find(),用于找出第一个符合条件的成员,如果没有就返回undefined
findIndex()用于返回第一个符合成员的位置,如果所有成员都不符合条件则返回-1
fill()用于填充数组,其接受三个参数:char,start,end。char为第一个参数表示要填充的字符,start表示从哪个位置开始填充,end表示结束位置
entries(),keys(),values()分别是对数组的键值对,键名,键值进行遍历
for( let index of [1,2,3,4].keys()) {
console.log(index);
}
(2)对象的扩展:
Object.is(),用于判断两个参数是否是严格相等
Object.assign(),用于对象的合并,将源对象的所有可枚举属性都复制到目标对象。
如果只有一个参数是undefined和null,则会报错因为它们无法转成对象
如果是字符串,数值和布尔值不在首参数,那么只有字符串会以数组的形式拷贝入目标对象,其他值都不会产生效果。
属性遍历:
Object.keys(),返回一个当前对象自身可遍历属性组成的数组
Object.getOwnPropertyNames()返回一个数组,包含对象自身的所有属性(不包含Symbol属性,但是包含不可枚举属性)的键名
Object.getOwnPropertySymbols()返回一个数组,包含对象自身的所有symbol属性的键名
Reflect.ownKeys()返回一个数组,包含对象自身的所有键名。
Object.setPrototypeOf()用于设置参数的原型
116. http2.0的相关理解:
http2.0的主要目标是改进传输性能,实现低延迟和高吞吐量。所有http首部,值以及他们的使用场景都不会变。
http2.0主要是为了突破上一代众所周知的性能限制,是对1.x版本标准的扩展,而非替代,http2.0增加了新的二进制分帧数据层。
http2.0通过支持首部字段压缩和同一连接上发送多个并发消息,让应用更有效的利用网络资源,减少延迟的感知时间,而且它还支持服务器到客户端的主动推送机制。
(1)二进制分帧层
http2.0将所有的传输消息都分割为更小的消息和帧,并对它们采用二进制格式的编码
(2)流,消息和帧
流式连接中的一个虚拟信道,流可以承载双向的消息,每个流都有一个唯一的整数标示符
帧是http2.0中的最小通信单位,http2.0所有的帧都是采用二进制编码,所有首部数据都会被压缩编码。
(3)多向请求和响应
http2.0中新的二进制分帧层突破了这些限制,实现了多向请求和响应;客户端和服务器可以把http消息分解为互不依赖的帧,然后乱序发送,最后在另一端把他们重新组合起来。
(4)请求优先级
浏览器会给每一个帧都分配一个优先值,浏览器在发现资源时立即发派请求,指定每个流的优先级,让服务器决定最优的响应次序。这样请求就不必排队了,即节省了时间,也最大限度的利用了每个连接。
(5)每个来源一个连接
http2.0连接是持久化的,而且服务器和客户端之间只需要一个连接即可
(6)服务器推送
http2.0连接后,客户端和服务器交换settings帧,借此可以限定双方并发流的最大数量。所有推送资源多遵循同源策略。
117. TCP协议的超时重传机制?
TCP协议是一种面向连接的可靠的传输层协议,基本原理:在发送数据之后,就开启一个定时器,若是在这个是时间内没有收到发送数据的ACK确认报文,则对该本文进行重传,在达到一定次数还没有成功时就放弃发送一个复位信号。
比较重要的是超时重传时间,如何设置这个定时器的时间(RTO),实际上RTO是根据网络中的RTT(传输往返时间)自适应调整的。
118. TCP协议的拥塞避免?
(1)慢启动和拥塞避免
慢启动的基本思想是在TCP开始在一个网络中传输数据或者因为数据丢失并开始重发时,首先慢慢的对网络实际容量进行试探,避免由于发送了过量的数据而导致阻塞。
慢启动为发送方的TCP增加了一个窗口:拥塞窗口,记为cwnd。当与另一个网络中的主机建立TCP连接时,拥塞窗口被初始化为1个报文段,每收到一个ACK,拥塞窗口就加倍,直到大于慢启动门限值ssthresh。
拥塞避免算法:(慢启动和拥塞避免)
  实际中拥塞避免和慢启动是一起实现的
(1)对一个给定的连接,初始化拥塞窗口cwnd为1,ssthresh为65535个字节
(2)TCP数据的输出不能超过cwnd和接收方通告窗口的大小。拥塞窗口时发送方使用的流量控制,而通告窗口是接收方进行的流量控制。
(3)当拥塞发生时(超时或者收到重复确认),sshthresh被设置为当前窗口大小的一半(cwnd和接收方通告窗口大小的最小值)。此外如果超时引起了阻塞,则cwnd被设置为1个报文段(这就是慢启动)
(4)当新的数据被对方确认时,则增加cwnd,增加的方法则依赖于是否正在进行慢启动还是拥塞避免,如果cwnd小于sshthresh则正在进行慢启动,否则就是在进行拥塞避免。慢启动的状态下cwnd每次会加倍增加,而拥塞避免的情况下每个往返时间内最多可以发送一个报文段,呈现线性增长。
快重传和快速恢复算法:
快重传不是以时间为驱动而是以事件为驱动。快重传要求接收方在收到一个失序的报文段后就立即发送重复确认,发送当一旦连续收到三次相同的重复确认就立即重传对方尚未收到的报文段,而不必等到设置的重传计时器时间到期。
与快重传相对应的是快恢复算法:
当收到连续三个重复确认后就执行乘法减小算法,将ssthresh门限减半
接下来将cwnd设置为sshthresh的大小,接下来执行拥塞避免算法,线性增大
119. TCP协议的滑动窗口和流量控制?
TCP的滑动窗口分为两种窗口:接受窗口,发送窗口,接收方通过通告控制发送方的窗口大小,从而避免对方发送速度过快导致包的丢失。
ACK中包含两个重要的信息:
(1)期望接收到的下一个字节的序号n,该你表示接收方已经接收到了前n-1个字节数据。
  (2)当前窗口的大小m,这样的话接收到这两个数据之后发送方就可以计算得出还要发送多少字节给接收方,假设大年发送方已经发送到了x字节那么还需要发送的字节数为y = m- (x- n),这就是滑动窗口控制流量的基本原理。
滑动窗口基本原理:
(1)对于TCP回话的发送方,其任何时候都会将数据分为四类,已经发送并得到对方ACk的数据,已经发送还没有收到对方ACK的数据,未发送但对方允许发送的数据,未发送且不允许发送的数据。
(2)接收方在任意时刻接受缓存存在三种:已经接收,未接收准备接收,未接收且未准备接收。未接收但准备接收称之为接收窗口。
实际应用中的滑动窗口:
(1)1比特滑动窗口协议,发送方和接收方的窗口大小都是1,所以叫做1比特滑动窗口协议。发送方每次只能发送一个,但是必须等待这个数据报的ACK,才能发送下一个。
(2)后退n协议,此时发送窗口的大小为n,而接收方的接收窗口大小为1,一旦发现某个数据帧出现了问题,就会重新发送该数据帧到n的所有数据帧,会导致数据的大量重发。
(3)选择重传协议,是针对后退n协议的改进,每次只要求重发错误的那个帧,避免了数据的重发。
120. 移动端浏览器兼容?
(1)如果图片加载很慢的情况下,一般使用canvas方法加载
(2)防止页面的放大与缩小,一般使用viewport
(3)format-detection:启动或者禁用自动识别页面中的电话号码
(4)html5调用安卓或者安卓的拨号功能为在a的href中加tel即可
121. git常用的命令有哪些?
(1)git init,用于初始化当前项目,并在当前文件夹下面常见一个.git的文件夹
(2)git clone url,用于获取远程的的文件
(3)git log,用于打印消息
git log --oneline --number,每条log只显示一行,显示Number条
git log --oneline --graph,图像化的表示出分支合并历史
git log --author
(4)git add.会递归的添加当前目录的所有文件
(5)git commit -m "",用于提交被add进来的改动
(6)git rm file,移除文件
122. 利用js获取盒模型的宽高?
(1)dom.style.width/height,这种方法只能获取到内联样式设置的宽高,
(2)dom.offsetWidthoffsetHeight,
(3)dom.getBoundingClientRect().width/height,这种方法根据元素在视窗中的位置来获取宽高。
123. javaScript中的作用域链?
关于作用域链就得讲到执行环境,执行环境是javaScript中的一个重要概念。执行环境定义了变量和函数有权访问的其他数据,每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。
当代码在一个环境中执行时,会创建变量对象的一个作用域链,作用域链的用途就是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端始终都是当前执行的代码所在环境的变量对象,如果该执行环境是一个函数,
则将其活动对象作为变量对象。活动对象最开始只有arguments一个变量,作用域中的下一个变量来自外部环境,这样一直延续到全局执行环境,全局执行环境的变量对象始终都是作用域链的最后一个对象。
每个环境可以向上搜索作用域链,以 查询变量和函数名,但是任何环境都不能通过向下搜索作用域链而进入另一个人执行环境。
124. Promise的相关实现原理?
首先Promise构造函数中要实现异步对象状态和回调函数的剥离,并且分离之后还能够使回调函数正常运行
其次要实现链式调用并且状态管理。
相关的文章链接:https://www.cnblogs.com/bdbk/p/5176594.html
下面口述一下具体原理:
首先构造函数来说每个构造函数都有三个属性:state:当前的对象状态,value:异步结果的对象变量,handlers:存储回调函数。在构造函数中有两个参数分别是fulfill和reject。fulfill函数对应的是异步任何处理成功的时候,该函数将state设置为fulfilled,value设置为传入的参数,并且遍历handlers数组依次执行相关函数。
reject函数则将state状态设置为rejected,将value设置为传入的error。最后将fulfill和reject传入fn函数,修改promise对象的状态和结果。
接下来是回调方法then,该函数也是返回一个Promise对象,在该函数中又重新定义了两个函数分别是onResolvedFade和onRejectedFade。onResolvedFade函数中会执行链式结果传递,首先语句如下:var ret = onResolved ? onResolved(val) :val;,之后判断ret是否为一个Promise对象,如果是则继续ret.then(function(val){resolve(val)});,否则的话就直接resolve(ret);
onRejectedFade函数第一句也是同样var ret = onRejected?onRejected(val):val;,之后调用reject(ret).在then方法中才将回调函数push到handlers属性上面。之后通过判断当前状态如果为fulfilled就执行onResolvedFade,否则的话就执行onRejectedFade。
125. 具体的AMD和cmd规范?
(1)https://www.cnblogs.com/migrantworkers/p/6577324.html
(2)https://www.jianshu.com/p/9b44a1fa8a96
126. http的请求头和响应头分别包含哪些部分?
请求头包含的部分:
(1)Accept-Charset,浏览器能够显示的字符集
(2)Accept-Encoding,浏览器能够处理的压缩编码
(3)Accept-Language,浏览器当前设置的语言
(4)Connection,服务器和浏览器之间的连接类型
(5)Cookie,当前页面设置的任何Cookie
(6)Host,发出请求的页面所在的域
(7)Referer,发出请求页面的url
(8)User-Agent,浏览器的用户代理字符串
响应头包含的部分:
(1)Date,消息发送的时间
(2)server,服务器的名字
(3)Connection,服务器和客户端的连接类型
(4)content-type,后面的文档是什么类型
(5)Cathe-control,控制http缓存
127. 浏览器渲染和服务器渲染之间的区别?
浏览器端渲染指的是用js生成html,前端做路由,适合做单页应用程序
服务器端渲染指的是后台语言通过一些模板引擎来生成html,适合多页面应用。
浏览器端渲染优点明确,后端只提供数据,前端做视图和交互逻辑,分工明确,但缺点是用户等待时间变长了。
服务器端渲染:服务器在接收到用户请求之后,会计算出用户所需的数据,然后将数据更新成视图发送给客户端,客户端直接将数据嵌入页面即可。这样做的好处就是响应很快,用户体验比较好,有利于SEO。缺点是前后端分工有些不明确,不利于并行开发,同时也增加了服务器的计算压力。
发布了30 篇原创文章 · 获赞 6 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_21901233/article/details/80671002
今日推荐