纯干货,最近在准备,记录一下,以后方便看
讲讲Promise
Promise是处理异步操作的一种方案,用来解决回调地狱的问题,通过对象的状态来管理异步操作的结果,提供更可靠更优雅的方式来处理异步代码。
Promise对象一共有三个状态,状态一经修改不能改变,pending初始状态,fulfilled完成状态,rejected拒绝状态,resolve()将状态由pending改为fulfilled并传值,then()方法处理成功结果,rejected()将状态由pending改为rejected并传值,catch()方法处理失败结果,并且Promise对象可以由finally()处理
then(): 接收resolve()成功结果
catch(): 接收rejected()失败结果
finally(): 最终都要执行
all(): 被reject时立即返回rejected 或 全部resolve 返回resolve
allSettled(): 只当所有的Promise都返回了才返回结果(结果都在then中运行),全resolve才resolve
race(): 返回第一个返回结果
any(): 返回第一个返回resolve结果的
Promise优点: 代码可读性提高,解决回调地狱问题,通过catch()更好的处理错误情况,提供更多的功能all() race()处理多个异步操作
Promise缺点: 无法取消Promise,已经创建无法取消,可能导致内存泄漏,成功失败就无法改变
讲讲async+await
aynsc声明函数是异步,返回一个promise
二者提供更加简易的异步处理方法
await必须搭配async使用
await + Promise: await会等待Promise状态改为fulfilled,如果成功将剩余任务返回到微任务队列,如果失败,剩余任务不会返回到微任务队列,即后面的代码都走不到
await + 普通值: 相当于Promise状态改为fulfilled,将剩余任务返回到微任务队列
await + 函数: 会立即执行该函数,执行完后才将剩余任务返回到微任务队列
地址栏敲下回车发生了什么
url解析
dns查询
建立tcp连接
发送http请求
服务器响应请求
html文件构建dom树,css构建cssom树
dom树和cssom树合并为渲染树
重绘重排构建页面
断开tcp连接
defer,async
defer: 不阻止dom构建,js加载完在dom完全构建后执行
async: 不阻止dom构建,js加载完后立即执行
js阻塞dom解析,css阻塞js执行,浏览器遇到script没有async和defer时触发页面渲染
dns查询过程
浏览器检查有没有dns缓存
用户递归访问本地域名服务器
本地域名服务器迭代查询根域名服务器,顶级域名服务器,权限域名服务器
返回ip地址
重排重绘
重排: 计算dom位置和几何信息
重绘: 上样式
history,hash原理和区别
history利用HTML5 History Interface 新增的pushState和replaceState方法,不影响页面加载情况下动态修改浏览器历史记录,通过监听popState事件,响应用户前进后退操作。History.go,History.back,History.forward
hash通过window.location.hash属性和onhashchange事件监听urlhash值的变化,执行对应的js切换网页。#及#后为散列值,散列值不发送到服务端,所以改变hash不会重新加载页面。散列值变化时,可通过location.hash来获取、设置hash值
histroy:url中不包含#,兼容性较差,url改变属于http请求会向服务端发起请求,利于SEO,url与后端配置页面不同会返回404,会被缓存,有历史记录
hash:url中包含#,兼容性较好,url改变不会向服务端发起请求,不利于SEO,无历史记录
处理跨域
协议,域名,端口,有一个不一样就是跨域
cors, devserver设置proxy代理, jsonp接口
tcp和udp的区别
tcp面向连接,基于字节流,一对一,数据可靠,效率较低,流量控制,拥塞控制
udp无连接,基于数据报,一对多,一对一,多对多,不可靠,效率高
强缓存,协商缓存
浏览器加载资源时,会根据http header判断是否命中强缓存,若命中直接读取不发起请求。若未命中,浏览器发送请求至服务端,根据服务端中的http header判断是否命中协商缓存,若命中,服务端通知浏览器直接读取缓存,不返回资源。若未命中,返回资源
强缓存:服务端通过响应头设置cache-control: 相对时间 、Expires: 服务端绝对时间
Expires因客户端本地时间可修改,导致客户端和服务端时间不一致,导致强缓存直接失效
二者都存在时,cache-controle优先级高于Expires
协商缓存:
浏览器向服务端第一次请求该资源时,响应头中有Last-Modified 并设置为最后一次修改时间。第二次请求该资源时,请求头中加上If-Modified-Since 并设置为上次请求该资源时Last-Modifed的值,服务端收到If-Modifed-Since与文件Last-Modifed对比,若命中即无变化,不返回资源,返回304通知浏览器读取本地缓存,不更新Last-Modifed。若未命中即变化,返回200,返回资源,更新Last-Modified
Etag过程同Last-Modifed类似,Etag的值是文件的唯一标识hash,响应头中携带Etag,请求头中携带If-None-Match,值为上次请求该资源时响应头中Etag的值,唯一区别是返回304时会携带etag,即使etag没有变化
Etag&&Last-Modified,优先判断Etag,Etag解决Last-Modified的一些问题,Last-Modifed检查粒度是S级,如果1S内改变文件内容多次,无法判断。某些服务端无法精确得到文件最后修改时间。文件内容不改变,但是最后修改时间变化了,不希望浏览器认为文件修改了
tcp三次握手,四次挥手
三次握手:
客户端向服务端SYN=1,seq=x请求连接
服务端向客户端发送SYN=1,ACK=1,seq=y,ack=x+1
客户端向服务端发送ACK=1,seq=x+1,ack=y+1
四次挥手:
客户端向服务端FIN=1,seq=x请求释放连接
服务端向客户端发送ACK=1,ack=x+1,seq=y
服务端向客户端发送FIN=1,ACK=1,seq=z,ack=x+1
客户端向服务端发送ACK=1,ack=z+1,seq=x+1
等待2MSL,关闭连接
osi7层模型
物理层,数据链路层,网络层,传输层,会话层,表示层,应用层
为什么tcp不是二次握手或者四次握手
SYN, ACK, seq, ack, FIN
SYN: 1, 请求建立连接
FIN: 1,请求断开连接
ACK: 1,表示收到
seq: 客户端序列号
ack: 服务端序列号
tcp确保双方都具有数据收发的能力,通过同步序列号来完成
客户端携带seq传给服务端,服务端携带ack传给客户端,
二次握手: 如果网络延迟高,第一次客户端向服务端发送的请求中,服务端向客户端应答没有及时到达,客户端第二次发送请求,此时网络好,服务端的应答及时到达,两端建立连接开始通信,通信完后断开连接,此时第一次服务端的应答到达客户端,实际上断开连接了,但是能传输数据,造成安全问题,不稳定,三次握手就不会出现这种情况,因为当客户端收到服务端应答后还会发送一个应答应答消息
四次握手: 避免资源浪费,可把第二次和第三次合并,将seq和ack包合并为一个,也可实现双方同步序列号
http1.0 1.1 2.0区别
1.0: 只能短连接,仅支持get post 方法,多次请求需建立多次tcp连接,发送请求需等上次请求响应
1.1: 解决短连接问题,新增http请求方法,不用等待请求响应,同时间多次发送请求但是响应顺序需保持与请求顺序一致
2.0: 解决相应顺序须保持与请求顺序一致问题,多路复用,二进制分帧,首部压缩,服务器推送
二进制分帧: 发送数据以帧发送
首部压缩: 静态表和动态表,静态表根据http规范提前定义好请求头部字段,动态表客户端和服务端各自维护一张表,发送请求时先看动态表里有没有,有就用,没有就看静态表,有就用,没有就哈夫曼编码,再存动态表,服务端查过表之后没有就解码再存动态表
服务器推送: 发送请求时服务器将其他静态资源一起推送给客户端(请求html时,返回html和css)
localstorage,sessionstorage,cookie区别
localstorage: 浏览器存储,5m,调用api存储和调用api删除
sessionstorage: 浏览器存储,5m,调用api存储和调用api删除或关闭页面删除
cookie: 浏览器发送请求时自动携带cookie里的数据,4kb,容易被劫持,过期自动删除,服务器返回的响应头中set-cookie设置过期时间
get,post区别
get: 会被浏览器缓存,参数在url里,较不安全,只支持url编码,受浏览器设计影响,大小有限制,会被保存在历史记录中
post: 不会被浏览器缓存,较安全,参数在request body里,支持多种编码,大小无限制,不会被保存在历史记录中
cookie,session区别
cookie存储在浏览器,session存储在服务器
cookie存储字符串,session存储任意数据
cookie较不安全,session存在服务器较安全
cookie大小4kb,session远大于4kb
cdn缓存原理
CDN的基本原理就是广泛采用各种缓存服务器,将这些缓存服务器分不到用户访问相对集中的区域或网络中,在用户访问时,利用全局负载技术将用户的访问指向距离最近的工作正常的缓存服务器,由缓存服务器直接响应(参考反向代理).CDN的关键技术在于内容存储和分发技术.
http,https区别
https是http的安全版本,https = http + ssl/tls,http明文传输,https加密传输
http和https使用连接方式不同,默认端口不同,http为80,https为443
https需设计加密和多次握手,性能较差
https需ssl证书,成本较高
https加密有对称加密(采用协商的密钥对数据加密),非对称加密(实现身份认证和密钥协商),摘要算法(验证信息完整性),数字签名(身份验证)
websocket理解,运用
全双工通信,长连接
const ws = new WebSocket(‘’)
ws.onopen(),ws.onmessage(),ws.close(),ws.onerror(),ws.send(),ws.close()
心跳机制: 发送一个小数据包,根据服务端返回的结果,如果是断开连接,则再次调用连接websocket
js数据类型
基本数据类型: Number,Boolean,Null,Undefined,String,Symbol
复杂数据类型: Array,Function,Object,Date,Regexp
判断数据类型方法
type: 判断基本数据类型
instanceof: 判断复杂数据类型,原理: 根据原型链查找
Object.prototype.toString.call(): 通用
数组方法
push()末尾新增,修改原数组
slice()剪切,不修改原数组
splice()删除插入替换,修改原数组
shift()头部删除,修改原数组
pop()末尾删除,修改原数组
unshift()头部新增,修改原数组
concat()拼接,不修改原数组
join()每个元素中添加进元素,返回字符串,不修改原数组
indexOf()搜索数组元素
filter()过滤,不修改原数组
map()循环遍历,不修改数组
forEach()循环遍历,不修改原数组
reduce()循环遍历,不修改原数组
sort()排序,修改原数组
reverse()倒叙排序,修改原数组
toString()转换为字符串,不修改
js闭包
内部函数引用外部函数的变量,外部函数返回内部函数,形成闭包,能让外部访问内部变量,可能造成内存泄漏
作用域链
当使用变量或者方法时,会一层层向外查找变量或者方法
判断到达底部
element.scrollTop(可变): 他的滚动条滚动距离element.scrollLeft(可变): 他的横向滚动条滚动距离element.scrollWidth: content+padding+border 宽element.scrollHeight: content+padding+border 高element.offsetTop: 上边距离父级元素的距离element.offsetLeft: 左边距离父级元素的距离element.offsetWidth(只读): content+padding+border 宽element.offsetHeight(只读): content+padding+border 高element.clientTopelement.clientLeftelement.clientWidth: content+padding 宽element.clientHeight: content+padding 高window.scrollY: 滚动条滚动距离document.documentElement.scrollTop: 滚动条滚动距离window.scrollX: 横向滚动条滚动距离document.documentElement.scrollLeft: 横向滚动条滚动距离element.getBoundingrect(): 元素(上下左右)距离视口的距离,width(宽),height(高)window.innerHeight: 视口高度window.innerWidth: 视口宽度页面底部: document.body(document.documentElement).scrollTop – scrollHeight === clientHeight元素顶部到页面顶部的距离: element.getBoundingrect().top + window.scrollY</span>
防抖,节流
防抖:只执行最后一次
节流:只执行第一次
web常见攻击方式
XSS跨站脚本攻击:
存储型: 攻击者将恶意代码提交到目标网站数据库,用户访问xss脚本的页面,服务端将恶意代码拼接在html中返回,恶意代码执行,窃取用户数据
反射型: 攻击者构造特殊url,用户打开此url,服务端将恶意代码从url取出拼接在html中返回,恶意代码执行,窃取用户数据
dom型: 攻击者构造特殊url,用户打开此url,前端取出url中的恶意代码并执行,窃取用户数据
解决: 实体编码,<用<等
CSRF跨站请求伪造:
用户登录a.com,保留cookie
攻击者诱导用户访问b.com
b.com向a.com发送请求
a.com接收请求,验证请求,确认是用户凭证,误认为是用户发送请求
a.com以用户执行请求
SQL注入攻击: 输入验证不严谨,构造恶意sql语句查询数据库
解决: 加强输入验证
虚拟列表优化
列表每个item高度固定,获取到整个包裹区的高度,视口高度除以item高度计算视口容纳数据个数。包裹区占位,内容区与包裹区顶部重合,内容区高度是末索引 * item高度。先获取到包裹区顶部在滚动条的位置,滚动条位置到了包裹区顶部(包裹区顶部到了页面顶部),(滚动条位置-包裹区顶部离页面的距离)计算出首索引,通过视口容纳数据个数计算出末索引,赋值给内容区,展示
处理并发请求
请求Promise数组,正在进行的Promise数组
Promise.race()
js实现继承
构造函数继承,原型链继承,原型式继承,寄生式继承,寄生组合式继承
对应js代码实现继承
js事件循环
同步,异步任务
调用栈: 从文件入口开始,同步任务执行入栈
宏任务: settimeout,setinterval,IO
微任务: Promise回调,网络请求
执行完同步任务,遇到异步任务,根据类型放入宏任务队列/微任务队列,同步任务执行完,先执行所有的微任务,再执行第一个宏任务.宏任务可能会产生同步任务/宏任务/微任务,执行完同步任务,遇到异步任务放入对应的队列,执行完微任务,执行下一个异步任务
函数缓存柯里化
套壳函数,函数里return函数,传入两个参数的函数拆解为两个传一个参数的函数,能先传入一个参数将第一个函数的结果存起来
Set,Map,WeakSet,WeakMap
Set: 无重复元素, set.add() sss.delete() sss.clear() sss.keys() sss.values() sss.size() sss.has()
Map: map.set() map.get() map.has() map.clear() map.delete() map.keys() map.values()
WeakSet: 可被垃圾回收,如果没有其他值引用该值,该值直接被回收,值只能是对象和symbol
WeakMap: 可被垃圾回收,如果没有其他值引用该值,该值直接被回收
说说原型链
构造函数有自己的prototype对象,prototype的constructor指回构造函数,构造函数构造的实例对象有个__proto__属性指向prototype,当实例对象使用属性或方法时,在自己的属性方法中找,找不到就通过__proto__查找prototype,prototype没有就一层层找到window对象最后指向null。一般可以在顶级对象上定义用于属性或方法的共享,比如Array.prototype.func
new操作符具体干了什么
创建一个对象,构造函数的this指向该对象,为该对象赋构造函数的属性和方法,返回该对象
普通函数,箭头函数区别
箭头函数没有this指向,他的this指向在被创建时就被确定,从自己作用域链上一层继承this,无法调用call,apply,bind,没有arguments,不能作为构造函数,没有prototype
call,apply,bind区别
call(this, 参数,参数): 立即调用
apply(this, [参数,参数]): 立即调用
bind(this, 参数, 参数): 返回一个新的函数
说说作用域
全局作用域: 全局定义的作用域
局部作用域: 函数内就是局部作用域
块级作用域: const,let形成块级作用域
作用域链: 内部作用域可访问到外部作用域,代码在当前作用域查找变量时,如果当前作用域没找到会沿着作用域链向上查找直到找到或者到达全局作用域为止即无此变量
说说浅拷贝,深拷贝
浅拷贝: 公用一个地址,改变该对象,另一个对象也改变了 Object.assign
深拷贝: JSON.parse(JSON.toString(obj)) 循环调用会有问题,需要自己手写,递归调用赋值,根据数据类型判断
说说内存泄漏
引用计数: 引用了次数+1,次数为0时自动释放
标记清除: 从root下是否可达,不可达自动释放
避免内存泄漏: 手动释放 xxx = null
说说盒子模型
box-sizing设置
border-box: (content + padding + border)width + margin
content-box: (content)width + padding + border + margin
说说css选择器优先级
内联>id选择>类选择>标签选择
!important无视权重规则,直接显示!impotant
权重相同情况下,后面的会覆盖前面的样式
说说rem,em,vw,vh
rem: 根据root根元素字体fontsize的大小
em: 根据父元素字体fontsize的大小
vw: 根据视口100%宽度,x就是x%
vh: 根据视口100%高度,y就是y%
说说BFC机制
块级格式化上下文,用于清除浮动,解决外边距合并问题,计算高度时,浮动元素也会计算进去
overflow:hidden || auto || overlay || scroll
float: left || right
position: absolute || fixed
display: inline-block
display: table-cell
display: flex || inline-flex
隐藏页面元素
display: none
position: absolute
visibility: hidden
height: 0, width: 0
opacity: 0
元素水平垂直居中
display: flex, align-items: center, justify-content: center
display: grid, align-items: center, justify-content: center
display: relative, display:absolute, top: 50%, left: 50%,transform: transparent(-50%, -50%)
画三角形
height: 0, width: 0,border-style: solid,border-color: transparent transparent black,border-width: 0 50px 50px
vue2,vue3区别
vue2: v-for优先级高于v-if。响应式原理object.defineProperty()的get/set,发布订阅,劫持属性进行响应式,但是当劫持对象的新增对象时,劫持不到,需要通过Vue.set手动劫持响应。optionsAPI,data(),methods,computed()。生命周期beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed。只允许有一个根节点。
Vue3 : v-if优先级高于v-for。响应式原理proxy,不会有object.defineProperty()的问题,因此vue3也就没有Vue.set。compositionAPI。生命周期:setup(),onbeforemount,onmounted,onbeforeupdate,onupdated,onbeforeunmount,onunmounted。允许多个根节点。
实例挂载过程发生什么
初始化:实例化Vue,初始化生命周期,初始化事件系统,初始化响应式数据
编译:模板预编译,编译成render函数
渲染:创建虚拟dom,虚拟dom转成真实dom,挂载到dom
更新:触发更新,生成新的虚拟dom,虚拟dom diff对比,更新真实dom
生命周期钩子调用:beforeCreate,created,beforeMount,mounted
为什么data是函数而不是对象
根组件data可以是函数也可以是对象,因为data不会被多次引用
组件data必须为函数,防止多个组件实例共享一个data对象,造成变量污染互相影响
综上,data是函数而不是对象
vue组件通信
prop,emit
ref
$parent,$root,$children
eventbus
provide,inject
vuex
localstorage,sessionstorage
vuex
state, getter, mutations, actions(异步提交mutations)
路由权限管理
路由定义时,meta字段定义roles,在router.beforeEach()里判断后端返回的用户信息中roles数组字段有没有该route里meta字段的role,有就进,没有就重定向或者报错页面,登陆后后端给出菜单,router.addRoute()
key原理
key一般用于v-for,相当于给每个循环遍历来的dom取了一个名字,当数据变化引起更新视图时,不用key的话,Vue的diff算法会遍历所有的数据从头到尾重新渲染一遍,用key可以让diff算法通过key顺序来对比,这样就可以只更新真正发生变化的dom,减少diff算法次数,带来性能的提升,提高渲染效率
diff算法
vue采用虚拟dom技术,即渲染视图时,会复制轻量级的对应虚拟dom,当数据变化引起视图变化时,会先更新虚拟dom,diff算法找到虚拟dom和真实dom的差异从而更新真实的视图,而不是将全部视图从头到尾更新一遍
SPA首屏优化
减小入口文件体积:路由懒加载
静态资源本地缓存
组件重复打包
gzip打包
精灵图
小图片base64编码
UI框架按需加载
SSR服务端渲染
MVVM设计模式
model,view,viewmodel,数据驱动视图,视图驱动数据
webpack,vite区别
webpack是模块构建工具,生态完善可配置性高,适用于大型项目,热更新全量更新,哪怕更新小文件,都会重新编译整个应用,编译打包完运行在浏览器
vite是前端构建工具,生态较不完善,适用于中小型项目,冷启动,即时热更新,开发模式下热更新增量更新,只更新变化的,利用es模块编译
微信小程序,h5区别
小程序是双线程架构,逻辑层(jscore)和渲染层(webview)是分开的,通过数据桥连接,性能提升不会阻塞。支持热启动,离线访问友好。深度集成,如微信小程序可直接调起微信支付等。增量更新,更新版本热补丁。不支持document和window对象,无法实现复杂效果。脱离w3c学习成本较高
简述微信小程序原理
微信小程序采用wxml
、wxss
、javascript
进行开发,本质是一个单页应用,所有的页面渲染和事件处理,都在一个页面内进行,但又可以通过微信客户端调用原生的各种接口。微信的架构,是数据驱动视图的MVVM
模式,其视图UI
和数据是分离的,所有的页面更新,都需要通过对数据的变更来实现。小程序分为两个部分Webview
和AppService
,Webview
主要用来展现渲染界面,AppService
用来处理业务逻辑、数据及接口调用,通过系统层JSBridge
实现通信,实现UI
的渲染与事件的处理。
webpack构建流程
初始化参数:获取用户在webpack.config.js文件配置的参数
开始编译:初始化compiler对象,注册所有的插件plugins,插件开始监听webpack构建过程的生命周期事件,不同环节会有相应的处理,然后开始执行编译。
确定入口:根据webpack.config.js文件的entry入口,开始解析文件构建ast语法树,找抽依赖,递归下去。
编译模块:递归过程中,根据文件类型和loader配置,调用相应的loader对不同的文件做转换处理,在找出该模块依赖的模块,递归本操作,直到项目中依赖的所有模块都经过了本操作的编译处理。
完成编译并输出:递归结束,得到每个文件结果,包含转换后的模块以及他们之前的依赖关系,根据entry以及output等配置生成代码块chunk
打包完成:根据output输出所有的chunk到相应的文件目录
有问题请指出