JavaScript(ES6)
1. Promise
1.1 promise 的了解
依照
Promise/A+
的定义,Promise
有四种状态:pending:
初始状态, 非fulfilled
或rejected.
fulfilled:
成功的操作.rejected:
失败的操作.settled: Promise
已被fulfilled
或rejected
,且不是pending
另外,
fulfilled
与rejected
一起合称settled
Promise
对象用来进行延迟(deferred
) 和异步(asynchronous
) 计算Promise
实例拥有then
方法(具有then
方法的对象,通常被称为thenable
)。它的使用方法如下1
promise.then(onFulfilled, onRejected)
- 接收两个函数作为参数,一个在
fulfilled
的时候被调用,一个在rejected
的时候被调用,接收参数就是future
,onFulfilled
对应resolve
,onRejected
对应reject
- 接收两个函数作为参数,一个在
1.2 promise的实现
Promise
是ES6
新增的语法,解决了回调地狱的问题。可以把
Promise
看成一个状态机。初始是pending
状态,可以通过函数resolve
和reject
,将状态转变为resolved
或者rejected
状态,状态一旦改变就不能再次变化。then
函数会返回一个Promise
实例,并且该返回值是一个新的实例而不是之前的实例。因为Promise
规范规定除了pending
状态,其他状态是不可以改变的,如果返回的是一个相同实例的话,多个then
调用就失去意义了。 对于then
来说,本质上可以把它看成是flatMap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61// 三个常量用于表示状态
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function MyPromise(fn) {
const that = this
this.state = PENDING
// value 变量用于保存 resolve 或者 reject 中传入的值
this.value = null
// 用于保存 then 中的回调,因为当执行完 Promise 时状态可能还是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用
that.resolvedCallbacks = []
that.rejectedCallbacks = []
function resolve(value) {
// 首先两个函数都得判断当前状态是否为等待中
if(that.state === PENDING) {
that.state = RESOLVED
that.value = value
// 遍历回调数组并执行
that.resolvedCallbacks.map(cb=>cb(that.value))
}
}
function reject(value) {
if(that.state === PENDING) {
that.state = REJECTED
that.value = value
that.rejectedCallbacks.map(cb=>cb(that.value))
}
}
// 完成以上两个函数以后,我们就该实现如何执行 Promise 中传入的函数了
try {
fn(resolve,reject)
}cach(e){
reject(e)
}
}
// 最后我们来实现较为复杂的 then 函数
MyPromise.prototype.then = function(onFulfilled,onRejected){
const that = this
// 判断两个参数是否为函数类型,因为这两个参数是可选参数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v=>v
onRejected = typeof onRejected === 'function' ? onRejected : e=>throw e
// 当状态不是等待态时,就去执行相对应的函数。如果状态是等待态的话,就往回调函数中 push 函数
if(this.state === PENDING) {
this.resolvedCallbacks.push(onFulfilled)
this.rejectedCallbacks.push(onRejected)
}
if(this.state === RESOLVED) {
onFulfilled(that.value)
}
if(this.state === REJECTED) {
onRejected(that.value)
}
}
2. ES6
2.1 ES6中增加了新的特性(理解)
- 新增模板字符串(为
JavaScript
提供了简单的字符串插值功能) - 箭头函数,
for-of
(用来遍历数据—例如数组中的值。)arguments
对象可被不定参数和默认参数完美代替。ES6
将promise
对象纳入规范,提供了原生的Promise
对象。generator- 增加了
let
和const
命令,用来声明变量。 - 增加了块级作用域。
let
命令实际上就增加了块级作用域。- 还有就是引入
module
模块的概念
2.1 箭头函数与普通函数的区别
- 函数体内的
this
对象,就是定义时所在的对象,而不是使用时所在的对象 - 不可以当作构造函数,也就是说,不可以使用
new
命令,否则会抛出一个错误 - 不可以使用
arguments
对象,该对象在函数体内不存在。如果要用,可以用Rest
参数代替 - 不可以使用
yield
命令,因此箭头函数不能用作Generator
函数 - 箭头函数的特点:
- 箭头函数其实是没有
this
的,这个函数中的this
只取决于他外面的第一个不是箭头函数的函数的this
。在这个例子中,因为调用a
符合前面代码中的第一个情况,所以this
是window
。并且this
一旦绑定了上下文,就不会被任何代码改变
- 箭头函数其实是没有
2.2 let var const
let
- 允许你声明一个块级作用域被限制在块级中的变量、语句或者表达式
- let绑定不受变量提升的约束,这意味着let声明不会被提升到当前
- 该变量处于从块开始到初始化处理的“暂存死区”
var
- 声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的
- 由于变量声明(以及其他声明)总是在任意代码执行之前处理的,所以在代码中的任意位置声明变量总是等效于在代码开头声明
const
- 声明创建一个值的只读引用 (即指针)
- 基本数据当值发生改变时,那么其对应的指针也将发生改变,故造成
const
申明基本数据类型时 - 再将其值改变时,将会造成报错, 例如
const a = 3
;a = 5
时 将会报错 - 但是如果是复合类型时,如果只改变复合类型的其中某个
Value
项时, 将还是正常使用
3. 异步编程的实现方式
- 回调函数
- 优点:简单、容易理解
- 缺点:不利于维护,代码耦合高
- 事件监听(采用时间驱动模式,取决于某个事件是否发生):
- 优点:容易理解,可以绑定多个事件,每个事件可以指定多个回调函数
- 缺点:事件驱动型,流程不够清晰
- 发布/订阅(观察者模式)
- 类似于事件监听,但是可以通过‘消息中心’,了解现在有多少发布者,多少订阅者
- Promise对象
- 优点:可以利用then方法,进行链式写法;可以书写错误时的回调函数;
- 缺点:编写和理解,相对比较难
- Generator函数
- 优点:函数体内外的数据交换、错误处理机制
- 缺点:流程管理不方便
Generator
是ES6
中新增的语法,和Promise
一样,都可以用来异步编程
- async函数
- 优点:内置执行器、更好的语义、更广的适用性、返回的是Promise、结构清晰。
- 缺点:错误处理机制
async和promise的区别
async
和await
相比直接使用Promise
来说,优势在于处理 then 的调用链,能够更清晰准确的写出代码。缺点在于滥用await
可能会导致性能问题,因为await
会阻塞代码,也许之后的异步代码并不依赖于前者,但仍然需要等待前者完成,导致代码失去了并发性
4. html5
4.1 html5有哪些新特性,移出了哪些元素
- 增加的元素:主要是对图像,位置,存储,多任务等功能的增加
- 绘画canvas
- 用于媒介回放的video和audio
- 本地离线存储localstorage,用于长期存储数据,关闭;浏览器页面依然存在
- sessionStorage会话存储在浏览器关闭之后自动删除
- 语义化更好的内容标签header nav article section footer aside
- 表单控件calendar,date ,time,email,url search
- 新的技术websocket,webworker
- 移出的元素
- 纯表现的元素:basefont,center,tt,strike
- 对可用性产生负面影响的元素frame,noframes,frameset
4.2 html5 的离线存储怎么使用,解释一下工作原理
用户在没有与英特网进行连接时,可以正常访问站点应用,在连接时会更新用户机器上的缓存文件
原理:html5的离线存储是基于新建的.appcache文件的缓存机制,通过这个文件上的缓存清单离线存储资源,这些资源就会像cookie保存下来,之后当网络处于离线状态,就会调用离线存储的资源来显示页面
如何使用:
- 在页面头部的下面添加manifest属性
- 在cache.manifest文件中编写离线存储资源
- 在离线状态下调用window.applicationCache进行需求实现
4.3 浏览器是怎么对HTML5
的离线储存资源进行管理和加载的呢
- 在线的情况下,浏览器发现html的头部有manifest属性,如果用户是第一次访问,浏览器就会根据manifest文件下载资源并进行离线存储.如果已经访问过并且已经离线存储了就会直接访问离线的资源加载页面,浏览器会对比新的manifest文件和旧的manifest文件,如果文件发生改变就会重新下载离线存储资源,反之不做任何改变
- 离线的情况下浏览器就会直接使用下载存储的资源显示页面
4.4 iframe的缺点
- iframe会阻塞页面的加载, iframe和主页面共享连接池,而浏览器对相同域的链接有限制,所有对页面的并行加载有影响
- 搜索引擎的检索无法解读这种页面,不利于SEO优化
- 如果需要使用
iframe
,最好是通过javascript
动态给iframe
添加src
属性值,这样可以绕开以上两个问题
4.5 canvas和svg的区别
- svg绘制的每一个图像都是一个单独的dom节点,可以方便的进行元素的修改和事件的绑定,canvas绘制的就是一张画布
- svg输入的是矢量图像,可以进行缩放不会产生锯齿和失真,canvas和图片一样会产生锯齿和失真
4.6 src和href的区别
- src是替换当前元素,href是将文档和资源建立连接
<link href='base.css' ref='stylesheet'
那么浏览器会识别该文档为css
文件,就会并行下载资源并且不会停止对当前文档的处理。这也是为什么建议使用link
方式来加载css
,而不是使用@import
4.7 网页中用到的图片格式
- webp:在加快图片加载速度的图片格式。图片压缩体积大约只有
JPEG
的2/3
- Apng:是PNG的位图动画扩展
- png-8,png-16,jpeg,gif,svg
4.8刷新页面,一次js请求会有哪些缓存
- DNS缓存,CDN缓存,浏览器缓存,服务器缓存
4.9网页有大量图片一次性加载很慢的优化方案
- 图片的懒加载:在图片的未加载区域添加滚动事件,当当图片与浏览器顶端的距离与页面的距离,如果前者小于后者优先加载
- 图片预加载:当有一些幻灯片,相册等
- 一些CSS图片可以采用CSSsprite,SVGsprite,base64,iconfont等技术处理图片
- 如果图片过大可以使用特殊编码的图片,先加载一张压缩比较严重的图片提高用户体验
- 如果图片展示区域小于图片的真实大小,先在服务器端将图片压缩到显示区域的大小在进行展示
5 JS进阶基础
5.1 变量提升的理解
- 两种执行环境:当js代码执行庭时会产生执行环境,只要代码不是写在函数内部,就会在全局执行环境中,在函数内部执行的会产生函数执行环境,全局执行的产生全局执行环境
- 在生成执行环境会有两个阶段,创建阶段和代码执行阶段;
- 创建阶段:js解释器就会找到所有需要提升的变量和函数,并给他们提前在内存中开辟好内次空间,函数会在保存在整个内存空间,变量声明并赋值为undefined,所以在第二个阶段就可以直接提前使用
- 在变量提升的过程中,相同的函数会覆盖上一层就的函数,,并且函数优先于变量提升
5.2 bind,call,apply的区别
- 相同点:三者都用于改变this的指向
- call和apply作用相同,传参的方式不同,除了第一个参数以外
- call可以接受一个参数列表
- apply可以接收一个参数数组
1 | let a = {value:'hello'} |
- bind和其他的 方法作用一致,会返回一个函数,可以通过bind实现函数柯里化(柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。)
5.3 函数柯里化
- 概念:将接收多个参数的函数转化为接收一个参数的函数的技术
- 理解:用闭包把参数保存起来,当参数的数量足够执行函数了,就开始执行函数。
- 应用:延迟计算、参数复用、动态生成函数的作用
- 实现(基本实现)
1 | function curry(fun,...args){ |
- 通过bind实现函数柯里化
- bind()方法所返回的函数的length(形参数量)等于原函数的形参数量减去传入bind()方法中的实参数量(第一个参数以后的所有参数),因为传入bind中的实参都会绑定到原函数的形参
1 | function func(a,b,c,d){}//fun的length为4 |
5.4 模拟实现bind,call和apply函数
- bind函数
1 | /* |
call函数
1
2
3
4
5
6
7
8
9
10
11// 1.模拟实现call(可接受参数列表)
Function.prototype.myCall = function (context) {
var context = context || window;
// 将this指向fn
context.fn = this;
var args = arguments.slice(1);
var result = context.fn(args);
//删除临时函数
delete context.fn;
return result;
}apply函数
1 | // 2.模拟实现apply(可接收数组列列表) |
5.5 原型链
- 每个函数都有一个prototype属性,指向原型,除了Function.prototype.bind()
- 每个对象都有一个
__proto__
属性,指向的是该对象的构造函数的原型,这个属性气质执行的是prototype
,但是prototype
内部属性我们并不能直接访问到,通过__proto__
来访问 - 对象可以通过
__proto__
来访问不属于对象中的属性,__proto__
将对象连接起来形成了原型链
5.6 如何判断对象类型
- 通过
Object.prototype.toString.call(xx)
- 通过instanceof可以正确的判断对象的类型,因为内部机制是通过判断原型链中是否能找到该类的原型
prototype
5.7基本数据类型和引用数据类型在存储上的差别
- 基本数据类型存在栈上
- 引用数据类型存储在堆上
5.8 深拷贝的方法
- json.parse(Json.stringify(object))
5.9 防抖和节流
防抖 (debounce): 将多次高频操作优化为只在最后一次执行,通常使用的场景是:用户输入,只需再输入完成后做一次输入校验即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 1.防抖:返回的是一个闭包,这里使用的是普通函数,里面的setTimeOut使用的是箭头函数,这样做的目的是让this指向准确,this的真是指向并不是debounce的调用者,而是返回闭包的调用者;对传入闭包的参数进行透传
function debounce(event, time) {
let timer = null
return function (...args) {
//清除之前的定时器,重新启动定时器开始计算
clearTimeout(timer);
timer = setTimeout(() => {
event.apply(this, args)
}, time);
}
}
// 如果需要理解执行在内部添加一个flag,第一次执行则立即执行
function debounceIEF(event, time, flag) {
let timer = null;
return function (...args) {
clearTimeout(timer);
if (flag && !timer) {
event.apply(this, args);
}
//设定延时
timer = setTimeout(() => {
event.apply(this, args)
}, time);
}
}节流(throttle): 每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作,通常使用场景: 滚动条事件 或者
resize
事件,通常每隔100~500 ms
执行一次即可。两种实现方式- 时间戳
1
2
3
4
5
6
7
8
9
10
11// 2.防抖:时间戳和定时器两种实现方式
// 时间戳:第一次肯定触发,最后一次不会触发
function throttle(event, time) {
let pre = 0;
return function (...args) {
if (Date.now - pre > time) {
pre = Date.now;
event.apply(this, args);
}
}
}- 定时器
1
2
3
4
5
6
7
8
9
10
11
12// 定时器:第一次事件不会触发,最后一次一定会触发
function throttleTime(event, time) {
let timer = null;
return function (...args) {
if (!timer) {
let timer = null;
timer = setTimeout(() => {
event.apply(this, args)
}, time);
}
}
}- 结合版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 定时器和时间戳的结合班,相当于是节流和防抖的结合版,第一次和最后一次都会触发
function throttleAll(event, time) {
let pre = 0;
let timer = null;
return function (...args) {
if (Date.now - pre > time) {
clearTimeout(timer);
timer = null;
pre = Date.now;
event.apply(this, args)
} else if (!timer) {
timer = setTimeout(() => {
event.apply(this, args)
}, time);
}
}
}
5.10 数组降为
- 一般的数组降维的方式
1 | [1,2,[3]].flatMap(v=>v) |
将一个多维数组彻底降维
1
2
3const flatDeep = (arr)=>{
return Array.isArray(arr)?arr.reduce((a,b)=>[...a,flatDeep(b)],[]):[arr]
}
5.11 typeof和instanceof()有什么区别
typeof对于基本数据类型,除了null都可以显示正确的类型
instanceof对于对象,除了函数都会显示object,instanceof 可以正确的判断对象的类型,因为他是通过原型链查找是是否含有该类的原型
instanceof的湿实现
1 | function instance(right, left) { |
CSS
1.div+css相对于table标签的优势
- 改版的时候更加方便,只需要修改css样式即可
- 结构和样式分离
- 有利益SEO引擎的优化,排名更靠前
- 页面加载速度快,结构清晰,table标签会阻塞页面的加载
2. css sprite是什么?优缺点
- 是将多个图片合并到一张图片中,中background-position和元素尺寸调节需要显示的背景图案
- 优点:
- 减少了服务器请求的数量,缓解服务器的压力
- 增加图片信息重复度,增加压缩比,减小图片的大小
- 更换风格方便只需要在一张图片上更换颜色和样式即可
- 缺点:
- 合并麻烦
- 维护麻烦
3. display:none和visibility:hidden区别
- 二者都可以让元素不可见
- 区别
- 是否在渲染树:前者会从渲染树中消失,不占据然和空间;后者不会,会占据空间,只是内容不显示
- 继承性:前者是非继承性元素,修改子孙节点,无法显示;后者是继承性元素,修改子孙节点可以进行显示
- 是否回流和重排:前者修改普通流中的display会造成回流;后者只会发生重绘
- 是否读取元素的内容:读屏器不会读取前者的元素内容,会读取后者的内容
4. link和@import的区别
- link是html的方式,import是css的方式
- link在页面加载时和页面并行加载,不会阻塞页面;import在页面加载完成在进行串行加载,会阻塞页面,不利于SEO优化,也会出现FOUC(Flash Of Unstyled Content)情况(文档样式短暂失效)
- 浏览器对link样式的支持早于import,可以使用import对老式浏览器隐藏样式
- link可以通过rel指定候选样式
- import必须在样式之前引入,可以在css中引入其他文件
- link由于@import
5. FOUC是什么?如何避免
- FOUC:flash of Unstyled Content->用户定义的文档样式加载之前浏览器使用默认样式显示,用户样式渲染加载完成之后重新显示样式,造成页面闪烁
- 解决方法:将样式表放在head标签中和页面一起加载
6.什么是BFC,如何创建BFC,BFC的作用
- BFC:块级格式化上下文(block format coontent),是页面中一块独立渲染的区域
- 创建规则
- 根元素
- float
- position为absolute或者fixed
- display为inlion-block,block,table-cell,table-caption,flex,inline-flex
- overflow不为visible(auto,hidden,scroll)
- 布局规则
- 内部的Box会在垂直方向,一个接一个地放置。
- Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠(解决方案:给其中一个包裹一成div并将div设置成BFC)
- 每个元素的margin box的左边, 与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如 此。
- BFC的区域不会与float box重叠。
- BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
- 计算BFC的高度时,浮动元素也参与计算
- 作用:
- 设置自适应的两栏布局
- 清除内部浮动(将父元素设置成BFC)
- 防止垂直方向margin的重叠
7. position.float,display关系
- display为none,positionhe float都不起作用
- 否则,如果
position
取值为absolute
或者fixed
,框就是绝对定位的,float
的计算值为none
,display
根据下面的表格进行调整。 - 否则,如果
float
不是none
,框是浮动的,display
根据下表进行调整 - 否则,如果元素是根元素,
display
根据下表进行调整 - 其他情况下
display
的值为指定值 - 总结起来:绝对定位、浮动、根元素都需要调整
display
8.清除浮动
- 在父元素添加height
- 在结尾处添加div元素并设置clear:both
- 给父元素的div定义伪类:after和zoom
- 给父元素div设置overflow:hidden(BFC清除内部浮动)
- 设置父级div浮动,同时要设置宽度
- 结尾处添加br标签并clear:both
- 建议使用第三种方式
1 | .clear-fix:after{ |
8. 为什么要初始化css样式
- 浏览器的兼容性问题:不同的浏览器对样式的默认值是不同的,如果没有初始化或造成浏览器之间的页面显示存在差异
- 初始化会对SEO产生影响,但这也是不可避免
9. css3有哪些新的特性
新增的特性
- 新增了各种css选择器
- border-radius
- 多列布局
- 阴影和反射
- 文字特性text-shadow
- 旋转
- 线性渐变
新增的伪类:p:first-of
网络和浏览器
1. http状态码:

2. 安全(XSS,CSRF)
- XSS:
XSS
通过修改HTML
节点或者执行JS
代码来攻击网站。- 分为持久性和非持久性
- 持久性:就是恶意 的代码被服务器写入数据中
- 非持久性: 修改url参数的方式加入攻击代码,有道用户访问连接从而进行攻击
- 预防:最普遍的做法是转义输入输出的内容,对于引号,尖括号,斜杠进行转义
- 分为持久性和非持久性
- CSRF:跨站请求伪造(英语:
Cross-site request forgery
),也被称为one-click attack
或者session riding
,通常缩写为CSRF
或者XSRF
, 是一种挟制用户在当前已登录的Web
应用程序上执行非本意的操作的攻击方法,CSRF
就是利用用户的登录态发起恶意请求- 预防:
Get
请求不对数据进行修改- 不让第三方网站访问到用户
Cookie
- 阻止第三方网站请求接口
- 请求时附带验证信息,比如验证码或者
token
- 密码安全
- 加盐:通常需要对密码加盐,然后进行几次不同加密算法的加密
3. 跨域解决方案
3.1 跨域引起的原因
- 跨域问题:浏览器处于安全考虑,有同源策略,如果协议,端口或者域名有一个不同,发送ajax请求就会失败
3.1 解决
- Jsonp的原理:利用script标签没有跨域限制的漏洞.通过动态的创建script标签指向一个需要发送的地址并提供一个回调函数来接收数据
- jsonp简单兼容性可以,但是只限于get请求
1 | script src="http://domain/api?param1=a¶m2=b&callback=jsonp"></script> |
- CORS:跨域资源共享,需要前后端同时支持
- 实现主要在后端,只要后端开启Access-Control-Allow-Origin,表示哪些域名可以进行访问,设置通配符宝石所有终端都可以访问
- 服务器端解决:同源策略是浏览器给ajax限制的,服务器端没有限制,可以让服务器端去获取信息在响应给自己的客户端(引入express框架中的request第三方模块)
- nginx代理服务器:在代理服务器中拦截请求转发给实际的请求网站
- possMessage跨域:允许来自不同源的的脚本采用异步方式进行有限通信,可以实现跨文本,多窗口,跨域消息传递,使用场景(iframe通信,同浏览器多窗口跨域访问)
1 | // 发送消息端 |
- websocket通信:是一种双向通信协议,在建立连接之后客户端和服务器端都能主动向对方发送或者接收数据而不受同源策略的限制
3.1 为什么使用多个域名存储网站资源更有效
- 有利于CDN缓存
- 突破浏览器的并发限制
- 节约cookie宽带
- 减少主域名的连接数,加快页面的响应速度
- 防止不必要的安全问题
4. 浏览器的渲染原理
4.1 浏览器的渲染过程
- 浏览器接收到 HTML 文件并转换为 DOM 树
- 将 CSS 文件转换为 CSSOM 树
- 生成渲染树:我们生成
DOM
树和CSSOM
树以后,就需要将这两棵树组合为渲染树,当浏览器生成渲染树以后,就会根据渲染树来进行布局(也可以叫做回流),然后调用GPU
绘制,合成图层,显示在屏幕上。
4.2 dom操作慢的原因:
- 因为
DOM
是属于渲染引擎中的东西,而JS
又是JS
引擎中的东西。当我们通过JS
操作DOM
的时候,其实这个操作涉及到了两个线程之间的通信,那么势必会带来一些性能上的损耗。操作DOM
次数一多,也就等同于一直在进行线程之间的通信,并且操作DOM
可能还会带来重绘回流的情况,所以也就导致了性能上的问题。
4.3 插入几万个 DOM,如何实现页面不卡顿?
分批次部分渲染
DOM
requestAnimationFrame
的方式去循环的插入
DOM虚拟滚动(
virtualized scroller
)原理:只渲染可视区域内的内容,非可见区域的那就完全不渲染了,当用户在滚动的时候就实时去替换渲染的内容
4.4 重绘和回流
- 重绘和回流会在我们设置节点样式时频繁出现,同时也会很大程度上影响性能。
- 重绘是当节点需要更改外观而不会影响布局的,比如改变
color
就叫称为重绘 - 回流是布局或者几何属性需要改变就称为回流。
- 回流必定会发生重绘,重绘不一定会引发回流。回流所需的成本比重绘高的多,改变父节点里的子节点很可能会导致父节点的一系列回流。
- 出现的场景,并且会导致性能问题
- 改变
window
大小 - 改变字体
- 添加或删除样式
- 文字改变
- 定位或者浮动
- 盒模型
- 改变
4.5 减少重绘和回流的方案:
- 用
transform
替代top
- 使用
visibility
替换display: none
,因为前者只会引起重绘,后者会引发回流(改变了布局) - 不要把节点的属性值放在一个循环里当成循环里的变量
- 要使用
table
布局,可能很小的一个小改动会造成整个table
的重新布局 - 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用
requestAnimationFrame
CSS
选择符从右往左匹配查找,避免节点层级过多- 将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点。比如对于
video
标签来说,浏览器会自动将该节点变为图层。- 设置为图层的方式有:
- will-change,
video
、iframe
标签
- 设置为图层的方式有:
- 静态资源尽量使用CDN加载(浏览器对单个域明有并发请求上线)
- 使用webpack优化项目
- 对于webpack4,打开项目使用production模式,会自动开启代码压缩
- 使用ES6模块来开启tree shaking,这个技术可以移除没有使用到的代码
- 优化图片,使用base64的方式写入文件
- 按照路由拆分代码,路由懒加载的方式
5 存储(session.cookie,localstorage,sessionStorage)
5.1有几种方式可以实现存储功能,分别有什么优缺点?
cookie
是网站为了标示用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)- cookie数据始终在同源的http请求中携带(即使不需要),记会在浏览器和服务器间来回传递
sessionStorage
和localStorage
不会自动把数据发给服务器,仅在本地保存- 存储大小:
cookie
数据大小不能超过4ksessionStorage
和localStorage
虽然也有存储大小的限制,但比cookie
大得多,可以达到5M或更大
- 有期时间:
localStorage
存储持久数据,浏览器关闭后数据不丢失除非主动删除数据sessionStorage
数据在当前浏览器窗口关闭后自动删除cookie
设置的cookie
过期时间之前一直有效,即使窗口或浏览器关闭
5.2 session和cookie
session
: 将user agent
和server
之间一对一的交互,抽象为“会话”,进而衍生出“会话状态”,也就是session
的概念cookie
:它是一个实际存在的东西,http
协议中定义在header
中的字段,可以认为是session
的一种后端无状态实现- session
的常见实现要借助
cookie来发送
sessionID
5.3 web开发中会话跟踪中的方式有哪些
- session
- cookie
- url重写
- 隐藏input
- ip地址
6. 浏览器中的 Event Loop事件循环
6.1 什么是执行栈
- 执行栈认为是一个存储函数调用的栈结构,遵循先进后出的原则
6.2 异步代码执行顺序?解释一下什么是 Event Loop
?
JS
在执行的过程中会产生执行环境,这些执行环境会被顺序的加入到执行栈中。如果遇到异步的代码,会被挂起并加入到Task
(有多种task
) 队列中。一旦执行栈为空,Event
Loop
就会从Task
队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说JS
中的异步还是同步行为补充:
- 不同的任务源会被分配到不同的
Task
队列中,任务源可以分为 微任务(microtask
) 和 宏任务(macrotask
)。在 ES6 规范中,microtask
称为 jobs,macrotask
称为task
。 - 微任务包括
process.nextTick
,promise
,Object.observe
,MutationObserver
- 宏任务包括
script
,setTimeout
,setInterval
,setImmediate
,I/O
,UI renderin
- 宏任务中包括了
script
,浏览器会先执行一个宏任务,接下来有异步代码的话就先执行微任务
- 不同的任务源会被分配到不同的
所以正确的一次 Event loop 顺序是这样的
- 执行同步代码,属于宏任务
- 执行栈为空,判断是否有微任务
- 执行完所有的微任务
- 必要的话渲染ui
- 执行下一轮的evenloop,执行宏任务中的异步代码
7 性能优化
- 图片优化
- 图片加载优化
- DNS预解析
- 节流
- 防抖
- 预加载
- 预渲染
- 懒执行
- 懒加载
- CDN
8.浏览器的缓存和 什么是 Service Worker
8.1 什么是 Service Worker
Service Worker
是运行在浏览器背后的独立线程,一般可以用来实现缓存功能。使用Service Worker
的话,传输协议必须为HTTPS
。因为Service Worker
中涉及到请求拦截,所以必须使用HTTPS
协议来保障安全Service Worker
实现缓存功能一般分为三个步骤:首先需要先注册Service Worker
,然后监听到install
事件以后就可以缓存需要的文件,那么在下次用户访问的时候就可以通过拦截请求的方式查询是否存在缓存,存在缓存的话就可以直接读取缓存文件,否则就去请求数据。
8.2 浏览器的缓存
作用:浏览器的缓存对优化性能是很重要的点,良好的缓存策略可以降低资源的重复加载提高网站的加载速度
浏览器缓存通常分为两种
- 强缓存:可以通过两种响应头实现expires/cache-control,强缓存表示在缓存期间不用请求,status code为200
1
2Expires: Wed, 22 Oct 2018 08:41:00 GMT
Cache-control: max-age=30- 协商缓存:如果缓存过期了可采用协商缓存,协商缓存需要请求,有效会返回304状态,实现方式有两种
- Last-Modified
- If-Modified-Since
9. 三次握手和四次挥手
TCP提供了一种可靠、面向连接、字节流、传输层的服务,采用三次握手建立一个连接。采用4次挥手来关闭一个连接。
TCP是一种面向连接的单播协议,在发送数据前,通信双方必须在彼此间建立一条连接。所谓的“连接”,其实是客户端和服务器的内存里保存的一份关于对方的信息,如ip地址、端口号等。
TCP可以看成是一种字节流,它会处理IP层或以下的层的丢包、重复以及错误问题。在连接的建立过程中,双方需要交换一些连接的参数。这些参数可以放在TCP头部。
9.1 三次握手
- 客户端发送一个SYN段,并指明客户端的初始序列号,即ISN(c).
- 服务端发送自己的SYN段作为应答,同样指明自己的ISN(s)。为了确认客户端的SYN,将ISN(c)+1作为ACK数值。这样,每发送一个SYN,序列号就会加1. 如果有丢失的情况,则会重传。
- 为了确认服务器端的SYN,客户端将ISN(s)+1作为返回的ACK数值。
9.2 四次挥手
- 客户端 – FIN –> 服务端,关闭客户端到服务器端的数据传送 FIN—WAIT
- 服务端 – ACK –> 客户端,服务器端将接受到的序列号加1作为返回值发送给客户作为响应 CLOSE-WAIT
- 服务端 – ACK,FIN –> 客户端, 关闭服务器端到客户端的连接LAST-ACK
- 客户端 – ACK –> 服务端,CLOSED
9.3 从输入URL到浏览器页面展示的步骤
- 浏览器接收到url交给DNS进行域名为ip地址(浏览器缓存,本地缓存,hosts文件,路由器缓存,ISD DNS缓存,DNS域名递归查询(会存在负载均衡ip地址不是同一个)),
- 打开socket与目标的ip地址端口建立tcp连接(通过三次握手)
- 连接建立成功发送http请求,服务器接收到请求并进行解析,将报文头通过tcp链接发送回浏览器
- 浏览器接收到http响应,浏览器根据根据响应的状态码进行处理
- 将下载完的内容转交给Renderer进程管理。Renderer进程开始解析css rule tree和dom tree,这两个过程是并行的,所以一般我会把link标签放在页面顶部。解析绘制过程中,当浏览器遇到link标签或者script、img等标签,浏览器会去下载这些内容,遇到时候缓存的使用缓存,不适用缓存的重新下载资源。
- 解析完成进行页面渲染(资源缓存,资源解码,解析HTML文档,构建dom树),CSSOM树,js解码)
- DOM 树与 CSSOM 树合并后形成渲染树。
- 渲染树只包含渲染网页所需的节点。
- 布局计算每个对象的精确位置和大小。
- 最后一步是绘制,使用最终渲染树将像素渲染到屏幕上。
- 绘制结束后,关闭TCP连接,过程有四次挥手
9.4 浏览器内核的理解,以及主流浏览器的内核
9.4.1 浏览器内核的理解
- 内核主要分为两部分:渲染引擎和js引擎
- 渲染引擎负责取得网页的内容和计算网页的显示方式,然后输出到显示器或者打印机.浏览器内核的不同对网页的解析方式会有所不同
- .js引擎主要是解析和执行javascript代码实现页面的动态效果
9.4.1 主流浏览器的内核
- IE: trident内核
- firefox:gecko内核
- safari:webkit内核
- opera:起初presto->谷歌内核blink
- 谷歌:blink
10 http/https
https: 较为安全的网络传输协议
- 证书(公钥)
SSL
加密- 端口
443
TCP:
- 三次握手
- 四次挥手
- 滑动窗口: 流量控制
- 拥塞处理
- 慢开始
- 拥塞避免
- 快速重传
- 快速恢复
缓存策略: 可分为 强缓存 和 协商缓存
# Vue相关面试
1. mvvm原理理解
mvvm的理解
- 在
MVVM
架构下,View
和Model
之间并没有直接的联系,而是通过ViewModel
进行交互,Model
和ViewModel
之间的交互是双向的, 因此View
数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View
上。 ViewModel
通过双向数据绑定把View
层和Model
层连接了起来,而View
和Model
之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全由MVVM
来统一管理
- 在
组成:
View
:界面Model
:数据模型ViewModel
:作为桥梁负责沟通View
和Model
UI
是通过数据驱动的,数据一旦改变就会相应的刷新对应的UI
,UI
如果改变,也会改变对应的数据。在
MVVM
中,最核心的也就是数据双向绑定,例如Angluar
的脏数据检测,Vue
中的数据劫持。Vue
内部使用了Object.defineProperty()
来实现双向绑定,通过这个函数可以监听到set
和get
的事件。
vue实现的整个流程
- 第一步:解析模板成 render 函数
- 第二步:响应式开始监听
Object.defineProperty
- 将
data
的属性代理到vm
vue实例上
- 第三步:首次渲染,显示页面,且绑定依赖
- 初次渲染,执行
updateComponent
,执行vm._render()
- 执行
render
函数,会访问到vm.list vm.title
(data属性) - 会被响应式的
get
方法监听到 - 执行
updateComponent
,会走到vdom
的patch
方法 patch
将vnode
渲染成DOM
,初次渲染完成
- 初次渲染,执行
- 第四步:
data
属性变化,触发rerender
虚拟dom提高性能
- 虚拟
dom
相当于在js
和真实dom
中间加了一个缓存,利用dom diff
算法避免了没有必要的dom
操作,从而提高性能 - 实现步骤
- 用
JavaScript
对象结构表示 DOM 树的结构;然后用这个树构建一个真正的DOM
树,插到文档当中 - 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
- 把2所记录的差异应用到步骤1所构建的真正的
DOM
树上,进行局部视图更新
- 用
2. vue生命周期
周期的理解
- 答:总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后
- 创建前/后: 在
beforeCreate
阶段,vue
实例的挂载元素el
和数据对象data
都为undefined
,还未初始化。在created
阶段,vue
实例的数据对象data
有了,el还没有 - 载入前/后:在
beforeMount
阶段,vue
实例的$el
和data
都初始化了,但还是挂载之前为虚拟的dom
节点,data.message
还未替换。在mounted
阶段,vue
实例挂载完成,data.message
成功渲染。 - 更新前/后:当
data
变化时,会触发beforeUpdate
和updated
方法 - 销毁前/后:在执行
destroy
方法后,对data
的改变不会再触发周期函数,说明此时vue
实例已经解除了事件监听以及和dom
的绑定,但是dom
结构依然存在
- 创建前/后: 在
什么是vue声明周期
- Vue 实例从创建到销毁的过程,就是生命周期。从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、销毁等一系列过程,称之为 Vue 的生命周期。
vue声明周期的作用
- 声明周期中有钩子函数可以控制整个vue实例不同时期的逻辑操作
vue生命周期总共有几个阶段?
- 创建,加载,更新,销毁
首次页面加载触发钩子函数
会触发下面这几个beforeCreate
、created
、beforeMount
、mounted
。
dom渲染哪个周期完成
DOM
渲染在 mounted
中就已经完成了
Vue Compiler实现
- 模板解析这种事,本质是将数据转化为一段
html
,最开始出现在后端,经过各种处理吐给前端。随着各种mv*
的兴起,模板解析交由前端处理。 - 总的来说,
Vue complier
是将template
转化成一个render
字符串。
3. Vue实现数据双向绑定的原理:Object.defineProperty()
vue
实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()
来劫持各个属性的setter
,getter
,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通Javascript
对象传给 Vue 实例来作为它的data
选项时,Vue 将遍历它的属性,用Object.defineProperty()
将它们转为getter/setter
。用户看不到getter/setter
,但是在内部它们让Vue
追踪依赖,在属性被访问和修改时通知变化。vue的数据双向绑定 将
MVVM
作为数据绑定的入口,整合Observer
,Compile
和Watcher
三者,通过Observer
来监听自己的model
的数据变化,通过Compile
来解析编译模板指令(vue
中是用来解析模板字符串的
),最终利用watcher
搭起observer
和Compile
之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input
)—>数据model
变更双向绑定效果还可以通过proxy代理实现双向绑定
3.1 双向绑定的实现defineProperty
3.2 proxy实现(ES6中新增的方法)
4. 前端路由的原理
本质就是监听
URL
的变化,然后匹配路由规则,显示相应的页面,并且无须刷新页面。目前前端使用的路由就只有两种实现方式,都不会引起页面的刷新Hash
模式:1
www.test.com/#/ 就是 Hash URL,当 # 后面的哈希值发生变化时,可以通过 hashchange 事件来监听到 URL 的变化,从而进行跳转页面,并且无论哈希值如何变化,服务端接收到的 URL 请求永远是 www.test.com
History
模式1
2History 模式是 HTML5 新推出的功能,主要使用 history.pushState 和 history.replaceState 改变 URL
当用户做出浏览器动作时,比如点击后退按钮时会触发 popState 事件
两种模式对比
Hash
模式只可以更改#
后面的内容,History
模式可以通过API
设置任意的同源URL
History
模式可以通过API
添加任意类型的数据到历史记录中,Hash
模式只能更改哈希值,也就是字符串Hash
模式无需后端配置,并且兼容性好。History
模式在用户手动输入地址或者刷新页面的时候会发起URL
请求,后端需要配置index.html
页面用于匹配不到静态资源的时候
5. bable原理
ES6、7
代码输入 ->babylon
进行解析 -> 得到AST
(抽象语法树)->plugin
用babel-traverse
对AST
树进行遍历转译 ->得到新的AST
树->用babel-generator
通过AST
树生成ES5
代码
WebPack
1.优化打包速度
操作系统
1. 进程和线程
本文链接: https://sparkparis.github.io/2020/06/10/%E9%9D%A2%E8%AF%95-3/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!