八股
非对称加密
通过两个密钥进行加密(公开密钥和私有密钥),公钥加密私钥解,私钥加密公钥解。对称加密只有一种密钥,并且是非公开的。
http和https的区别
- http是明文传输的协议,https是通过ssl加密传输的协议
- http是默认80端口,https默认443端口
- http的连接是无状态的,https连接的握手阶段比较费时
- https协议需要ca证书,费用较高。
tcp,upd,tcp如何保证可靠性
- tcp面向连接的可靠传输协议,udp是无连接不可靠的传输协议。udp的头部开销比tcp更小,数据传输速率和实时性更好。tcp只支持单播传输,udp支持单播,多播,广播。
- tcp的可靠传输是靠三次握手来保证的
udp传输数据为什么不可靠
ip层的主要任务是按照源ip地址向目标ip地址发送数据报,但不管发送结果,发送结果由传输层处理。udp协议只是在原有的基础上增加了源端口,目标端口,长度,校验和4个字段,因此udp协议
- 不保证消息交付:不确认,不重传,无超时。
- 不保证交付顺序:不设置包序号,不重排,不会发生队首阻塞
- 不跟踪连接状态:不必建立或重启状态机
- 不需要拥塞控制:不内置客户端/网络反馈机制
tcp传输数据为什么可靠
tcp传输数据采用ack确认号机制,通过三次握手四次挥手确保连接的建立,双方各自的接受和发送能力,以及数据全数传输。
localStorage的特点
- localStorage是存储在客户端,一般是5~10MB,localstorage内存储的数据一般不会过期(除非手动清除),数据不会发送给服务器。
- 常用的api有getItem(key),setItem(key,value),clear(),removeitem(key)
localStorage,cookie,sessionStorage的区别
- cookie存储的数据不超过4k,cookie本身被用于浏览器和server通讯,sessionStorage和localStorage可以达到5M+。
- cookie在设置的过期时间之前一直都有效,localStorage永久存储,浏览器关闭后也不删除,除非手动清除。sessionStorage数据在当前浏览器窗口关闭后自动删除。
- cookie的数据会自动传递到服务器,sessionStorage和localStorage的数据保存在本地。
npm run dev/serve是怎么解析执行的
- 运行 npm run xxx的时候,npm 会先在当前目录的 node_modules/.bin 查找要执行的程序,如果找到则运行;
- 没有找到则从全局的 node_modules/.bin 中查找,npm i -g xxx就是安装到到全局目录;
- 如果全局目录还是没找到,那么就从 path 环境变量中查找有没有其他同名的可执行程序。
OSI七层架构
- 物理层,数据链路层(ARP(ip->物理地址)),网络层(ip,icmp:报文协议,差错报文,询问报文),传输层(tcp),会话层,表示层,应用层(文件传输协议:http,dns)
浏览器输入URL到页面渲染
- 先通过DNS将域名解析成ip地址
- 拿到ip地址后,进行tcp连接
- 发送http请求:tcp三次握手
- 服务器处理请求并且返回请求报文
- 浏览器解析渲染页面
1. 解析html,构建dom树
2. 解析css,生成css规则树
3. 合并dom树和css规则树,生成render树
4. 布局render树,计算石村,位置
5. 绘制render树,绘制页面像素信息、、、
- 断开连接:tcp四次挥手
http状态码
- 1xx:指示信息类,表示请求已接受,继续处理。
- 2xx:成功类,表示请求已经成功接受
- 3xx:指示重定向,表示要完成请求需要进行进一步操作
- 4xx:指示客户端错误,请求有语法错误或者请求无法实现
- 5xx:指示服务器错误,服务器未能实现合法的请求
常见状态码、
- 200:客户端请求成功
- 301:永久重定向,请求的页面已经永久重定向到新的URL
- 302:临时重定向,所请求的页面临时重定向到新的URL
- 304:自上次请求后,请求的页面未修改过,服务器返回此响应,不会返回网页的内容
- 403:对请求页面的访问被禁止
- 404:请求资源不存在
- 500:服务器发生不可预期的错误原来缓冲的文档还可以继续使用。
- 503:请求未完成,服务器临时过载或宕机,一段时间后可以恢复正常
浏览器的缓存(强制缓存&协商缓存)
- 强制缓存:向浏览器缓存查找该请求结果,并根据该结果的缓存规则来决定是否使用该缓存结果的过程。 强制缓存暂时有三种情况:
- 不存在该缓存结果和缓存标识,强制缓存失效,直接向服务器发送请求。
- 存在缓存结果和缓存标识,结果已经失效,强制缓存失效,使用协商缓存。
- 存在缓存结果和缓存标识,结果未失效,直接返回缓存结果。
- 协商缓存:强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。控制协商缓存的字段:Last-Modified/If-Modified-Since和Etag/If-None-Match(优先级最高)。
- 协商缓存生效:304
- 协商缓存失效,返回200和请求结果。
- 综述
线程和进程
- 进程可以理解成一个最小的资源管理单元。可以独立运行,独立获得资源,独立接受调度。三种状态(就绪,阻塞,运行)
- 线程是最小的执行单元,处理机调度的单位,是进程中的实际运作单位。
- 进程是操作系统资源分配的基本单位,线程是任务调度和执行的基本单位,一个进程可以包含多个线程,同一个进程中的线程可以共享资源,不同进程不可共享资源,进程切换开销大,线程切换开销小。
进程之间的通信
管道通信
管道又称匿名管道,是一种最基本的IPC机制,由pipe函数创建。只能用于有亲缘关系的进程间通信。 通过pv操作控制对临界资源的分配,进行进程通信。
命名管道(FIFO)
上一种方式的通信是匿名的,可以解决上一种方式的局限性。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储文件系统中。命名管道是一个设备文件,因此即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO互相通信。
信号量
信号量的本质是一种数据操作锁,用来负责数据操作过程中的互斥,同步等功能
共享内存
消息队列
(msg)
了解的一些打包工具
webpack gulp vite
webpack打包
- 它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。
对虚拟dom的理解
- 虚拟dom抽象了原本的渲染模式,比如在某个操作钟,需要更新10个dom节点,但浏览器不知道有10个,它收到第一个更新dom请求后就会立即执行,最终导致执行10次流程,而通过vnode,虚拟dom不会立即操作dom,而是将10次更新的内容保存到本地的一个js对象钟,最终将这个js对象一次性添加到dom树上,避免了大量的重复计算。
MVVM
- 视图模型双向绑定,model-view-viewmodel的缩写,
css
BFC
BFC既块级格式化上下文
position属性的值以及区别
fixed
元素相对于浏览器窗口是固定位置,即使窗口滚动他也不会动。fixed定位使得元素的位置与文档流无关,不占据空间。
relative
对一个元素进行相对定位,他将出现在它所在的位置上,可以通过设置垂直或者水平位置,让元素“相对于”它的起点进行移动,在使用相对定位时,无论是否进行移动,元素仍占据原来的空间。
absolute
元素会被移出正常文档流,并不为元素预留空间,通过指定元素相对于最近的非 static 定位祖先元素的偏移,来确定元素位置。绝对定位的元素可以设置外边距(margins),且不会与其他边距合并。
static
该关键字指定元素使用正常的布局行为,即元素在文档常规流中当前的布局位置。此时 top, right, bottom, left 和 z-index 属性无效。
sticky
元素根据正常文档流进行定位,然后相对它的最近滚动祖先(nearest scrolling ancestor) 和 containing block (最近块级祖先 nearest block-level ancestor),包括table-related元素,基于top, right, bottom, 和 left的值进行偏移。偏移值不会影响任何其他元素的位置。
如何清除浮动
建立伪类选择器清除浮动
.clearfix:after{
content:'';/*子元素的内容为空*/
/*设置添加的子元素为块级元素*/
display:block;
/*子元素高度为0*/
height:0;
/*子元素不可见*/
visibility:hidden;
/* 设置清除浮动*/
clear:both;
}
给父元素加上 overflow:hidden;
添加额外标签
<div class="parent">
<!-- 添加额外标签并且添加clear属性-->
<div style="clear:both"></div>
</div>
哪些属性可继承
- 颜色
- 文字
- 字体间距行高对齐方式
- 列表的样式
盒模型(border-box,content-box)
- border-box:width=content+border+padding
- content-box:width = content
- 通过box-sizing改变属性
常见的布局方式
flex
- dispaly:flex;
- 一维布局
- 两根轴线(水平,垂直):flex-direction(row,row-reverse,column,column-reverse)
- flex-wrap:nowrap(不换行)/wrap(换行)
- 简写属性:flex-flow: flex-direction flex-wrap
grid
- display:grid;
- 二维布局,水平为行。垂直为列,行和列的交叉区域会产生行x列个单元格
- grid-column-start,grid-column-end,
- grid-column:grid-column-start grid-column-end
垂直中心布局
flex
.parent{
height:600px;
display:flex;
justify-content:center;
align-items:center;
}
gird
.parent{
display:grid;
justify-content:center;
align-items:center;
}
transform实现
.parent{
height:600px;
position:relative;
}
.child{
width:100px;
position:absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);
}
absolute margin:auto;
.parent{
height:600px;
position:relative;
}
.child{
width:100px;
height:100px;
position:absolute;
margin:auto;
left:0;
right:0;
top:0;
bottom:0;
}
九宫格布局
grid
<div class="box">
<div>九宫格1</div>
<div>九宫格2</div>
<div>九宫格3</div>
<div>九宫格4</div>
<div>九宫格5</div>
<div>九宫格6</div>
<div>九宫格7</div>
<div>九宫格8</div>
<div>九宫格9</div>
</div>
.box {
display: grid;
height: 600px;
width: 100%;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
}
.box > div {
width: 98%;
margin: 1%;
background-color: deeppink;
text-align: center;
line-height: 200px;
}
flex
.box {
display: flex;
flex-wrap:wrap;
height: 600px;
width:600px;
}
.box > div {
width:33%;
height:33%;
margin:1px;
background-color: deeppink;
text-align: center;
line-height: 200px;
}
两栏布局
<div class="outer">
<div class="left">left</div>
<div class="right">right</div>
</div>
浮动
.outer{
height:100px;
}
.left{
width:200px;
float:left;
height:100%;
background-color:pink;
}
.right{
margin-left:200px;//或者overflow:auto;(形成bfc)
height:100%;
background-color:blue;
}
flex
.outer{
height:100px;
display:flex;
}
.left{
width:200px;
height:100%;
background-color:pink;
}
.right{
flex:1;
height:100%;
background-color:blue;
}
z-index
- 注意在同一层级中z-index才生效
js基础
0.1+0.2 != 0.3?
- 原因:
进制转换:js采用IEEE754二进制进行浮点运算,最大可以存储53位有效数字,大于53位后面的会全部截掉,导致精度丢失。 对阶运算:由于指数位数不同,运算时需要对阶运算,阶小的位数要根据阶差来右移(0舍1入),尾数位移可能会发生数丢失的情况,影响精度。
使用Number.EPSILON
function isEqual(){
return Math.abs(a-b)<Number.EPSILON;
}
console.log(isEqual(0.1+0.2,0.3))//true
js数据类型
- Number
- String
- Boolean
- Undefined
- null
- Object
- Bigint:一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。
- Symbol:代表创建后独一无二且不可变的数据类型,它主要是为了解决可能出现的全局变量冲突的问题
判断元素类型
instanceof
可以区分Array,object,function,不能区分Number,Boolean,String。适合用于判断自定义的类实例对象。
typeof
可以快速区分基本数据类型。不能将objec,array,null区分开,都返回object
es6(新特性,组合式api)
字面量增强
var obj = {
/*以前*/
foo:function(){
console.log(this);
},
/*现在*/
bar(){
console.log(this);
},
/*箭头函数*/
baz:()=>{
console.log(this);
}
}
新增解构
- 解构赋值,如果没有找到,会返回undefined
/*数组解构*/
var names = ['aa','bb','cc'];
/*若没有,默认name4为dd*/
/*数组需要按顺序解构,如果不要前面的某个,空也得留出来*/
var {,,name3} = names;
console.log(name3)//cc
var {name1,name2,name3,name4='dd'} = names;
console.log(name1,name2,name3)//aa,bb,cc
/*对象解构*/
var obj = {
name:'why',
age:'18'
}
var {name,age} = obj;
var {age} = obj
var {name:newName} = obj//重命名
console.log(newName)//why
let,const,var
- let,const具有块级作用域
- var声明变量时,可以重复声明变量,后声明的同名变量会覆盖之前声明的遍历。const和let不允许重复声明变量。
- 在使用let、const命令声明变量之前,该变量都是不可用的。这在语法上,称为暂时性死区。使用var声明的变量不存在暂时性死区
- let,var声明的变量可以不设置初始值,但const必须声明初始值。
块级作用域
- ES5:全局作用域+函数作用域
- ES6:块级作用域
for...of
ES6新增可迭代对象
let arr = [1,2,3];
/*for...of可遍历set,map*/
for(let item of arr){
console.log(item)//1,2,3
}
//for in 遍历键值
for(let index in arr){
console.log(index)//0,1,2
}
新增Set,Map数据结构
- set:无重复元素
- map:存储映射关系(obj中只能用string/symbol作为key,map还可以用obj,Array作为key)
array.includes方法
可判断array中是否存在某元素,includes可以判断NAN的存在,indexof不行
Object.keys(obj),Object.values(obj),Object.entries(obj)
const obj = {
name:'why',
age:18
}
console.log(Object.keys(obj))//['name','age']
console.log(Object.values(obj))//['why',18]
console.log(Object.entries(obj))//[['name','why'],['age',18]]
flat:数组降维
箭头函数与普通函数的区别
声明方式不同
箭头函数通过(=>)声明,普通函数通过function声明。
this指向不同
箭头函数的this指向其上层作用域的this,指向是固定的,而普通函数的this指向是可变的
prototype
箭头函数没有原型prototype
构造函数
箭头函数没有自己的this,因此不能返回绑定好this的对象,因此不能做构造函数。
this
函数调用时,js会默认给this绑定一个值,this的绑定与定义位置无关。this的绑定与调用方式以及调用位置有关。this是在运行时被绑定的。
this的绑定规则
- 默认绑定:独立的函数调用,this指向全局对象,与定义位置无关,与调用方式以及调用位置有关。
- 隐式绑定:通过某个对象进行调用,执行的时候是哪个obj调用的,this就指向哪个obj。且在该对象内有对该函数的引用才行,若无引用,则Function not found;
- 显示绑定:通过apply,call,bind显示绑定this对象。
bind,call,apply
call,bind,apply都是显示绑定this,改变this指向的函数
call
var obj = {
name:'why'
};
function fn(){
console.log(this.name);
}
fn.call(obj)//'why'
自己实现call函数
Function.prototype.myCall = function(context){
//首先判断调用对象是否为函数
if(typeof this !== 'function'){
throw new Errow('type error');
}
let args = [...arguments].slice(1);
let result = null;
context = context || window;
//将被调用的方法设置为context的属性
//this为要调用的方法
context.fn = this;
//执行要被调用的方法
result = context.fn(...args)
//删除属性
delete context.fn;
return result;
}
apply
与call的用法相同,参数需要用数组的形式传递
Function.prototype.myApply = function(context){
//首先判断调用对象是否为函数
if(typeof this !== 'function'){
throw new Errow('type error');
}
let result = null;
context = context || window;
//将被调用的方法设置为context的属性
//this为要调用的方法
const fnsymbol = Symbol();
//执行要被调用的方法
context[fnsymbol] = this;
if(arguments[1]){
result = context[fnsymbol](...arguments[1]);
}else{
result = context[fnsymbol]();
}
//删除属性
delete context.fn;
return result;
}
bind
bind会返回一个新的函数,新函数调用指向bind绑定的this
js高级
函数提升(写输出)
- 当查找变量的时候,按照作用域链为路径查找,作用域链就是当前作用域+父级作用域。
var a = 100
function foo(){
/* AO对象中有a,但是a还没有赋值,暂时为undefined,因此在执行到a赋值的语句之前,输出a的值为undefined*/
console.log(a);
return
var a = 100;
}
foo();//undefined
promise
1.promise.all函数的作用 2.promise.then的值
防抖和节流
防抖
- 防抖就是在一段时间只执行一次。
- 举例说明,相当于回城,如果有事件来打断回城,那么回城会重新计时。
- 事件可以理解成触发事件,setTimeout用于计时,clearTimeout用于清空计时,重新计时,是防抖函数中的关键因素。
- 使用场景:调整页面的大小,验证表单某个字段是否重复时发生请求次数控制,防止表单多次提交
- 手写防抖:假设我们要点击一个按钮新增一条信息,但不希望每次点击都调用接口新增,我们希望多次点击只新增一次。
/*按钮被点击*/
let addBn = document.getElementById('add')
function addOne(){
console.log('add one')
}
//addBn.addEventListener('click',addOne);//绑定事件(未防抖)
/*设置防抖函数*/
function debounce(fun,time){
let timer
return function(){
clearTimeout(timer);//打断setTimeout的计时,重新计时
let args = arguments
timer = setTimeout(()=>{
fun.apply(this,args)//保证this指向跟未防抖的时候一样
},time)
}
}
addBtn.addEventListener('click',debounce(addOne,2000))//绑定事件(防抖)
- 总结:防抖就是在不断的操作中(输入/点击)最终只执行一次的一种提高性能的方法。
节流
- 节流就是在间隔事件内只执行一次
- 举个例子,就像法师使用技能,下一次使用必须等技能cd结束才行。
- 使用技能理解成触发事件,而技能的固定cd和当前时间则是节流的关键。
- 使用场景:滚动鼠标滚轮监听滚动条的位置,防止按钮多次点击等。
- 手写节流:假设我们要实现一个鼠标滚动打印事件,想让它在
3s执行一次
/*模拟一个触发事件*/
function scrollTest(){
console.log('现在我触发了')
}
document.addEventListener('scroll',scrollTest)
/*封装节流函数*/
function throttle(fun,time){
let t1 = 0//初始时间
return function(){
let t2 = new Date()//获取当前时间
if(t2-t1>time){
fun(this,arguments)//改变this指向
t1 = t2
}
}
}
document.addEventListener('scroll',throttle(scrollTest,3000))
- 总结:
节流就是在一段时间内不断操作而在你规定时间内只执行一次的提高性能的方法。
手写深拷贝
- 浅拷贝:拷贝对象指针,修改内容互相影响
- 深拷贝:整个对象拷贝到另一个内存中,修改互不影响。
let a;
/*方法一*/
let b = JSON.parse(JSON.stringfy(a))
/*方法二*/
const deeplone = function(a,cache){
if(!cache){
cache = new Map();
}
//判断是否为对象
if(a instanceof Object){
if(cache.get(a)){
return cache.get(a)
}
let result;//存储结果
if(a instanceof Function){
if(a.prototype){
//有prototype的就是普通函数
result = function(){
return a.apply(this,arguments);
}
}else{
//没有prototype就是箭头函数
result = function(){
return a.call(undefined,...arguments);
}
}
}else if(a instanceof Array){
//数组,下面遍历键值的时候赋值
result = [];
}else if(a instanceof Date){
result = new Date(a-0);
}else if(a instanceof RegExp){
//正则表达式
result = new RegExp(a.source,a.flags)
}else{
//对象,递归赋值
result = {}
}
cache.set(a,result);
for(let key in a){
if(a.hasOwnProperty){
//保证key不是原型属性
result[key] = deeplone(a[key],cache);
}
}
return result;
}else{
//普通类型可以直接返回
return a;
}
}
const a = {
number:1,
bool:false,
str:'hi',
empty1:undefined,
empty2:null,
array:[
{name:'coder',age:18},
{name:'why',age:19}
],
date: new Date(2000,0,1,20,30,0),
regexp:/\.(j|t)sx/i,
obj:{name:'coder',age:18},
f1:(a,b)=>{return a+b;},
f2(a,b){
return a-b
},
}
a.self = a;
const b = deeplone(a);
console.log(b.self === b)//true
b.self = 'hi'
console.log(a.self==='hi')//false
console.log(b.f2(6,5));//1
写一个判断数据类型的函数
面对对象
new
var obj = new Object();
obj.name = 'why';
obj.age = 18;
自变量
const obj = {
name:'why',
age:18
}
构造函数是干什么的
简单来说就是创建某类特定对象的。本质是通过new调用,返回新对象
new的原理
通过new关键字调用一个构造器函数时,this指向该构造器创建出来的新对象
- 创建一个空对象
- 根据原型链,设置空对象的_proto_属性为构造函数的prototype属性。
- 构造函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)
- 如果函数没有返回其他对象,则返回该新对象。
class以及继承
class
类,类似于java的类,将属性和方法封装到一个类中,具有面向对象的三大特性(封装,继承,多态)
继承
可以通过extends关键字进行继承的操作。
异步都有哪些
宏任务 微任务
- 宏任务:setTimeout,setInterval,Ajax,Dom事件
- 微任务:Promise.then, Node环境下的process.nextTick,async/await
二者区别 - 宏任务:Dom渲染后触发
- 微任务:Dom渲染前触发
事件循环event loop(写输出)
- 不同类型的任务会进入对应的任务队列。
- 先执行微任务,再执行宏任务
- js事件循环是实现异步的一种方法
ajax
介绍ajax,ajax返回值类型有哪些
ajax返回数据时如何指定数据类型
算法
栈和队列以及其应用场景√
栈
先进后出,括号匹配
队列
先进先出,事件队列
嵌套数组扁平化
二分查找√
排序(冒泡,快排)
两个栈实现一个队列√
找不重复数组的第二大元素
求前置和 和 后置和相同的个数(双指针)
找出给定字符中的最长不重复子串√
给定一个字符串,返回第一个只出现一次的字符的下标
大数加法√
实现一个函数,按规则转换字符串kkk
input:'1,2,3,5,7,8,10,11,14,15,16'
output:'13,5,78,1011,1416'
vue
vue的组件库(vue-router,vuex)
vue3新特性
vue的生命周期函数
- 哪个生命周期开始可以修改Dom
vue实现数据代理的底层原理
v-if和v-show的区别,以及使用场景
- 控制手段不同,v-show隐藏的原理是为该元素添加display:none属性,dom元素依然还在。v-if显示隐藏是将整个dom元素添加或者删除。
- v-if是条件渲染,会确保在切换过程中条件块内的事件监听器和子组件适当的被销毁和重建。v-show由false变为true的时候不会触发组件的生命周期。而v-if由false变为true,触发组件的beforeCreate,create,beforeMount,mounted钩子,由true变为false的时候触发组件的beforeDestory,destoryed方法。
使用场景
- v-if的切换开销大于v-show,如果需要频繁切换,用v-show,如果运行时条件很少改变,使用v-if。
vue当中key的作用
key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确更快的找到对应的vnode节点。
双向绑定的原理
双向绑定就是,model绑定到view,view更新model会更新,model更新也会引起view更新