前端面试记录

418 阅读16分钟

一个工作一年前端菜鸟的面试总结,最近在找工作,会持续更新面试过程中所遇的一些面试题,所有题目不分先后,凭面试记录更新

1.vue数据双向绑定的原理

vue是采用数据劫持结合发布订阅模式的方式,数据劫持是通过Object.defineProperty()来劫持各个属性的settergetter,在数据变动时发布消息给订阅者,触发相应的监听回调

执行以下三个步骤,实现数据的双向绑定:

1.  实现一个监听器Observer,用来劫持并监听所有的属性,如果有变动,就通知订阅者
2.  实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图
3.  实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器
2.发布订阅模式和观察者模式的区别

观察者模式:观察者模式定义了一种对象之间一对多的依赖关系,当一个对象的状态发生改变,所有依赖他的对象都会收到通知,并自动更新。

在观察者模式中存在两个主题:目标对象(object)和观察者(observer)

发布订阅模式:首先发布订阅模式的发布和订阅都是有统一的一个调度中心来处理。发布者发布消息事件到调度中心,订阅者将自己需要关注的消息事件注册到调度中心,调度中心来处理事件的注册与发布

在发布订阅模式中存在三个角色:任务发布者(publisher),事件调度中心(EventChannel),订阅者(Subscriber),案例:事件总线EventBus
3.this指向问题,对作用域链的理解

简单来讲,作用域就是变量和函数的可访问范围

作用域链则指的是:一般情况下,变量到创建该变量的函数的作用域中取值,如果当前作用域中没有查到,就会向上级作用域中取值,直到查到全局作用域中,这样一层一层向上查找的过程中所形成的链条被称作为作用域链

this指向问题:

this官方一点的解释就是说:this被自动定义在所有函数的作用域中,它提供了一种更好的方式来隐式的传递对象引用,这样使得我们API的设计或者函数变得更加简洁,而且还容易复用

所以我们可以认为this对象是执行上下文中的一个属性,它指向哪里是在函数调用的时候确定的,一般在全局函数中,this指向是window,而当函数被作为某个对象的属性调用时,this指向的就是那个对象 实际开发当中this的指向可通过四种调用模式来判断:

1.函数调用:当一个函数不是对象的属性时,直接作为函数来调用的时候,this指向全局对象window
2.方法调用:当一个函数作为一个对象的方法来调用时,this指向该对象
3.构造函数调用:this指向这个new关键字创建的实例对象
4.apply,call,bind方法改变this指向,第一个参数直接时this的指向

箭头函数中的this,首先要明确箭头函数是没有自己的this,箭头函数会根据其声明的地方来决定this,默认指向定义它时,所处的上下文的对象的this指向,没有上下文对象时,this就指向window

4.类和继承

类存在的目的就是为了生成对象, 类中的constructor函数负担起了之前的构造函数的功能,类中的实例属性都可以在这里初始化

继承可以使得子类具有父类别的各种属性和方法,而不需要再次编写相同的代码,在子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能

5.es6新特性
1. 变量声明let,const
2. 展开运算符...
3. set关键字
4. object.assign()
5. ``模板字符串
6. 箭头函数
7. promise
8. 解构赋值:数组根据下标解构,对象根据属性名解构
9. 类和继承
6.css盒子模型

css盒子模型分为两种,可使用box-sizing去设置,border-box指的是IE盒模型,content-box指的是标准盒模型

1.标准盒模型的盒子设置了height,width之后,他的实际的宽高就只包括了content内容的宽高,并不包括border,padding,margin

2.IE盒模型的盒子设置了height,width之后,他的实际宽高包括content本身的宽高还有border+padding
7.promise原理,async await的理解

promise是es6新增的一种进行异步编程的新的解决方案,从语法上来说,它就是一个构造函数,从功能上讲:Promise的实例对象可以用来封装一个异步操作,并且可以获取其结果(成功/失败)的值。

new Promise的时候必须传递一个回调函数(同步的回调),会在主线呈上立即执行,官方称之为Promise的excutor函数,excutor函数会接收到两个参数,都是函数,分别resolve和reject接收,resolve的调用会让promise的实例状态改变为成功,并且可以指定成功的value,reject的调用会promise的实例状态改变为失败,可以指定失败的原因reason

promise有三种状态:pending(等待态),fufilled(成功态),rejected(失败态)

promise是用来解决两个问题的:

1.支持链式调用,解决回调地狱
2.可支持多个并发请求,获取并发请求中的数据

promise相关的api

Promise.resolve()用于快速返回一个状态为fufilled或reject的promise实例对象 Promise.reject()用于快速返回一个状态必须为rejected的promise实例对象 Promise.all(promiseArr)返回一个新的promise实例,只有当所有的promise都成功才成功,又一个失败了就失败。 Promise.race(promiseArr)返回一个新的Promise实例,以最先出结果的promise状态为准 (promiseArr是包含n个promise实例的数组)

async await其实就是一个语法糖,用来解决异步问题

async声明function是一个异步函数,返回一个promise对象,可以使用then方法添加回调函数,async函数内部return语句返回的值,会成为then方法回调函数的参数

await操作符只能在异步函数async function内部使用 ,如果一个promise被传递给一个await操作符,await将等待promise正常处理完成并返回其处理结果,它会阻塞后面的代码,等待promise对象结果,如果等待的不是promise对象,则返回该值本身

8.axios的理解

axios是用来和后台交互获取数据的,基于XMLHttpRequest + promise的异步ajax请求库,支持请求/响应拦截器,支持请求取消,可批量发送多个请求,设置超时时间等

9.vuex的理解

vuex是vue.js应用程序开发的状态管理插件,它采用集中式存储管理应用所有组件的状态

vuex包含了5个核心属性:

1.  state:定义应用状态数据结构,可设置默认初始状态
2.  mutation:唯一可以更改state状态的方法(同步的)
3.  getters:类似于vue的computed对象,根据业务逻辑来处理state使得生成业务所需属性,提供一个函数mapGetters,可以让组件将数据从store获取到组件的计算属性中
4.  actions:也可以更改store的状态,但也是通过提交mutation来更改,并不是直接更改,可以包含任意异步操作
5.  modules:将状态管理进行模块化,其目的也是更好的维护

actions中dispatch和commit的作用与区别

相同点:二者最终都是用来提交mutation更改state的值的

不同点:dispatch用于异步操作修改state,commit用于同步操作俩修改state

10.css中display属性值都有哪些,position属性值都有哪些

display属性值:

1.none:隐藏当前元素,会引起页面的重绘,隐藏后的元素不占位
2.block:块级元素
3.inline-block:行内块元素,
4.inline:行内元素,设置宽高无效
5.flex开启弹性
6.table-cell

position属性值:

 1.absolute绝对定位
 2.relative相对定位
 3.static静态定位(默认),标准流
 4.fixed固定定位
 5.sticky粘性定位
11. 常见的数组方法,哪些方法会改变原数组,哪些不会

不会改变原数组的方法

 concat()方法连接两个数组,返回新的结果
 slice()截取数组
 join()将数组的所有元素放入一个字符串
 toString()将数组转为字符串
 map()循环数组进行一些操作,返回操作后的新数组
 find()查找当前数组符合某条件,找到第一个符合条件即返回该元素不再继续循环
 reduce()设置一个累加器
 
 

会改变原数组的方法

pop()操作数组尾部,删掉最后一个元素
push()操作数组尾部,从数组尾部推入新的元素
shift()操作数组头部,从数组头部推入新的元素
unshift()操作数组头部,从数组头部删除元素
reverse()反转数组
sort()对数组排序
splice()插入,替换,删除

拓展:关于forEach方法

forEach方法接受一个回调函数作为参数,回调函数传递三个参数(item,index,arr)

 forEach方法循环过程中无法跳出循环
 当数组的元素是基本数据类型是forEach不会改变原数组,
 当数组的元素是引用数据类型时,如果直接修改整个元素对象,无法修改原数组,修改元素对象里的某个属性时,原数组会发生改变

关于forEach详细讲解参考

12. cookie,localstorage,sessionstorage三者的区别
特性cookielocalstoragesesssionstorage
数据的声明周期一般由服务端生成可设置失效时间,如果是浏览器生成,默认关闭浏览器失效除非被清除,否则永久保存仅在当前会话有效,关闭页面或浏览器后被清除
存放大小4kb5MB5MB
与服务端通信携带在HTTP请求头,使用cookie保存数据过多会产生性能问题仅在客户端保存,不参与和服务端的通信同localstorage
用途一般由服务端生成用于标识用户的身份用于浏览器缓存数据同localstorage

相关的API:

  1. xxxStorage.setItem('key','value'); 该方法接收一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值

  2. xxxStorage.getItem('key'); 该方法接收一个键名作为参数,返回键名对应的值

  3. xxxStorage.removeItem('key'); 该方法接收一个键名作为参数,并把该键名从存储中删除

  4. 1.xxxStorage.clear(); 该方法会清空存储中的所有数据

注意:如果getItem(xxx)获取不到xxx对应的value值,则返回null,JSON.parse(null)结果依然为null

13. 常见的http状态码

2开头表示成功

200请求成功
204 (no content)请求处理成功,但没有内容被返回

3开头一般指重定向

301永久重定向
302临时重定向
304协商缓存

4开头是客户端的问题

400(bad request)请求报文中存在语法错误,无法理解
401未授权,用户认证失败,需要身份验证, (token过期)
403(forbidden)没有权限访问该资源
404(not found)找不到请求的资源,网址错误
405请求方式错误

5开头的是服务端问题

500服务器遇到错误,无法完成请求
502(bad gateway)网关错误
504网关超时
14.vue中的this和jquery中的this分别都指向什么

vue中的this

methods中如果用的是正常函数,那么它的this就指向Vue实例;如果是箭头函数,this就指向window对象

jquery中的this

jquery中this代表“当前对象”的意思,this表示的是html中的当前元素,利用"(this)语句"html元素变成jquery对象,进而使用jquery方法来处理当前对象语法为"(this)语句"把html元素变成jquery对象,进而使用jquery方法来处理当前对象 语法为"(this).jquery方法名()";

15.vue中computed和watch的区别

computed是计算属性,只有当它所依赖的值发生改变之后才会重新计算,有缓存,是同步执行的,属性值是一个函数

watch是侦听器,当被监听的值发生改变之后触发相应的操作,不接受缓存,支持异步监听,监听的函数接受两个参数(newVal,oldVal,新的值和改变前的值),监听的数据必须是在data中声明的或父组件传递过来的props数据,监听的对象还有两个属性immediate:组件渲染时就执行操作,deep:对复杂类型的数据开启深度监听,如数组对象

16.vue中nextTick的理解

语法:this.$nextTick(回调函数)

由于vue是异步更新的,当监听到数据的变化之后不会立即去更新dom,而是开启一个任务队列,缓存同一事件循环中所发生的所有数据变更

也就是说nextTick接收一个回调函数做为参数,并将这个回调函数延迟到dom更新之后才执行。(想要操作基于最新dom生成后的数据时,可以将当前操作放进nextTick里)

17.常见的css选择器的优先级

important>行内样式>id选择器>类选择器>标签选择器>*通配符选择器

18.vue的生命周期,vue中父子组件的生命周期顺序

vue的生命周期是指vue实例从创建->初始化数据->编译模板->挂载dom->渲染->更新->渲染-卸载的过程,生命周期函数的名字不可更改,生命周期函数中的this指向是组件实例对象

beforeCreate 此时无法访问到data中的数据,methods中的方法

created 可以访问到data中的数据,methods中配置的方法

beforeMount此时呈现的是未经Vue编译的dom,任何对dom的操作都不生效

mounted 此时页面中呈现的都是真实的dom,至此初始化过程结束;该阶段里对dom的操作均有效,一般在此进行:开启定时器,发送网络请求,订阅消息,绑定自定义事件等的初始化操作

beforeUpdate更新阶段,当data中数据发生改变时触发,此时数据是新的,但页面是旧的,页面尚未和数据保持同步

updated 此时数据更新完成,页面也更新完成,数据和页面保持同步

beforeDestory发生在组件实例销毁之前,此时组件实例中的data,methods,指令等都处于可用状态,马上要执行销毁过程,一般在此阶段关闭定时器,取消订阅消息,解绑自定义事件等的收尾操作

destoryed 发生在组件实例销毁之后此时的dom只剩空壳,组件的数据绑定和方法等统统被销毁

最容易被问到的问题是created钩子和mounted钩子的区别:
在created触发的时候,视图中的html并没有渲染出来,此时无法与dom进行交互,找不到相关元素
而在mounted钩子触发时,此时html已经渲染出来,可以直接操作dom节点

渲染顺序:父组件beforeCreate>父组件created>父组件beforeMount>子组件BeforeCreate>子组件created>子组件breforeMount>子组件mounted>父组件mounted

更新阶段:父组件beforeUpdate>子组件beforeUpdate>子组件updated>父组件updated

销毁顺序:父组件brforeDestory>子组件beforeDestory>子组件destoryed>父组件destoryed

19.判断一个对象是否为空的方法
Object.keys(obj).length === 0
Object.values(obj).length === 0
JSON.stringify(obj) === '{}'
20.正向代理与反向代理的区别

正向代理指从客户端代理,反向代理指的是从服务端代理

21.css实现左边固定,右边自适应的布局方法

弹性布局,定位,calc

22.一般在vue项目中会封装哪些工具函数

时间处理函数 枚举值处理函数 数组去重

23.js的事件循环机制

由于js是单线程的,为防止一些函数的执行时间过长阻塞后面的代码,js会将同步的代码压入执行栈中,依次执行,将异步的代码推入异步队列,异步队列又分为宏任务队列微任务队列,因为宏任务队列执行时间过长,微任务队列会优先于宏任务队列执行,微任务队列的代表就是promise所指定的回调,MutationObserver,宏任务队列的代表就是setTimeout,setInterval,setImmediate

24.浅拷贝的一些方法,深拷贝的理解

浅拷贝的一些方法:object.assign(),...展开运算符concat()方法, 注意:...展开运算符当数组里面只有基本类型的数据时,当前为深复制

深拷贝: 从堆内存中重新开辟一块新的区域存放新对象,对原始对象的所有属性进行递归拷贝,对所有的引用类型属性同样开辟新区域,修改新对象不会影响原对象 深拷贝方法:json.parse(json.stringify())

const obj = {
    num: 3,
    str: "啦啦啦",
    boy: {
      height: 21,
      weight: 105,
    },
    arr: [1, 2, 3],
    fn: function () {
      console.log(2333);
    },
    reg: /"/g,
    date: new Date(),
  };
  const deep = JSON.parse(JSON.stringify(obj));
  deep.boy.height = 30;
  console.log(obj);
  console.log(deep); //fn 没了 reg 为对象 date为字符串了

以上例子也可以看出来使用该方法之后DateRegExp类型的变成了{},而函数的话是直接没有了 封装一个深拷贝函数

    function deepClone(newObj, obj) {
        for (const key in obj) {
            //判断我们的属性值是属于哪种数据类型
            // 1.获取属性值 obj[key]
            let item = obj[key]
            // 2.判断这个值是否是数组,递归拷贝
            if (item instanceof Array) {
                newObj[key] = []
                deepClone(newObj[key],item)
                // 3.判断这个值是否是对象,递归拷贝
            } else if (item instanceof Object) {
                newObj[key] = {}
                deepClone(newObj[key],item)
            } else {
                // 4.属于简单数据类型,直接赋值
                newObj[key] = item
            }
        }
        return newObj
    }
25.处理跨域

由于浏览器的同源策略限制产生跨域,同源指的是端口号协议域名要相同, 跨域产生之后将会限制:cookie,LocalStorage,IndexDB无法读取,DOM和js对象无法获得,AJAX请求无法发送 有三个标签语序跨域加载资源:<img src=XXX>,<link href=XXX>,<script src=XXX>

1.jsonp方法:由于script标签引入的js是不受同源策略的限制,可以动态生成script获取数据,仅支持get请求,有局限性

2.跨域资源共享cors方法,让后端服务器设置access-control-allow-origin 的http响应头之后,浏览器会允许跨域请求

3.proxy通过服务器设置代理

4.node中间件代理原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发

5.nginx反向代理通过nginx配置一个代理服务器做跳板机,反向代理访问客户端的接口

26.http和https的区别
安全性:https安全性高于http请求,由于http请求是明文传输的,https请求通过ssl加密数据包
耗时性:https耗时高于http,https请求握手阶段比较耗时
端口:http默认端口是80,https默认端口是443
造价:https请求携带CA证书,
27.虚拟dom怎么理解

首先vue会将模板编译成render函数,运行render函数生成虚拟dom,虚拟dom通过diff算法对比差异渲染不同的部分,然后更新视图

虚拟DOM是通过状态生成一个虚拟节点树(vnode),然后使用虚拟节点树进行渲染。 在渲染之前,会使用新生成的虚拟节点树和上一次生成的虚拟节点树进行对比  (diff算法)  ,只渲染不同的部分,所以虚拟dom指的是dom节点在js中的一种抽象数据结构,虚拟dom的作用是每次响应式数据发生改变时引起页面的重新渲染,vue会对比更新前后的虚拟dom,匹配找出尽可能少的需要更新的dom从而达到提升性能的目的

28.css中的BFC是啥,怎么创建一个BFC

BFC(Block Formatting Context)被称为块级格式化上下文,是一个独立的渲染区域,规定了内部的box如何布局,并且这个区域的子元素不会影响到外面的元素

触发方式:

  • 根元素(html)
  • 浮动元素:float:left/right
  • overflow值不为visible,即为auto,scroll,hidden
  • 绝对定位元素:postion:absolute/fixed
29.css中让一个元素垂直水平居中的方法

开启弹性布局display:flex,设置jusitify-content:center,align-item:center

通过定位设置:

1.绝对定位+margin负值:top:50%,left:50%,margin-top:负值盒子本身高度的一半,margin-left:负值盒子本身宽度的一半,

2.绝对定位+transform:top:50%,left:50%,transform:translate(-50%,-50%),

3.利用calc计算偏移量

4.绝对定位+margin:auto:top:0,bottom:0,left:0,right:0加上margin:auto

29.闭包

闭包:指能够读取其他函数内部变量的函数

定义了一个在函数内部的函数,本质上是将函数内部和函数外部连接起来的桥梁

优点:可以长久地保存变量,不会被垃圾回收,维持变量不会被全局污染,提供对局部变量的间接访问
缺点:一旦形成闭包,只有在页面完全关闭之后,闭包所占用的内存才会被回收,会造成内存泄漏
应用场景:
1. 封装私有变量和函数:闭包封装的私有变量将其保存在闭包内部不能被外部直接访问和修改
2. 维护变量的生命周期:闭包可以让变量的生命周期长于函数的生命周期
3. 模拟块级作用域
30.vue中的data为什么是一个函数

new Vue 的时候data可以不使用函数返回的原因在于,每次new的时候,传入的都是新的对象(新的内存地址)。所以修改其中一个 vue 实例并不会影响其他实例

但是在组件中data必须是函数的原因是因为怕重复创建实例造成多实例共享一个数据对象

由于vue是组件化的,一个组件可能会被多次复用,就会创建多个实例,本质上这些实例都是用一个构造函数的,如果data是对象的话,对象属于引用类型,则所有的实例将共享引用同一个数据对象!从而造成数据污染,所以是为了保证组件不同的实例之间data不冲突,data必须是一个函数。

31.输入url之后到页面渲染出来所要经历的过程
浏览器拿到url之后会先去查找当前url是否存在缓存,如果存在并比较缓存是否过期
不存在就会进行DNS解析,解析当前url的IP
根据IP建立TCP连接
发起http请求(三次握手),请求服务器提供指定的网页资源
服务端根据http参数返回相应的响应的数据(将网页的html文件作为响应返回给浏览器)
浏览器根据响应数据渲染页面
关闭TCP连接(四次挥手)

渲染页面的过程:

  • 浏览器解析HTML文件,构建DOM树(表示网页内容的树状结构,它由多个节点构成,每个节点代表HTML中的一个元素,文本内容或注释)
  • 加载CSS样式(异步微任务):浏览器解析HTML文件时,遇到样式表会同时解析其中的CSS样式,并将其应用到享用的元素上,从而实现网页的样式和布局
  • 构建渲染树:浏览器根据DOM树和CSS样式信息整合构建渲染树(render Tree),渲染树是表示网页视觉呈现的树状结构,它包含了所有需要显示的元素,以及它们的样式和布局信息
  • 进行布局和绘制:浏览器根据渲染树进行布局和绘制,布局是指确定每个元素在屏幕上的位置和大小,而绘制则是将元素绘制到屏幕上
  • 显示网页:经过布局与绘制,浏览器将网页显示在屏幕上
32.SPA单页面应用

SPA(Single page application)单页面,仅在web页面初始化时加载相应的HTML,CSS,JS,一旦页面加载完成,SPA不会因为用户的操作进行页面的重新加载和跳转,取而代之的是利用路由机制实现HTML内容的变换

优点:用户体验好,反应快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重新渲染

缺点:由于一开始需要加载所有的HTML,CSS,JS,初次加载耗时多,SEO难度较大

优化首屏加载慢的方法

  1. 使用SSR(服务端渲染),即组件和页面通过服务器生成html字符串,再发送到浏览器。vue可以使用Nuxt.js实现服务端渲染。
  2. 减少入口文件体积:常用的手段是路由懒加载,把不同的路由对应的组件分割成不同的代码块,待路由被请求的时候单独打包路由,使入口文件变小。
  3. UI框架在引用时按需加载,开发过程中可能不止用到一个组件库,全局引入不推荐
  4. 避免组件重复打包:在webpack的config文件中,修改CommonsChunkPlugin的配置,设置minChunks为2表示会把使用3次及以上的包抽离出来,放进公共依赖文件,避免了重复加载组件
33.vue项目的优化
1.区分v-if和v-show的使用场景
2.给v-for加key
3.使用防抖和节流
4.路由的懒加载
5.使用keep-alive开启组件缓存
6.避免v-for和v-if一起使用(v-for优先级高于v-if)
7.首屏优化(骨架屏)
8.事件销毁

webpack打包层面的优化

 1.image-webpack-loader图片资源压缩
 2.使用插件减少es6转为es5的冗余代码
 3.提取公共代码,使用CommonsChunkPlugin
 
34.重绘和重排

重绘:当一个元素的外观发生改变,但没有改变布局,浏览器需要将其重新绘制出来的过程

表现为元素的外观发生改变

重排:当DOM的变化影响了元素的几何信息,浏览器需要重新计算元素的几何属性,将其重新安放在界面中的正确位置的过程

表现为重新生成布局,重新排列元素

如何避免过多的重排和重绘影响性能

1.集中修改样式
2.避免将dom节点的属性值放进循环里面作为变量

重排可能引起重绘,重绘不能引起重排

35.事件委托和事件冒泡

事件委托:当大量的子节点需要绑定事件处理的时候,管理起来就会比较麻烦,因此可以委托给父元素来进行统一的管理,并且可以方便的动态添加和修改元素,不会因为元素的改动而修改事件绑定

事件冒泡:事件冒泡是指从目标元素开始往顶层元素传播,途中如果有节点绑定了相应的事件处理函数,这些函数都会触发

事件捕获:当某个元素触发某个事件(比如click),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素这个过程中事件相应的监听函数是不会被触发的,到达该目标元素节点后,触发该元素事件,如果没有绑定事件则不执行

36.vue中使用v-for为什么加key

引用

key是虚拟DOM对象的标识,当状态中的数据发生变化时,vue会根据新数据生成新的虚拟DOM,随后vue会进行新虚拟dom旧虚拟dom的差异比较

新旧虚拟DOM比较规则:
1.旧的虚拟DOM中找到了与新虚拟DOM中相同的key,会比较虚拟DOM中的内容是否改变,内容没变,直接使用之前的真实DOM,如果内容改变,则生成新的虚拟DOM,随后替换页面中之前生成的真实DOM
2.旧虚拟DOM中的内容未找到与新虚拟DOM中相同的key,则会创建新的真实DOM,随后渲染到页面当中

尽量不要使用索引index作key值,一定要用唯一标识的值,如id等。因为若用数组索引index为key,当数组中指数据出现你需添加,逆序删除等的破坏顺序的操作,这时候会重新更新index索引,对应着后面的虚拟DOM等key值全部更新了,这个时候还是会做不必要的更新,就像没有加key一样,因此index虽然能解决key不冲突的问题,但是并不能解决复用的情况。如果是静态数据渲染,用索引号index做key值是没有问题的。

37.axios和ajax的区别,fetch

传统的ajax一种原生javascript的异步请求技术,核心使用XMLHttpRequest(XHR)对象,多个请求之间如果有先后关系的话就会出现回调地狱

Jquery Ajax的出现是对原生XHR的封装,除此以外还增添了对JSONP的支持,但是它还是有一些局限性:

1.本身针对MVC的编程,不支持MVVM模式
2.基于原生的XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案
3.不符合关注分离的原则
4.JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务)

axios本质上也是对于原生XHR的封装,只不过是基于promise的实现版本,可以使用在浏览器和nodejs当中,符合es规范,它有以下的特征

1.从浏览器中创建XMLHttpRequest
2.从nodejs中创建http请求
3.支持promiseAPI
4.客户端支持防御CSRF攻击
5.提供了请求拦截和相应拦截
6.自动转换JSON数据
7.取消请求
8.转换请求和响应数据

Fetch 是浏览器内置的API,用于发送网络请求,也是基于promise的方式进行网络通信。 提供了对 Request 和 Response (以及其他与网络请求有关的)对象的通用定义方法,Fetch API 提供了一个 JavaScript接口,用于访问和操纵HTTP管道的部分,例如请求和响应。它还提供了一个全局 fetch()方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源,没有使用XMLHttpRequest

1.语法简单更加语义化
2.基于Promise实现,支持async await
38.如何实现单点登录SSO

首先来说单点登录(SingleSignOn),它指的是多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分 这里就不赘述,引用了两篇解释很详细的文章:

  1. 单点登录看这篇就够了

  2. 说说什么是单点登录

39.opacity:0,visiblity:hidden,display:none三者的区别

首先来说这三个属性都是用来控制元素的显示隐藏的

opacity:0visibility:hiddendisplay:none
dom结构元素会渲染在页面中,占据位置元素会被渲染在页面中,占位,只是被隐藏元素不会被渲染在dom中,不会占据位置
事件可以进行事件监听无法进行事件监听,点击事件不生效无法进行事件监听
性能不会引起重排,重绘,节省性能会发生重绘会引起元素的重排,耗费性能
继承会被子元素继承,且子元素并不能通过 opacity: 1 来取消隐藏是继承属性,子孙节点消失是由于继承了visibility: hidden,子元素可以通过设置 visibility: visible 来取消隐藏是非继承属性,由于元素从渲染树消失造成子孙节点消失,即使修改了子孙节点的属性也不会改变,毕竟子类也不会被渲染
transitiontransition 支持 opacity,opacity 可以延时显示和隐藏transition 支持 visibility,visibility 会立即显示,隐藏时会延时transition 不支持 display
40.浏览器的垃圾回收机制,缓存机制

浏览器有两种垃圾回收策略:

1.标记清除:标记阶段即为所有的活动对象做上标记,清除阶段则把没有标记的销毁
2.引用计数:对象有没有其他对象引用到它,如果没有引用指向该对象(引用计数为0),对象将被垃圾回收机制回收.

浏览器的缓存机制:

浏览器的缓存机制也就是我们说的HTTP缓存机制,其机制是根据HTTP报文的缓存标识进行的

HTTP请求(Request)报文:请求行-http头(通用信息头,请求头,实体头)-请求报文主体(只有POST才有报文主体)

1.强制缓存
2.协商缓存

强制缓存 :强制缓存就是客户端直接从本地获取资源,不需要发起请求,当客户初始发起请求给服务端,服务端会返回资源并返回一个Cache-Control一般来设置缓存的最大过期时间,那么这个时候浏览器再次发送请求时,它会先检查它的 cache-control 是否过期,如果没有过期则直接从本地缓存中拉取资源,直到cache-control失效,会重新连接服务端发起资源,重新返回资源和新的 cache-control

304协商缓存:协商缓存也被称作为对比缓存,是一种服务端缓存策略,当客户端第一次发起请求给服务端,服务端会返回该请求的请求资源和一个资源标识,该资源标识是当前请求资源的唯一标识,浏览器在次发起请求的时候会携带该资源标识,服务端则根据当前资源标识对比服务端和客户端的资源,如果一致,则返回304,如果不一致,则返回200并返回新的资源和资源标识。

41.前端安全问题

XSS全称(Cross Site Scripting)跨站脚本攻击:是前端最常见的安全问题。XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中,攻击者通过注入非法的html标签或者javascript代码,从而当用户浏览该网页时,控制用户浏览器。

CSRF全称(Cross-Site Request Forgeries)跨站请求伪造:指攻击者冒充用户发起请求(在用户不知情的情况下),完成一些违背用户意愿的事情

42.浅谈一下null和undefined

通俗的话来讲undefined指的是未定义一般声明一个变量不设置值则为undefined,而null空值,一般null用来指一个空对象 相同点:

  • 值相等

    console.log( undefined == null )//true
    
  • 转布尔值都为false

    console.log( Boolean(undefined) )//false 
    console.log( Boolean(null) )//false
    

不同点:

  • 数据类型不同

    console.log(undefined === null) // false
    
  • 转number类型的值不同

    console.log(Number(undefined)) // NaN
    console.log(Number(null)) // 0
    
43.webpack的打包流程
1.初始化参数:解析webpack配置参数,合并shell并传入和webpack.config.js文件配置的参数,形成最后配置的结果
2.开始编译:将上一步得到的参数初始化compiler对象,注册所有配置的插件,监听webpack构建生命周期的时间节点做出相应的反应,执行对象的run方法开始执行编译
3.确定入口,从配置的entry入口,开始解析文件构建AST语法树,找出依赖,递归下去
4.完成模块的编译并输出,递归完成后得到每个文件结果,包含每个模块以及他们之间的依赖关系,根据entry或分包配置生成代码块
5.输出完成,输出所有的chunk到文件系统

webpack中loader和plugin的区别:

loader即为文件加载器,操作的是文件,将文件A转化为文件B,是一个单纯的文件转化过程。

plugin为插件,是一个扩展器,丰富webpack本身,增强功能,针对的是loader结束之后,webpack打包的整个过程,他并不直接操作文件,而是基于事件机制工作,监听webpack打包过程中的某些节点,执行广泛的任务。

44.防抖和节流

防抖是指一个频繁触发的函数在规定的时间之内,只让最后一次触发生效

话不多说上代码一般面试过程中也会让手写

 function debounce(fn, delay) {
        let timer = null
        return function () {
            clearTimeout(timer) // 每次进来清楚上次的定时器
            let args = arguments
            timer = setTimeout(() => {
                fn.apply(this, args) // 改变函数的this指向,并接收函数的参数(如果有参数)
            }, delay)
        }
  }

节流是指规定的时间之内事件多次触发,将其设置成每隔一段时间触发一次

function throttle(fn, delay) {
        let t1 = 0 // 定义初始时间
        return function () {
            let t2 = new Date() // 获取当前时间
            // 判断当前时间-初始时间大于规定的时间就触发
            if (t2 - t1 > delay) {
                fn.apply(this, arguments)
                t1 = t2
            }
        }
 }
45.vue路由有哪几种模式,路由守卫

vue路由有两种模式hash模式、history模式,vue默认开启hash模式的路由,可通过mode属性配置(mode:"histroy" // 或者 "hash")

  • hash模式:即地址栏URL中的存在'#'符号,#及其后面的内容就是hash值,hash值不会包含在HTTP请求中,hash路由是依据#后的地址进行监听跳转的,通过location.hash来获取当前的hash值,通过hashChange事件进行监听,相同的hash地址不会触发该事件,hash值的改变都会在浏览器的访问历史中增加一个记录,可以通过浏览器的回退/前进按钮控制hash值
  • history模式:该模式地址干净,美观,但是相比hash模式兼容性较差,它是利用h5新增的pushState和replacestate两个方法来记录路由状态,通过onpopstate进行监听实现页面跳转的,该模式应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题

$router$route的区别

  • $route是一个当前路由信息的对象,包含了当前路径,查询参数,路径参数等,该对象是制度的,不可直接修改其属性
  • $router是vue的Router的实例对象,包含了许多导航的路由操作API,比push/replace

全局路由守卫

  • router.brforeEach(to.form,next) 全局前置守卫:主要用于登录验证
  • router.bedoreResolve(to.form,next) 全局解析守卫在每次导航时都会触发
  • router.afterEach(to,form) 全局后置守卫:在路由跳转完成后出发,由于此时路由已经完成跳转嘛,所以不会有next,一般用于更改页面标题,声明页面等辅助功能

路由独享守卫

  • router.beforeEnter

组件路由守卫

  • beforeRouteEnter
  • beforeRouteUpdate
  • beforeRouteLeave
beforeRouteEnter (to, from, next) { 
// 在渲染该组件的对应路由被 confirm 前调用 
// 不!能!获取组件实例 `this` 
// 因为当守卫执行前,组件实例还没被创建 
}, 
beforeRouteUpdate (to, from, next) { 
// 在当前路由改变,但是该组件被复用时调用 
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, 
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 
// 可以访问组件实例 `this` 
}, 
beforeRouteLeave (to, from, next) { 
// 导航离开该组件的对应路由时调用 
// 可以访问组件实例 `this` }

完整的导航解析流程

  • 导航被触发。
  • 在失活的组件里调用 beforeRouteLeave 守卫。
  • 调用全局的 beforeEach 守卫。
  • 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  • 在路由配置里调用 beforeEnter
  • 解析异步路由组件。
  • 在被激活的组件里调用 beforeRouteEnter
  • 调用全局的 beforeResolve 守卫 (2.5+)。
  • 导航被确认。
  • 调用全局的 afterEach 钩子。
  • 触发 DOM 更新。
  • 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
46.js继承方式
  • 原型链继承
  • 构造函数继承
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承
47.flex布局中flex:1是哪三个属性的缩写

flex:~:它其实包含了三个属性flex-growflex-shrinkflex-basis,按照翻译来看依次是增长、收缩、基础

  • flex-basis首先来说flex-basis你可以理解width属性,或者在flex-direction:column情况下的height属性,但是flex-basis有更高的优先级,当flex-basis:auto时,可以理解为width为父容器的宽度
  • flex-grow这个属性决定容器剩余的空间分配方式
  • felx-shirink这个属性决定容器超出空间的分配方式
48.前端基本的代码规范问题

1.vue组件命名首字母大写驼峰命名,在html中使用组件时采用-连字符连接

2.变量采用小驼峰命名

3.函数名采用小驼峰式命名,构造函数使用大驼峰

4.常量命名全部大写用_连接

49.typeof和instanceof的区别

typeof是一个一元运算符,可以用来检测基本数据类型和函数,返回该当前运算数对应的类型的字符串 注意:看以下栗子可以看出typeof无法精准判断出引用类型数据(全部返回object)

typeof [] // object
typeof {} // object
typeof function // function
typeof null // object

instanceof用来判断当前变量是否是某构造函数的实例,返回一个布尔值,可以判断引用类型数据,不能精准判断出基本类型数据

// 定义构造函数
let Car = function() {}
let byd = new Car()
byd instanceof Car // true
let car = new String('xxx')
car instanceof String // true
let str = 'xxx'
str instanceof String // false
//instanceof 判断数组有弊端
let arr = []
console.log(arr instanceof Object); // true
console.log(arr instanceof Array); // true

综合看来以上两种方法都有一定的弊端,可以使用object.prototype.tostring.call()来精确的判断数据类型

Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(666); // "[object Number]"
Object.prototype.toString.call('xxx'); // "[object String]"
50.箭头函数和普通函数的区别
  1. 声明方式不同
  • 声明一个普通函数需要使用function关键字,并且function关键字可以声明一个匿名函数也可以声明成一个具名函数
  • 声明一个箭头函数无需使用function关键字,比普通函数简洁
  1. this的指向不同
  • 普通函数this指向函数运行时调用它的对象
  • 箭头函数本身没有this,它的this就是定义时上层作用域中的this
  • 也就是说,箭头函数的this指向是固定的,相比之下,普通函数的this指向可以改变
  1. 箭头函数没有原型
  2. 箭头函数不能当成一个构造函数
  3. 箭头函数没有自己的arguments
51.http请求有哪些方式,有什么区别

常见的http请求方式有getpost请求

   区别:
   1.get请求一般用于获取数据,post请求一般用于发送数据
   2.get请求如果需要传递参数会拼在url后面,post请求传递参数需要把参数放在请求体中发送
   3.get请求传递参数大小有限制(由于浏览器地址栏限制),post请求参数大小没有限制
   4.get请求安全性较低(参数会直接暴露在url上,不安全),post请求安全性较高,
   5.get请求会被浏览器主动缓存,post请求不会走缓存
52.toString()和String()的区别
  • 语法区别:(par是指要转换的内容,可以是数值,布尔值,对象和字符串) toString()括号内不能包含参数,需要以par.toString()的形式使用,String()的参数需要包含在括号内,使用语法为String(par)

  • 对于转换值的区别:toString()不能处理转换的值是null或者undefined的情况,而String()是可以处理转换的值是null或者undefined的参数,转换值为null或undefined的字符串

image.png

53.template的原理和作用

template是html中的一个内容模板元素,是一种用于保存客户端内容机制,该内容在加载页面时不会呈现,但随后可以在运行时使用 JavaScript 实例化。

template的作用是模板占位符,可帮助我们包裹元素,但在循环过程当中,template不会被渲染到页面上,,vue中的<template>是给vue提供的容器标签,只起到包裹性质的作用,它不会被渲染为真正的 DOM 元素。

54.v-model语法糖
  1. 作用在表单元素上:动态绑定了 input 的 value, 指向了 messgae 变量,并且在触发 input 事件的时候去动态把 message设置为目标值
  2. 作用在组件上:自定义组件中,v-model默认利用名为value的prop和名为input的事件;通过v-model属性配置子组件接收的prop名称,及派发的事件名称;
<input v-model="sth" />
//  等同于
<input v-bind:value="message" 
 v-on:input="message=$event.target.value">
//$event 指代当前触发的事件对象;
//$event.target 指代当前触发的事件对象的dom;
//$event.target.value 就是当前dom的value值;
//在@input方法中,value => sth;
//在:value中,sth => value;
55.vue自定义指令

自定义指令是一种用于扩展vue的模板语法的机制,通过自定义指令可以在DOM元素上添加自定义行为,并在元素插入更新和移除时进行相应的操作

vue的自定义指令通过vue.direactive函数来定义,它接收两个参数:指令名称和指令选项对象。指令选项对象包含一系列钩子函数用于定义指令的行为

bind(el,bind){} // 指令第一次绑定到元素时调用,可执行一次性初始化设置
inserted(el,bind){} // 被绑定元素出入父元素时调用
update(el,bind){} // 被绑定元素所在的组件更新时调用
unbind(el,bind){} // 指令与元素解绑时调用,可执行清理操作
56.插槽的使用

slot插槽的作用是指向子组件的指定位置插入一段自定义内容,这个内容可以是HTML或者其他的组件 插槽分为三种

// 1.默认插槽
<slot></slot> // 只需要在子组件中写入slot,然后在父组件中插入内容
// 2.具名插槽
// 给插槽slot定义一个name属性
// 在父组件中就可以使用v-slot:name或者#name往指定的插槽填充内容
<slot name=“xxx”></slot> 
// 3.作用域插槽
// 是为了在父组件中访问子组件的数据,这里的value是存放在data中的数据
// 接着我们就可以在父组件中定义一个变量来接收子组件中传递的数据
<slot name="xxx" v-bind:value="value"></slot>
57.vue中为什么监听不到数组的变化

对于对象而言,每一次的数据变更都会对对象的属性进行一次枚举,一般对象本身的属性数量有限,所以对于遍历枚举等方式产生的性能损耗可以忽略不计,但是对于数组而言,数组包含的元素量是可能达到成千上万,假设对于每一次数组元素的更新都触发了枚举/遍历,其带来的性能损耗将与获得的用户体验不成正比,故vue无法检测数组的变动。

解决方法:

1.this.$set(array, index, data)

2.splice方法

58.keep-alive的使用

keep-alive是vue中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染dom,可以通过inclue设置名称匹配的组件被缓存,exclude设置名称匹配的组件不会被缓存,设置缓存后组件加载的生命周期会新增activeddeactived两个钩子函数

59.TypeScript

typescript是一个强类型的javascript的超集,支持es6语法,支持面向对象编程的概念,如类,接口,继承,泛型等,ts并不直接在浏览器中运行,需要编译器编译成js来运行

  • 作用:快速简单易于学习,在编译时能提供错误检查功能,支持静态类型,强类型,模块,可选参数等,能通过使用继承来支持可重用性
60.对nodejs的理解

nodejs是一个基于chromeV8引擎的js运行环境

基于express框架,可以快速的构建web应用

  • fs模块是nodejs官方提供的用来满足用户对文件的读写操作需求的模块
  • path模块是nodejs官方提供的用来处理路径的模块,它提供了一系列的方法和属性,用来满足用户对路径的处理需求
  • http模块是node.js官方提供的、用来创建web服务器的模块,通过http.createServer()方法,就能方便的把一台普通的电脑,变成一台Web服务器,从而对外提供Web资源服务
61.vue3相比vue2做了哪些改变
  1. 生命周期

    可通过组合式API使用生命周期钩子,在使用时需要先引入

    onBeforeMount(() => {})
    onMounted(() => {})
    onBeforeUpdate(() => {})
    onUpdated(() => {})
    onBeforeUnmount(() => {})
    onUnmounted(() => {})
    
  2. 组件模板没有根标签,vue会内部处理生成一个fragment片段标签

  3. vue3中使用组合式API可将同一逻辑的内容写到一起,增强了代码的可读性、内聚性,其还提供了较为完美的逻辑复用性方案。

  4. Vue3提供Teleport组件可将部分 DOM 移动到 Vue app 之外的位置。比如项目中常见的 Dialog 弹窗。

  5. 响应式原理改用Proxy:由于vue2中使用object.defineProperty无法监听对象或数组新增、删除的元素。

  6. 对typescript的支持

  7. treeShaking打包优化:Vue3中将全局 API 进行分块。如果您不使用其某些功能,它们将不会包含在您的基础包中

62.ts中type和interface的区别

interface 是接口,用于定义一个对象,描述对象的属性和方法。

type 是类型别名,用于给各种类型定义别名,让 TS 写起来更简洁、清晰。

63.vue和react的区别

共同点:二者都使用了虚拟dom的方法

不同点:

  1. 数据驱动方式不同:react是单向数据流来管理数据,所以react中组件之间的数据交互相对复杂些,而vue使用了双向数据绑定来管理数据,使得组件之间的数据交互更加简洁。
  2. 组件化的方式不同,react是基于组件实现的,组件包含了状态和行为,所有的组件共享一个状态树,而vue也是基于组件的,但每个组件都有自己的状态,并可以很容易的将数据和行为绑定在一起
  3. 模板语法不同:react使用jsx语法,将html和说js结合在一起,vue则是将html,css,js组合在一起用各自的处理方式,使组件有更高的可读性和可维护性
  4. 生命周期不同,react生命周期分为三个阶段:初始化,更新,卸载。vue的生命周期分为八个阶段:创建,挂载,更新,销毁等 react使用状态发生变化,重新渲染页面
64.opacity设置透明度和rgba中的a设置透明度有何区别

rgbaopacity都能设置透明效果,他们的区别是opacity设置后会对他的自己所有内容设置透明度,rgba只会对颜色或背景色造成影响,并且子级不会继承透明效果。

65.MVVM模式和MVC模式

mvc由Model(模型),View(视图),Controller(控制器)三部分组成,controller负责将model的数据用view显示出来

mvvm由Model(数据模型),View(UI视图),ViewModel(数据对象)三个部分组成

两者的最大区别是 mvvm它实现了view和model的自动同步,当model属性改变时,不需要手动操作Dom元素去改变view显示,而是改变属性后,该属性对应的view会自动改变

66.vue组件传值

vue2

  1. 父组件使用props向子组件传值
  2. 子组件通过$emit触发自定义事件向父组件传值
  3. 使用ref获取子组件实例传值
  4. 使用$parent或$root向父辈组件传值
  5. 使用attr和listeners实现祖辈隔代传值
  6. 使用eventBus事件总线 实现兄弟组件传值
  7. 使用provide和inject
  8. 使用vuex实现统一状态管理

vue3

  1. 父组件使用props向子组件传值 (defineProps
  2. 子组件通过$emit触发自定义事件向父组件传值 (defineEmits
  3. 使用ref获取子组件实例传值 (需要通过defineExpose对外暴露)
  4. 使用$parent或$root向父辈组件传值(需要通过defineExpose对外暴露)
  5. 使用useAttrs方法,可以获取组件身上的属性和方法(props优先级高于userAttrs
  6. 使用eventBus事件总线 实现兄弟组件传值(mitt插件
  7. 使用provide和inject
  8. 使用pinia实现统一状态管理
  9. 使用插槽形式传值
67.vue3中的suspense

<Suspense> 是一个内置组件,用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。

68.TS中interface的优点

interface(接口) 是 TS 设计出来用于定义对象类型的,可以对对象的形状进行描述。而type (类型别名),顾名思义,类型别名只是给类型起一个新名字。它并不是一个类型,只是一个别名而已