2022前端面试题集及解答

1、做接口前后端联调如何分析是前端还是后端问题
答:

  • 抓包请求参数,页面上输入参数,后端接口文档需要的参数是否一致,如果不一致就是前端bug
  • 如果一致没有返回预期的效果,就是后端bug;
  • 渲染中,参数一致,也返回预期效果,但是页面数据显示不正确就是前端bug;
    也可以用http状态说明,一般4XX,5XX是后端问题,其它是前端问题

2、cookies,sessionStorage和localStorage都有什么区别
答:

  • cookies在请求数据传输的时候会携带至后台,sessionStorage,localStorage是纯客户端缓存
  • cookies有过期机制,sessionStorage是会话级缓存,窗口关闭后会销毁,localStorage是本地持久缓存手动才会清除
  • cookies只能存储4KB并且不推荐前端用js直接操作,sessionStorage,localStorage存储有5MB

3、箭头函数和function普通函数之间有什么区别?
答:

  • 箭头函数中的this指向是固定不变的,只会从自己的作用域的上一层继承,而function定义的函数,this指向会随着调用环境变化而变化。
  • 箭头函数没有prototype,不可用new,不能用call和apply绑定,因为没有this.

4、常见的SPA首屏优化方式有哪些?
答:
大家基本都会采用服务器ssr渲染,路由懒加载。
首屏优化的核心是加载和解析的性能。
加载优化:

  • 减少http请求,合理使用缓存,再快的网速,再大的并发数都不如直接跳过请求。

  • 启用CDN加速:让不同地区的人都可以享受稳定可靠的下载速度

  • 静态资源分域,将图片,音视频等静态资源放到不同的域名,防止加载时受浏览器并发数限制

  • DNS预解析,浏览器通过查询系统hosts或者进一步询问本地的DNS服务器就可以获得结果,但是也可能要经历完整的寻址过程:览器缓存-系统缓存-路由器缓存-ISP DNS缓存-递归搜索。DNS解析所需的时间差异非常大,延迟范围可以从1ms(本地缓存结果)到普遍的几秒钟时间。所以利用DNS预解析是有意义的。最明显的例子,DNS预解析在某个页面中包含非常多的域名非常有效,如搜索结果页。如何使用X-DNS_prefetch-Control: on|off,on:启用DNS预解析。

  • 尽量启用http2
    解析优化

  • 资源体验优化:雪碧图,压缩图片,字体,音视频等文件的体积

  • 按需加载:

  • 使用骨架屏

5、VueX的重要核心属性有哪些?
答:五大核心属性:

  • state,用来存放数据,相当于vue里面的data
  • mutaions,同步方法,可以修改state的data
  • actions,异步方法,可以发送请求,不能直接修改state中的数据,要通过调用mutations里面的方法来修改;
  • getters:相当于vue里面的computed,是state中数据的派生数据
  • modules,用于上述四个数据的分模块注册

6、什么是跨域?一般怎么解决跨域问题?
答:浏览器的同源策略的三大要素:协议相同、域名相同、端口 相同;同源三要素有一项不符合就是跨域。解决跨域的方法有:通过jsonp,或者通用代理服务器的方法通过本地代理转发。

7、Vue中v-if和v-show的共同点和不同点有哪些?
答:相同点:都能控制元素的显示隐藏.不同点:v-show本质是通过控制display:none;v-if是动态的向DOM树中添加或删除DOM元素,v-if更适合带有权限的操作,v-show更适合日常使用。可以减少数据渲染。

8、vue有哪些优点?
答:

  • 简单易学,文档资料集全,UI库很多,社区完善
  • 轻量级,MVVM框架
  • 双向数据绑定,页面渲染更加高效
  • 组件化开发,组件复用
  • Virtaual DOM,操作DOM非常耗性别,而虚拟DOM操作属于预处理,极大解放dom操作

9、vue2.0与3.0的区别
答:

  • virtual DOM 完全重写,mounting & patching 提速 100%;
  • 默认进行懒观察(lazy observation):在 2.x 版本里,不管数据多大,都会在一开始就为其创建观察者。当数据很大时,这可能会在页面载入时造成明显的性能压力。3.x 版本,只会对「被用于渲染初始可见部分的数据」创建观察者,而且 3.x 的观察者更高效。
  • 部分命令发生了变化:比如启动项目 npm run serve
  • 默认项目目录结构也发生了变化:如移除了配置文件目录,config 和 build 文件夹
  • 数据双向绑定:放弃 Object.defineProperty ,使用更快的原生 Proxy;
  • 3.0 新加入了 TypeScript 以及 PWA 的支持
  • vue2和vue3生命周期钩子函数的不同 Vue2--------------vue3 beforeCreate -> setup() created -> setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted activated -> onActivated deactivated -> onDeactivated errorCaptured -> onErrorCaptured
  • vue2和vue3定义数据变量和方法的改变:在vue2中定义数据变量是data(){},创建的方法要在methods:{}中。而在vue3中直接在setup(){}中,在这里面定义的变量和方法因为最终要在模板中使用,所以最后都得 return。

10、为什么最好把link放在head之间?
答:第一:规范要求;第二:从页面渲染原理来说,CSS的解析和html的解析需要同时进行,CSS的解析不会阻塞页面渲染。html解析得到的dom树需要css解析的样式规则,然后通过加流重绘计算后交给GPU负责合并图层生成页面。所以在不阻塞的情况下,你是选择串行还是并行呢,有什么更好的理由不让放head呢?第三:如果不放head会出现:一些浏览器会阻止渲染导致页面不能逐步呈现,没有样式或者空白不利用户体验。

11、说一下CSS选择器有哪些?
答:元素,id,类,分组,兄弟元素,伪类,属性,通配,复合,子元素,后代元素选择器

12、ES5数据结构中Set和Map有什么区别?
答:set可以用于数组去重,因为set值唯一,而map不唯一,map存储的是键值对,可以通过get方法取值而set不行,set存储的是值

13、为什么要封装axios?
答:封装之后,相当于给axios给出规范,使用起来更加简单,调用 方式统一,利于代码的管理和维护

14、js中bind,call,apply之间有什么区别?
答:三者都是重定义对象中的this:call跟apply绑定完this会立即调用当前的函数,apply传入的参数必须是数组,bind通常绑定完this返回不会立即调用当前函数,而是将函数返回。

  • obj.method.bind(“obj中this的指向对象“,”参数“,”参数”)();//bind返回新函数,需要调用
  • obj.method.call(“obj中的this的指向对象”,“参数”,“参数”);
  • obj.method.apply(“obj中的this的指向对象”,[‘参数’,‘参数’]);

15、什么是回流和什么是重绘?什么情况下使用?
回流:也有称重排,当渲染树节点发生改变,影响了节点的几何属性(如宽、高、内边距、外边距、或是float、position、display:none;等等),导致节点位置发生变化,此时触发浏览器重排(reflow),需要重新生成渲染树。
重绘:渲染树节点发生改变,但不影响该节点在页面当中的空间位置及大小。譬如某个div标签节点的背景颜色、字体颜色等等发生改变,但是该div标签节点的宽、高、内外边距并不发生变化,此时触发浏览器重绘(repaint)
注意:回流必将引起重绘,而重绘不一定会引起回流。
何时回引起回流?
1、添加或者删除可见的DOM元素;2、元素位置改变——display、float、position、overflow等等;3、元素尺寸改变——边距、填充、边框、宽度和高度4、内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;5、页面渲染初始化;6、浏览器窗口尺寸改变——resize事件发生时;
如何避免回流?
1、将动画效果应用到position属性为absolute或fixed的元素上,否则会引起父元素及后续元素频繁回流。
2、避免频繁操作DOM树,可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
3、避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。

16、Vue组件间是如何通信的?
1、父子组件间通信;父向子传递数据是通过 props,子向父是通过 events( e m i t ) ; 通 过 父 链 / 子 链 也 可 以 通 信 ( emit);通过父链 / 子链也可以通信( emit/parent / c h i l d r e n ) ; r e f 也 可 以 访 问 组 件 实 例 ; p r o v i d e / i n j e c t A P I ; children);ref 也可以访问组件实例;provide / inject API; childrenref访provide/injectAPIattrs/ l i s t e n e r s ( listeners( listenersattrs与 l i s t e n e r s 是 两 个 对 象 , listeners 是两个对象, listenersattrs 里存放的是父组件中绑定的非 Props 属性, l i s t e n e r s 里 存 放 的 是 父 组 件 中 绑 定 的 非 原 生 事 件 。 ) 2 、 兄 弟 通 信 B u s ; V u e x 3 、 跨 级 通 信 B u s ; V u e x ; p r o v i d e / i n j e c t A P I 、 listeners里存放的是父组件中绑定的非原生事件。) 2、兄弟通信Bus;Vuex 3、跨级通信Bus;Vuex;provide / inject API、 listeners2BusVuex3BusVuexprovide/injectAPIattrs/$listeners

17、vuex有哪些辅助函数,都有什么作用
vuex中提供了一些非常方便的辅助函数,比如mapState、mapGetter、mapMutation、mapAction等。
mapGetters和mapState用法一样,通过简单的…mapState([‘userName’])来代替比较长的计算属性函数在computed中调用即可。
mutations和actions返回的是函数,所以应该放在组件的methods属性中。

18、document.load与document.ready的区别?
load是页面加载完成后(包括所有的资源DOM文档树,css文件,js文件,图片资源)执行的一个函数;
ready是页面DOM树加载完成(不包括CSS,图片)执行的一个函数;

19、Vue的常见修饰符有哪些,各有什么作用?
Vue的常见修饰符:
事件修饰符:
.stop 阻止事件继续传播
.once 事件将只会触发一次
.prevent 阻止标签默认行为
.native可以理解为该修饰符的作用就是把一个 Vue 组件转化为一个普通的 HTML 标签(
.passive当我们在监听元素滚动事件的时候,会一直触发 onscroll 事件,在 PC 端是没啥问题的,但是在移动端,会让我们的网页变卡,因此我们使用这个修饰符的时候,相当于给 onscroll 事件整了一个 .lazy 修饰符。
表单修饰符:
v-model.lazy 转化为change事件再同步
v-model.number转化为数字内型
v-bind修饰符
.sync能对props进行一个双向绑定

.prop通过自定义属性存储变量,避免暴露数据,防止污染 HTML 结构;

20、javascript如何检测一个字符串类型?
JS数据类型主要分为两大类:基本数据类型和引用数据类型
基本数据类型:number、string、boolean、null、undefined、symbol(es6)
引用数据类型:object(array、function、date…)
1)typeof str == “string”:typeof 一般用来判断基本数据类型,除了判断null会输出"object",其它都是正确的,typeof 判断引用数据类型时,除了判断函数会输出"function",其它都是输出"object"
2)instanceof 可以准确的判断引用数据类型,它的原理是检测构造函数的prototype属性是否在某个实例对象的原型链上
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object);
3)constructor(构造函数):null和undefined是无效的对象,所以他们不会有constructor属性!
2022前端面试题集及解答_第1张图片
4) Object.prototype.toString.call()
Object.prototype.toString.call(‘’) ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); // [object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; // [object global] window 是全局对象 global 的引用

21、js中new操作符做了什么事情
new操作符的主要作用是产生对象。通过new创建空对象,为创建对象打基底,开辟了内存,将this 指针指向这个对象。

22、javascript如何实现继承,有什么优缺点?
JavaScript常见的继承方式:
1)原型链继承

function Parent() {
  this.name = 'parent1';
  this.play = [1, 2, 3]
}
function Child() {
  this.type = 'child2';
}
Child.prototype = new Parent();
let child1 = new Child()
console.log(child1)             // Parent { type: 'child2' }
console.log(child1.name)        // parent1

优点:不仅能继承父类的实例属性和方法,还能继承原型属性或者方法
缺点:父类的引用属性被共享了,改动一处其他的也会被改

2)构造函数继承(借助 call)

function Parent2(){
    this.name = 'parent1';
    this.age = '18';
}

Parent2.prototype.getName = function () {
    return this.name;
}

function Child(){
    Parent2.call(this);
    this.type = 'child'
}
let child = new Child();
console.log(child);  // 没问题, Child { name: 'parent1', age: '18', type: 'child' }
console.log(child.getName());  // 会报错 child.getName is not a function 

优点:父类的引用属性不会被共享,优化了第一种继承方式的弊端
缺点:但是只能继承父类的实例属性和方法,不能继承原型属性或者方法

3)组合继承
前面我们讲到两种继承方式,各有优缺点。组合继承则将前两种方式继承起来

function Parent3 () {
    this.name = 'parent3';
    this.play = [1, 2, 3];
}

Parent3.prototype.getName = function () {
    return this.name;
}
function Child3() {
    // 第二次调用 Parent3()
    Parent3.call(this);
    this.type = 'child3';
}

// 第一次调用 Parent3(), 原型链继承
Child3.prototype = new Parent3();
// 手动挂上构造器,指向自己的构造函数
Child3.prototype.constructor = Child3;
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play);  // 不互相影响,[ 1, 2, 3, 4 ] [ 1, 2, 3 ]
console.log(s3.getName()); // 正常输出'parent3'
console.log(s4.getName()); // 正常输出'parent3'

这种方式看起来就没什么问题,方式一和方式二的问题都解决了,但是从上面代码我们也可以看到Parent3 执行了两次,造成了多构造一次的性能开销

4)原型式继承:Object.create方法实现普通对象的继承
let parent4 = {
name: “parent4”,
friends: [“p1”, “p2”, “p3”],
getName: function() {
return this.name;
}
};

let person4 = Object.create(parent4)

5)寄生组合式继承

class Person {
  constructor(name) {
    this.name = name
  }
  // 原型方法
  // 即 Person.prototype.getName = function() { }
  // 下面可以简写为 getName() {...}
  getName = function () {
    console.log('Person:', this.name)
  }
}
class Gamer extends Person {
  constructor(name, age) {
    // 子类中存在构造函数,则需要在使用“this”之前首先调用 super()。
    super(name)
    this.age = age
  }
}
const asuna = new Gamer('Asuna', 20)
asuna.getName() // 成功访问到父类的方法,Person: Asuna

寄生组合式继承,借助解决普通对象的继承问题的Object.create 方法,在前面几种继承方式的优缺点基础上进行改造,这也是所有继承方式里面相对最优的继承方式

23、改变this指针的方向
1)call、apply、bind三者为改变this指向的方法
call

function fn(a,b,c){
        console.log(this,a+b+c); // this指向window
    }
    fn();
    fn.call(document,1,2,3);//call改变之后this指向document  
    //输出 #document 6   1,2,3是实参 结果相加为6
      fn.apply(document,[1,2,3]); //apply
      let ff = fn.bind('小明',1,2,3); //手动调用一下

call、apply与bind区别:前两个可以自动执行,bind不会自动执行,需要手动调用
call、bind与apply区别:前两个都有无数个参数,apply只有两个参数,而且第二个参数为数组

24、JS中同步和异步执行顺序

JavaScript语言是单线程,就意味着所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。同步任务指的是:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。异步任务指的是,不进入主线程、而进入任务队列(task queue)的任务,只有任务队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

25、keep-alive属性及生命周期
生命周期 用的是激活(activated)/停用(deactivated)。如何解决keep-alive缓存:第一种:使用会将数据保留在内存中;第二种:beforeRouteLeave(to, from, next) {
// 设置下一个路由的 meta
to.meta.keepAlive = false;
// C 跳转到 A 时让 A 不缓存,即刷新
next();
},第三种每次组件渲染的时候,都会执行beforeRouteEnter
beforeRouteEnter(to, from, next){
next(vm=>{
console.log(vm)
// 每次进入路由执行
vm.getData() // 获取数据
})
},

26、webpack代码分隔的实现
1)多入口打包2)动态引入

27、遍历数组的方法?
1) indexOf() 遍历数组,返回元素在数组中第一次出现的下标,两个参数,第一个是要查找的元素,第二个是开始查找的下标
2) forEach() 遍历数组获取每一个元素,没有返回值;
3)map() 遍历数组,返回一个新数组,数组由参数里的返回值组成.必须使用return.
4) filter() 遍历数组,返回一个新数组:新数组由参数里,条件为true的元素组成
5)some() 遍历数组,返回参数函数里符合条件的元素,只要检测到有一个元素符合条件就return.
6)reduce() 遍历数组, 返回参数函数里的返回值.一般作为累加器计算总价
7) every() 遍历数组,判断数组元素是否全部符合函数参数里的条件,全部满足返回true,否则false.
8)for of 遍历数组/字符串

前端学习群,需要的要我jjljzh

你可能感兴趣的:(前端,html5,vue,前端,vue.js,http)