2023前端面试合集

  • js基础类
    • 1.什么是原型链及原型链存在的意义
    • 2.什么是作用域?
    • 3.什么是闭包?
    • 4.浏览器的渲染过程
    • 5.数据类型简介:
    • 6.深浅拷贝
    • 7.css水平居中的几种方法
    • 8.你知道数组有哪些实用高阶函数吗?
    • 9.call、apply、bind的区别
  • Vue类
    • 10.vue2和vue3的区别
    • 11.route和router的区别
    • 12.vue路由两种模式的区别
    • 13.keep-alive的生命周期
    • 14.mvvm的优势:
    • 15.虚拟DOM的作用
    • 16.vue如何实现性能优化
    • 17.路由的守卫
    • 18.Vue 组件中 data 为什么必须是函数?
    • 19.ES6的新特性有哪些?
    • 20.模块化开发的好处:
    • 21.JSON.stringify深拷贝的缺点
    • 22.防抖和节流,有什么区别?
    • 23.JS中promise对象的作用与使用
    • 24.async-await语法
    • 25.宏任务和微任务

js基础类

1.什么是原型链及原型链存在的意义

(1).什么是原型链

答:原型链就是每个对象拥有一个原型对象,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系就被称为原型链。
简单理解:原型链就是在当前对象中找不到定义的话,会在当前对象的原型对象上继续查找,直至undefined。

(2).原型和原型链存在的意义是什么?

答:实例对象可以共享构造函数原型属性和方法,节省内存。构造函数原型上的属性和方法越多,节省内存越大。

2.什么是作用域?

作用域分为:1.全局作用域,2. 函数作用域, 3.块级作用域

答:作用域是在运行时代码中的某些特定部分变量,函数和对象的可访问性,作用域决定了代码区块中变量和其他资源的可见性。

3.什么是闭包?

能够访问其他函数内部变量的函数,被称为闭包。
那种场景会用到,一些事件函数的封装.
闭包本身会带来常驻内存,引用后不会及时销毁.

4.浏览器的渲染过程

当我们在浏览器里输入一个 URL 后,最终会呈现一个完整的网页。会经历以下几个步骤:

1、HTML 的加载

页面上输入 URL 后,会先拿到 HTML 文件。HTML是一个页面的基础,所以会在最开始的时候下载它,下载完毕后就开始对它进行解析

2、其他静态资源的下载
HTML 在解析的过程中,如果发现 HTML 文本里面有一些外部的资源链接,比如 CSS、JS 和图片等,会立即启用别的线程下载这些静态资源。在 head 中遇到 JS 文件时,HTML 的解析会停 下来,等 JS 文件下载结束并且执行完,HTML 的解析工作再接着来,防止 JS 修改已经完成的解析结果

由上得知,JS 文件放在 head 中属于同步加载,会阻塞 DOM 树的构建,进而影响页面的加载。当 JS 文件较多时,页面白屏的时间也会变长

5.数据类型简介:

js常用的基本数据类型包括undefined - - (未定义)、null- - (空的)、number - - (数字)、boolean- - (布尔值)、string- - (字符串)、Symbol - - (符号);

js的引用数据类型也就是对象类型Object- - (对象),比如:array - - (数组)、function - - (函数)、date - - (时间)等;

6.深浅拷贝

浅拷贝:浅拷贝是会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化

深拷贝: 深拷贝和浅拷贝是针对复杂数据类型(对象及数组)来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝。深拷贝复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。 深拷贝后的对象与原来的对象是完全隔离的,互不影响,对一个对象的修改并不会影响另一个对象。

7.css水平居中的几种方法

  1. margin和width实现,在容器上定义一个固定的宽度,配合margin左右的值为auto。
  2. 内容设置inline-block和父元素text-align:center。
  3. 绝对定位实现水平居中,我想大家也非常的熟悉了,并且用得一定不少。
  4. css3的flex实现。
  5. css3的fit-content 。

8.你知道数组有哪些实用高阶函数吗?

数组有很多的方法,都很简单方便,向push,pop等等,但是对于高阶函数在实际使用用的很多,现在就来总结一下方法使用及场景首先,什么是高阶函数:高阶函数,就是函数中可以传入另一个函数作为参数的函数。
点击查看案例详解

  1. forEach
  2. filter
  3. map
  4. sort
  5. some
  6. every
  7. reduce
  8. find

9.call、apply、bind的区别

  • call、apply、bind相同点:都是改变this的指向,传入的第一个参数都是绑定this的指向,在非严格模式中,如果第一个参数是nul或者undefined,会把全局对象(浏览器是window)作为this的值,要注意的是,在严格模式中,null 就是 null,undefined 就是 undefined
  • call和apply唯一的区别是:call传入的是参数列表,apply传入的是数组,也可以是类数组
  • bind和call、apply的区别: bind返回的是一个改变了this指向的函数,便于稍后调用,不像call和apply会立即调用;bind和call很像,传入的也是参数列表,但是可以多次传入,不需要像call,一次传入
  • 值得注意:当 bind 返回的函数 使用new作为构造函数时,绑定的 this 值会失效,this指向实例对象,但传入的参数依然生效 (new调用的优先级 > bind调用)

Vue类

10.vue2和vue3的区别

  1. 双向数据绑定原理不同
  2. 是否支持碎片;
  3. API类型不同;
  4. 定义数据变量和方法不同;
  5. 生命周期钩子函数不同;
  6. 父子传参不同;
  7. 指令与插槽不同;
  8. main.js文件不同。

vue2:vue2不支持碎片。
vue3:vue3支持碎片(Fragments),就是说可以拥有多个根节点。
vue2:vue2的双向数据绑定是利用ES5的一个APIObject.definePropert() 对数据进行劫持,结合发布订阅模式的方式来实现的。
vue3:vue3中使用了ES6的Proxy API对数据代理。相比vue2.x,使用proxy的优势如下:
(1).defineProperty只能监听某个属性,不能对全对象监听
(2).可以省去for in,闭包等内容来提升效率(直接绑定整个对象即可)
(3).可以监听数组,不用再去单独的对数组做特异性操作vue3.x可以检测 到数组内部数据的变化。

11.route和router的区别

router为VueRouter的实例,相当于一个全局的路由器对象,里面含有很多属性和子对象,例如history对象。经常用的跳转链接就可以用this.$router.push,和router-link跳转一样。

route相当于当前路由对象。可以获取到一些参数,每一个路由都会有一个router对象,可以从里面获取name,path,params等。

12.vue路由两种模式的区别

hash模式
就是指 url 后面的 # 号以及后面的字符。每次刷新页面时是直接更改 # 后的东西。

hash 原理:灵活运用了 html的瞄点功能,改变 # 后的路径本质上是更换了当前页面的瞄点,所以不会刷新页面。通过监听浏览器的 onhashchange()事件变化,查找对应的路由规则。

由于 hash 值变化不会导致浏览器向服务器发出请求,而且 hash 改变会触发 hashchange事件(hashchange只能改变 # 后面的 url片段);虽然 hash路径出现在URL中,但是不会出现在 HTTP请求中,对后端完全没有影响,因此改变 hash值不会重新加载页面,基本都是使用 hash 来实现前端路由的。

history模式
history 原理: 利用 H5的 history中新增的两个API :pushState()和 replaceState()和一个事件onpopstate监听URL变化。

包含 back、forward、go方法;history 模式 URL就要和后端进行一致,所以要改为 history也需要后端的配合,否则会报错;history 每次刷新会重新向后端请求整个网址,也就是重新请求服务器。如果后端没有及时响应,就会报错404!。

13.keep-alive的生命周期

在组件切换过程中 把切换出去的组件保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性

被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated(组件激活时使用) 与 deactivated(组价离开时调用)

钩子的触发顺序created-> mounted-> activated,退出时触发deactivated。当再次进入(前进或者后退)时,只触发activated

keep-ailve用法:

  • 如果需要缓存整个项目,直接在app.vue中用keep-alive包裹router-view即可
  • 要缓存部分页面,需要在路由地址配置中,在meta属性中添加一个状态,在app.vue中判断一下包裹的router-view即可
  • 也可以使用exclude和include规定缓存哪个组件不缓存哪个组件

14.mvvm的优势:

  1. mvc和 mvvm 都是一种设计思想,主要就是 mvc 中 controller 演变成 mvvm 中的 viewModel。mvvm 主要解决了 mvc 中大量 dom 操作提高了页面 渲染性能和加载速度的问题
  2. mvvm 和 mvc 最大的区别就是:它实现了 view 和 Model 的自动同步,当 model 的属性改变时,我们不用在手动操作 dom 元素来改变 view 的显示,它会自动变化dom
  3. 整体看来,mvvm 比 mvc 精简很多,我们不用在用选择器来频繁的操作 dom

15.虚拟DOM的作用

  • 维护视图和状态的关系
  • 复杂视图情况下提升渲染性能
  • 虚拟 DOM 在更新真实 DOM 之前会通过 Diff 算法来对比两个新旧两个虚拟 DOM 树之间的差异,实现局部更新,最终把差异更新到真实 DOM

16.vue如何实现性能优化

1.代码模块化,常用的地方封装成单独的组件,在需要用到的地方引用,而不是写过多重复的代码,每一个组件都要明确含义,复用性越高越好,可配置型越强越好,包括咱们的css也可以通过less和sass的自定义css变量来减少重复代码。
2.Vue路由设置成懒加载,当首屏渲染的时候,能够加快渲染速度。
3.打包优化,修改vue.config.js中的配置项,把productionSourceMap设置为false,不然最终打包过后会生成一些map文件,如果不关掉,生成环境是可以通过map去查看源码的,并且可以开启gzip压缩,使打包过后体积变小。
4.减少图片使用,可以用一些css3的效果来代替图片效果,或者使用雪碧图来减少图片的体积。
5.按需引入,咱们使用的一些第三方库可以通过按需引入的方式加载。避免引入不需要使用的部分,无端增加项目体积
6.使用cdn的方式外部加载一些资源,比如vue-router、axios等Vue的周边插件,在webpack.config.js里面,externals里面设置一些不必要打包的外部引用模块

17.路由的守卫

对路由守卫的认识

路由守卫分为三种:全局导航守卫,路由守卫,组件内导航守卫。路由跳转本身是一个比较复杂的过程,但是可以通过导航守卫把这个过程进行细化,可以在每个细化的过程(钩子函数)中进行相应的操作。

路由导航守卫:导航守卫就是在路由跳转前后的一些钩子函数
路由拦截:在路由跳转到指定的路有前,可以手动让其跳转到其他的路由界面,并且也可以让跳转停止掉。

路由守卫的分类
1 全局守卫

  • 全局前置守卫:beforeEach 会多次触发,
  • 全局解析守卫:beforeResolve 解析守卫
  • 全局后置守卫:afterEach 把组件实例对象传入到组件beforeRouteEnter守卫的next回调中

2.路由守卫

  • beforeEnter 路由内的独享守卫

3.组件内的守卫

  • beforeRouteEnter 进入组件之前触发,只在进入组件时触发一次
  • beforeRouterUpdate 组件更新之前触发(动态参数变化查询字符串变化)进入组件后参数变化可多次触发
  • beforeRouterLeave 离开路由组件之前触发 只在离开组件时触发一次

18.Vue 组件中 data 为什么必须是函数?

因为一个组件是可以共享的,但他们的data是私有的,所以每个组件都要return一个新的data对象,返回一个唯一的对象,不要和其他组件共用一个对象。

如果是对象形式,所有的实例都将共享同一份data的数据对象,一旦某个组件修改了data,所有的实例都将受到影响,这是我们不想要的结果。

这样每一个实例的data属性都是独立的,不会相互影响了。所以,你现在知道为什么vue组件的data必须是函数了吧。这都是因为js本身的特性带来的,跟vue本身设计无关。

19.ES6的新特性有哪些?

 - 新增了块级作用域(let,const)
 - 提供了定义类的语法糖(class)
 - 新增了一种基本数据类型(Symbol)
 - 新增了变量的解构赋值
 - 函数参数允许设置默认值,引入了rest参数,新增了箭头函数。
 - 数组新增了一些API,如isArray / from / of 方法;数组实例新增了 entries(),keys() 和 values() 等方法。
 - 对象和数组新增了扩展运算符
 - ES6新增了模块化(import / export)
 - ES6新增了Set和Map数据结构。
 - ES6原生提供Proxy构造函数,用来生成Proxy实例
 - ES6新增了生成器(Generator)和遍历器(Iterator) 

说说解构赋值

 - 解构:分解数据结构。
 - 赋值:为变量赋值。
 - 解构赋值:从数组或者对象中提取值,按照对应的位置,对变量赋值(在数组解构中,只 要解构的目标可以遍历,就可以实现解构赋值)。

20.模块化开发的好处:

  1. 避免变量污染,命名冲突
  2. 提高代码复用率
  3. 提高了可维护性
  4. 方便依赖关系管理

21.JSON.stringify深拷贝的缺点

1、如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2、如果obj里面有RegExp,则打印出来是空对象
3、如果对象中有函数或者undefined,则会直接被丢掉
4、如果json里有对象是由构造函数生成的,则会丢掉对象的constructon
5、如果对象中存在循环引用的情况也无法正确实现深拷贝
6、如果对象中存在NAN,则序列化后会变成null

22.防抖和节流,有什么区别?

开发中,经常会遇到以下场景:监听鼠标移动 onmousemove,监听页面滚动 onscroll,监听大小变化 onresize,监听 input 输入,按钮的搜索、提交功能等。这些场景下,事件会被频繁触发,但我们并不想事件被频繁触发,这时就需要通过防抖和节流来限制频繁操作。

防抖和节流都是为了解决事件频繁触发的问题,但在实现原理上有些不同,具体实现原理看下文。

1.防抖(debounce):

防抖触发高频率事件时n秒后只会执行一次,如果n秒内再次触发,则会重新计算。

简单概括:每次触发时都会取消之前的延时调用。

应用场景:
1.scroll事件滚动
2.浏览器窗口的缩放resize事件
3.搜索框输入查询的时候
4.表单验证
5.按钮的提交事件

代码:

function debounce (fn, delay = 1000) {
  let time = null
  return function () {
    // 获取当前this
    let that = this
    // 判断是否已经存在,如果存在直接清除
    if (time) {
      clearTimeout(time)
    }
    time = setTimeout(() => {
      // 使fn 中this,执行当前调用者,并传入参数
      fn.apply(that, arguments)
    }, delay)
  }
}
// 测试demo
function logger(e){
	console.log('log -----')
}
btn.addEventListener('click',debounce(logger, 1000))

2.节流(thorttle):

高频事件触发,每次触发事件时设置一个延迟调用方法,并且取消之前延时调用的方法。

简单概括:每次触发事件时都会判断是否等待执行的延时函数。

代码:

function throttle (fn, delay = 1000) {
	let time = null;
	return function () {
		let that = this;
		// 如果已经存在定时器了,则 不做处理
		if (!time) {
			time = setTimeout(() => {
				fn.apply(that, arguments);
				// 完结时,将time改为null
				time = null;
			}, delay);
		}
	};
}

区别: 降低回调执行频率,节省计算资源。

  • 防抖和节流本质是不一样的。防抖是将多次执行变为最后一次执行,节流是将
    多次执行变成每隔一段事件执行
  • 函数防抖一定连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行
    一次。

23.JS中promise对象的作用与使用

Promise 它是一个ES6提出一个新语法,用来优化异步代码的写法。
promise的作用:ajax是异步请求,多层嵌套会造成回调地狱,promise模拟同步,将异步回调类似于同步来处理业务逻辑。

promise的then方法是异步方法,但会优先于定时器执行。

1、promise参数是一个函数,这个回调函数有两个形参(自定义,一般约定写为resolve,reject)
2、promise有三种状态pending (等待),pending(成功),rejected (失败),通过参数resolve,reject控制
3、当状态一旦发送改变 就不会重新改变
4、成功执行.then里的回调函数,失败执行.catch内的回调函数

特点:

将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
流程更加清晰,代码更加优雅。
Promise对象提供统一的接口,使得控制异步操作更加容易。

缺点:

无法取消Promise,一旦新建它就会立即执行,无法中途取消。
如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

24.async-await语法

async,await 是es7中新增的语法,用来进一步改进异步代码的写法,是promise升级版!

async

  • async函数返回一个 Promise 对象。
  • async函数内部return语句返回的值是Promise 对象的值
function f1 () {
  return 1
}
async function f2 () {
  return 1
}
async function f3 () {}
const r1 = f1()
const r2 = f2()
const r3 = f3()
console.log(r1) // 1
console.log(r2) // Promise, resolved(1)
console.log(r3) // Promise, resolved(undefined)
r2.then(res => { console.log(res) })
r3.then(res => { console.log(res) })

await 命令

  • await的外层函数必须有一个async.
  • 正常情况下,await命令后面是一个 Promise 对象,返回该promise的值。如果不是 Promise 对象,就直接返回对应的值。

async函数内部的执行流程

  1. 在执行async函数(设名为asyncF)时,进入函数内部:
  2. 按序执行同步代码
  3. 遇到await,跳出asyncF函数,
  4. 继续执行后续代码。
  5. 当await后的异步代码执行完成之后,接着执行asyncF中的后续代码。

25.宏任务和微任务

为什么任务要分为同步任务和异步任务

试想一下,如果js的任务都是同步的,那么遇到定时器、网络请求等这类型需要延时执行的任务会发生什么?

页面可能会瘫痪,需要暂停下来等待这些需要很长时间才能执行完毕的代码

所以,又引入了异步任务。

● 同步任务:同步任务不需要进行等待可立即看到执行结果,比如console
● 异步任务:异步任务需要等待一定的时候才能看到结果,比如setTimeout、网络请求

异步任务,又可以细分为宏任务和微任务。下面列举目前学过的宏任务和微任务。
2023前端面试题合集(附答案)持续更新中...
● 先执行同步代码
● 遇到宏任务,放入队列
● 遇到微任务,放入微任务队列
● 执行栈为空
○ 将微任务入栈执行
● 所有的微任务完成之后,取出宏任务队列来执行

宏任务和微任务谁先执行 答:宏任务

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。