徜徉在知识海洋的一群鲸鱼
前端面试小tips
前端面试小tips

前端面试小tips

OSI七层模型

应用层,对APP数据进行编码,展示给用户的图片,声音,视频,http协议,

表示层,编码和解码

会话层,两个APP之间的会话,

传输层,建立TCP和UDP的连接,TCP要求整个文件完整,但是会造成延时,UDP实时采集,但是会丢帧。

网络层,基于IP地址进行路由转发,寻址,路由选择,帧变成包,

数据链路层,各段链路的通信协议,数据传输过程中有不同的协议,数据传输过程中,封装成帧,

物理层,实际传输,物理传输

TCP/IP模型

应用层,会话层,传输层,网络层,数据链路层

TCP和UDP

基于连接的是tcp,三次握手,客户先初始确认连接发包,服务接收返回确认信息,客户提出请求,传输确认,四次挥手,非连接的是udp

TCP三次握手

客户端发出连接请求,服务端收到,客户端提出请求,

HTTP请求码

1表示请求,2表示请求成功,200请求成功,201创建成功,可以进行post和pull了,204返回的无内容,3表示重定向,301表示资源更新,url更新

302临时url,304未修改,可以直接用缓存,400语法错误,401身份未识别,403拒接普通请求,404没有资源,409冲突,500服务器出错,502网关错误,503超载维护。

webpack原理

首先入口文件,根据逐层识别模块依赖,commonjs和es6的import进行分析,然后分析代码,转化代码,编译代码,输出代码,最终打包

loader加载

文件加载器,资源文件,进行编译压缩,统一打包到指定文件,处理一个文件可以多个loader,loader执行顺序和配置顺序相反,最后一个loader先执行,执行函数把文件内容作为参数,然后执行之后的返回值作为下一个loader的参数,最后执行的loader返回js源码。

plugin生成

监听webpack生命周期期间广播的事件,合适的时候对webpack提供的api改变输出结果

区别

loader直接编译操作文件,plugin基于事件工作,监听打包过程的节点,并执行相应任务

防抖节流

防抖就是推迟执行触发函数,当一直触发函数时,函数会一直重置推迟事件,跟LOL里的回城一样。

频繁点击事件,监听浏览器滚动事件,监听用户缩放事件

// 第一个参数是需要进行防抖处理的函数,第二个参数是延迟时间,默认为1秒钟
function debounce(fn, delay = 1000) {
// 实现防抖函数的核心是使用setTimeout
    // time变量用于保存setTimeout返回的Id
    let time = null
    function _debounce() {
        // 如果time不为0,也就是说有定时器存在,将该定时器清除
        if (time !== null) {
            clearTimeout(time)
        }
        time = setTimeout(() => {
            fn()
        }, delay)
    }
    // 防抖函数会返回另一个函数,该函数才是真正被调用的函数
    return _debounce
}
这样只是实现了简单防抖,下面介绍传入参数的情况,
由于外部接收的事件,都在fn里面,所以我们需要在防抖函数调用fn的时候apply(this,arguments)这样把参数传输进去
但是我们第一次也会延迟,为了解决这个问题,我们需要
在debounce再传入一个Boolean参数,如果为true,设置一个变量firstClick记录值为!time,然后判断firstClick为真,就直接执行,然后给time设置一个计时函数,把t变回null,如果参数不为true就直接执行计时函数。

节流就是技能CD,触发时会立即执行,但是需要冷却时间

// interval 间隔时间,也就是cd的长短
function throttle(fn, interval) {
    //该变量用于记录上一次函数的执行事件
    let lastTime = 0
    
    const _throttle = function(...args) {
        // 获取当前时间
        const nowTime = new Date().getTime()
        
        // cd剩余时间
        const remainTime = nowTime - lastTime
        // 如果剩余时间大于间隔时间,也就是说可以再次执行函数
        if (remainTime - interval >= 0) {
            fn.apply(this, args)
            // 将上一次函数执行的时间设置为nowTime,这样下次才能重新进入cd
            lastTime = nowTime
        }
    }
    // 返回_throttle函数
    return _throttle
}

ajax怎么使用promise封装

说说promise 说说 async await

vue响应式原理

vuedata初始化为一个Observer并对对象中的每个值,重写了其中的getsetdata中的每个key,都有一个独立的依赖收集器。在get中,向依赖收集器添加了监听在mount时,实例了一个Watcher,将收集器的目标指向了当前Watcherdata值发生变更时,触发set,触发了依赖收集器中的所有监听的更新,来触发Watcher.update

传统项目与框架区别 两者优势

码层面的问题

原生JS                             框架
1.缺少规划,代码混乱    ---->      结构化开发
//原生js的代码写的很随意,没有规划性
2.缺少限制,容易冲突    ---->      独立文件,独立作用域
//原生js开发不是多人并行开发,修改代码容易造成全局变量和其他业务发生变化
//框架借助glub和webpack等工程化工具,实现各页面独立开发,相互不影响,有自己的作用域,只要本作用     域的变量不发生冲突,与其他页面的变量问题就不会产生冲突,最重要的是解决了多人并行开发的问题
3.缺少支撑,能力要求高  ---->      提供支持,只关心业务
//原生JS要负责前端的架构、工具、业务等;而框架的架构和工具都做了集成,只关心业务逻辑即可

效率问题

原生JS							框架
1.关注所有流程     ----------->    只关注业务
//原生js开发者要关注业务外的所有流程,还不能并行开发,浪费很多时间,效率低下
//框架的出现使得开发者只需要关注前端的业务逻辑实现,不需要关注其他,提高了效率
2.团队效率低		 ------------>   并行开发
//js一个人开发改动会影响其他人的代码和功能实现,只能等一个人开发完了其他开发者才能继续开发
//框架开发只要接口和参数不变,只会影响自己的模块,对别人的模块不会产生影响,可以多人并行开发
3.测试效率低      ------------>    模块测试,自动化测试
//js的影响是全局的,一个业务改动会覆盖全局,测试也是要再次进行全局测试
//模块改动只会影响本模块和有业务关系的模块,测试范围缩小;还有的支持工具测试和自动化测试,极大   	 地提高了测试的效率

多页应用的问题

	原生JS							框架
    1.路由体验问题    --------->       使用单页路由
    //原生js会加载所有内容,导致所有html,css,js,请求都要重新加载,耗费时间长
    //框架的html,css,js都是提前加载好的只用请求数据,页面显示很快
    2.无页面切换效果   --------->     可以添加过场动画
    //原生js切换页面,整个页面会销毁,加载会出现白屏,用户体验不高
    //框架是单页,加载并不会整个页面销毁,页面切换实际就是好DOM元素的切换,可以添加过场动画提高视	 觉效果
    3.浪费服务器资源    -------->       减少服务器请求
    //原生js加载会销毁整个页面,重新请求资源加载,加重服务器负担
    //框架只请求数据,大大减少了服务器负担

框架开发的不足(缺点)

兼容性问题,SEO不友好(对低版本浏览器,向ES6这样的不友好)

有场景要求,开发自由度比较低

黑盒开发,框架本身有出错的风险(使用的API接口,内部实现是不可见的)

学习成本(学习设计思路和API的用法)

为什么可以通过 this.xxx 触发响应式流程

项目中什么用的 vuex

状态管理,大型单页项目用到同一个状态,所以需要vuex进行状态管理,vue组件调用actions,actions提交到mutations,mutations再把变化传递给state,然后渲染到组件上

重排重绘

渲染里面生成布局就是重排,布局绘画就是重绘,dom变化影响了元素的几何信息,浏览器需要计算,安置,这就是重排,回流

添加删除dom,改变元素位置,尺寸,内容,字体大小,窗口尺寸,激活伪类,设置style,

不影响布局只是单单影响元素外观进行绘制,就是重绘,

js 闭包 细节题:如果父函数有a,b变量 子函数引用b变量 退出父函数 a变量会被销毁吗

js对象的继承

组合继承,父类.call(this,value) 子类.prototype=new 父类 寄生组合继承,用object.create,第二个参数是构造器。

class继承,用extends,构造器用super

统计字符串中字符出现的次数,返回出现频率最高的次数以及出现这些次数的字符

适配移动端怎么做的 什么样的效果

使用flexible调整fonsize的依据

哪些场景适合用rem、哪些适合px

多端设备,适合rem

position

flex 怎么用的 说说下面三个子元素占据宽度是多少

跨域

bind,call,apply

登陆持久化

用户信息为啥放在localStorage

伪造域名窃取本地token怎么办

使用路由守卫进行权限管理

js基本数据类型,==和===,事件循环理解,深浅拷贝,闭包理解和举例,vue和react声明周期,正则表达式,日期格式化,

number string boolean null undefied symbol 转化判断,直接判断,_.clone(obj) _.clonedeep(obj)

object.assign(obj) {…obj} JSON.parse(JSON.stringify(obj))

tcp队首阻塞,七层网络模型,进程和线程,小程序和web渲染区别,axios拦截,统计请求时长,electron底层线程,js的worker,css新标准,vuerouter模式原理,vue2和3区别,顺时针打印矩阵,解析url参数,

tcp和udp区别,图片懒加载,http状态码,缓存,数组扁平,小于n最大数,sleep函数,域名共享cookie,

前后端认证方式,webpack版本区别,plugin和loader,hooks优点,

flex布局

https://blog.csdn.net/xiaomao_wxj/article/details/107747964

移动端适配

https://juejin.cn/post/6953091677838344199

es6

https://juejin.cn/post/7091066836061519908

grunt,gulp自动化

https://juejin.cn/post/6850418112026984462

webpack

https://juejin.cn/post/6943468761575849992#heading-7

TCP和UDP

https://juejin.cn/post/6844903800336023560

进程和线程

https://juejin.cn/post/7146410097281859591

vue生命周期

https://juejin.cn/post/7032881219524100132

call apply bind

https://juejin.cn/post/6844903891092389901

手写promise

http://jingqun.top/webwork/6-%E6%89%8B%E5%86%99%20Promise.htm

const PENDING = ‘pending’
const RESOLVED = ‘resolved'
const REJECTED =  'rejected'

function MyPromise(fn){
 把this给that
设置state为pending
设置value为null
设置resolvedCallbacks为空数组
设置rejectedCallbacks为空数组
其中value保存reject和resolve传入的值,剩下俩数组用于保存then后的回调,当promise执行完后可能状态还是等待
完善resolve函数
function resolve(value){
判断state是不是pending
如果是设置state为resolved
value为value
resolvedCallbacks数组map(cb = >cb(that.value))}
reject同理
接下来实现如何执行Promise传入的函数
try{
fn(resolve,reject)}
catch(e){reject(e)}
实现复杂的then函数
MyPromise.prototype.then=function(onFulfilled,onRejected){
把this给that
onFulfilled=typeof onFulfilled==='function'?onFulfilled :v=>v
onRejected = typeof onRejected === 'function' onRejected :r=>{throw r}
如果状态是pending
两个数组同时push
如果是rejectd执行onRejected(this.value)
如果是resolved同理