前端面试题整理之40道面试题(三)

175 阅读20分钟

前端常见面试问题整理

学习过程中的简单记录,若问题敬请指教!文章持续更新中...

路过的朋友,可以点个赞,关注一下~~~

1. JS中的基本数据类型

  • 基本数据类型:string、boolean、number、null、undefined、symbol
  • 引用数据类型:object,array,function,date,RegExp

基本数据类型的数据直接存储在栈中,而引用数据类型的数据存储在堆中。

注意

顺便提一句,栈内存是自动分配内存的。而堆内存是动态分配内存的,不会自动释放。所以每次使用完对象的时候都要把它设置为 null,从而减少无用内存的消耗

2. 什么事作用域,什么事作用域链

  • 作用域:规定变量和函数的可使用范围

  • 作用域链:每个函数都有一个作用域链,查找变量或者函数时,需要从局部作用域到全局作用域依次查找,这些作用域的集合称作作用域链

3. 常见的宏任务和微任务都有那些?

  • 宏任务:script、setTimeOut、setInterval、setImmediate
  • 微任务:promise.then、process.nextTick、Object.observe

注意:Promise 是同步任务

4. 简单说一下防抖和节流

防抖:一定时间之后在执行。n秒之后再执行事件,若n秒内被重复触发,则重新计时。

节流:一定时间之内只执行一次。n秒内只运行一次,若在n秒内重复触发,只有一次有效。

使用场景

防抖:一般用在按钮点击中,比如表单提交;再有就是联想搜索中也比较常见

节流:一般用在一些 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率

5. typescript 中的infer关键字

infer主要用于推断某个复杂类型,常与 extends 和三元运算符组合使用,用于推断某个复杂类型的部分,简单的说,就是用来推导泛型参数。

使用规则

type ParamsArray<T> = T extends Array<infer P> ? P : T;
  1. inter 只能出现在 extends 关键字的右侧;
  2. inter P 可以理解成数学上的未知数 x;
  3. 其中 extends 关键字的作用,是用来判断 右边的类型 是否兼容 左边的泛型 T,如果兼容则返回 ? 后面的内容,否则返回 : 后面的内容。

6. typescript中的extends关键字

extends关键字在ts中在不同场景代表的含义不一样:

  • 表示继承
  • 表示约束
  • 表示分配(条件类型)

继承

class Animal {
    say() {
      console.log('say');
    }
  }

 class Dog extends Animal {}
 const dog = new Dog();
 dog.say();

泛型约束

type c = <T extends { name: string }>(arg: T) => any


function fn(cb: C) {
    cb({ name: '回调' });
  }

fn((arg) => {
    console.log(arg.name);	// 回调
  });

条件类型 (需要特别理解 extend在三元运算符中的含义)

type Human = {
    name: string;
  };

 type Duck = {
    name: string;
  };

 type Bool = Duck extends Human ? 'yes' : 'no'; 	// yes

/**
* 解释:Bool 的类型是yes,这是因为Human 和 Duck 的类型往前相同,或者说Human类型的一切约束条件,Duck都*		 具备;需要理解的是,这个A extends B 是指类型A可以分配给类型B,而不是说类型A是类型B的子集。
*/

可参考文章地址: juejin.cn/post/722136…

7. voidnever 区别。

void:表示空值,null 或者 undefined。

never:表示空值,不能为任何值。

8.async/awaitPromise 什么区别?

  • 都是解决异步编程的一种方式

  • Promise的出现解决了传统回调函数导致的“地狱回调”问题,但它的语法导致了它向纵向发展行成了一个回调链,遇到复杂的业务场景,显然不美观。

  • async/await代码简洁,使得异步代码看起来像同步代码,await的本质是可以提供等同于”同步效果“的等待异步返回能力的语法糖,只有这一句代码执行完,才会执行下一句。

  • async/awaitPromise一样,是非阻塞的。

  • async/await是基于Promise实现的,可以说是改良版的Promise,它不能用于普通的回调函数

9. 浏览器输入 URL 到渲染页面的过程。

  1. dns解析,获取对应的ip地址
  2. 根据IP地址,访问服务器
  3. 建立TCP连接(三次握手)
  4. 发送请求
  5. 服务器给出响应
  6. 浏览器得到响应的资源,并进行解析和渲染
  7. 断开连接(四次挥手)

10. 三次握手

三次握手 : 建立TCP连接的时候,客户端与服务器端出现的三次握手

三次握手的步骤:

  1. 客户端向服务器发起连接请求
  2. 服务器确认收到连接请求,并且向客户端发送连接请求
  3. 客户端确认收到服务器发送的连接请求

11. 四次挥手

四次挥手 : 是浏览器与客户端断开连接时发生的(四次)

四次挥手的步骤:

  1. 甲方发出断开连接的请求
  2. 乙方确认收到
  3. 乙方发出断开连接的请求
  4. 甲方确认收到

##12. 为什么连接是三次握手,而断开时四次挥手?

  1. 建立连接的时候没有数据的传输
  2. 断开连接的时候可能存在数据的传输

当甲方发出断开连接请求后,已方确认收到后,可能已方还有数据没有给甲方,所以已方不能立即断开连接,只能等到已方把所有的事件处理完后,才能给甲方发一个可以断开连接的请求。

13. ES6 新增的数据类型

基础类型:symbol

其他类型:Set类型Map类型weakSet类型WeakMap类型TypedArray类型

14. 事件循环机制

事件循环机制,是javaScript或Node为解决单线程代码执行不阻塞主进程一种机制,也就是我们所说的异步原理。

15. webpack常见的性能优化

webpack的性能优化,主要体现在三个方面:

  • 构建性能:指开发阶段的构建性能。当构建性能越高,开发效率越高。
  • 传输性能:重点考虑网络中的总传输量、JS文件数量以及浏览器缓存。
  • 运行性能:指JS代码在浏览器端的运行速度

构建性能

  1. 减少模块解析
  2. 优化loader性能,限制loader的应用范围
  3. 开启多线程打包
  4. 热替换 (Hot Module Replacement)

传输性能

  1. 分包(手动分包、自动分包)
  2. 体积压缩

运行性能

主要是书写代码质量的高低。可以参考常见的设计模式、代码规范、最佳实践等。

16. Vue2.0 与 Vue3.0 有那些区别?

  • 更小

    vue2采用的是面向对象编程,vue3采用的是函数式编程。

  • 更快

    vue3修改了细腻dom算法,只针对变化层进行diff,而vue2是对所有的dom进行diff。

  • vue3 加强了typescript的支持

  • 数据双向绑定

    vue2 使用的是definedProperty,vue3使用的是proxy

  • 多根节点

    vue3是允许有多个根节点的,即template里面可以同时写多个节点并列,

    而vue2只能有一个根节点,在根节点里面再去添加其他节点。

  • vue3使用的是组合式api,vue2使用的是选择式api

  • vue2是使用this,vue3取消了this

  • 生命周期不一样;vue3增加setup

...

17. webpackvitevite为什么快?

webpack是先打包再启动开发服务器;

vite是直接启动开发服务器,然后按需编译依赖文件。

18. 上线之后 如何提示用户刷新当前页面

目前有三种解决方案(大致概念)

  1. WebSoket套接字,需要后端开对应的服务进行连接(不推荐)
  2. 前端轮询获取。定时获取对应的版本信息,需要后端参与!
  3. 前端设置版本,生成版本号文件,触发条件获取版本号文件资源,进行比对。(触发条件一般为请求或者路由守卫)。特别注意本方法需要设置服务器禁止缓存。纯前端。

19. UDP协议有什么优点?

  • 传输数据之前通信双方不需要建立连接
  • 传输数据不需要维护连接状态,包括收发状态等。
  • UDP数据报首部很短,只有8字节,相对于TCP的20字节首部的开销小很多
  • 吞吐量不受流量控制算法的调节,只受应用软件生成数据的速率、传输带宽、信源和信宿主机性能的限制。

20. 0.1 + 0.2 是否等于 0.3,如何解决?

因为在0.1 + 0.2实际上计算的是这两个数字在计算机里所存储的二进制

0.1 和 0.2 在进行二进制转换的时候会出现无限循环的情况。

解决方案

可将其转换为整数后进行计算,运算之后再转为小数

21.generator 函数和 async 函数有什么区别?

  • generator函数是ES2015提供的异步解决方案
  • async函数是ES2017提供的异步函数语法,是generator的语法糖。
  • 执行方式不同,generator执行需要使用执行器(next()等方法);async函数自带执行器,与普通函数的执行一样。
  • async 的语法语义更加清楚,async 表示异步,await 表示等待;而 Generator 函数的(*)号和 yield 的语义就没那么直接了;

22. WebPack如何实现热更新,与vite的热更新有什么区别?

webpack的热更新是使用HRM(热模块替换)技术实现的,在代码变化时,通过patch的方式更新对应的模板,而vite的热更新则是通过webSocket和浏览器原生的API实现的,它能够更快的更新代码,并且支持更多的语言和框架。

23. 怎么看 Vue 和 React

vue是渐进式javascript框架。j渐进式框架自底向上增量开发的设计是vue开发的两个概念。

react是facebook开发的用于构建用户界面的javaScript库。react主张的是函数式编程的理念,实现了q界面的高性能高效率开发,react很擅长处理组件化的页面。

相同点

  • 数据驱动视图

  • 组件化

  • 都使用了Virtual Dom + Diff算法

不同点

  • 核心思想不同

    • vue定位是低门槛,快速开发。主要特点:灵活易用的渐进式框架,进行数据拦截代理,对侦测数据的变化更敏感、更精确。

    • react定位是提出UI开发的新思路,用更好的方式去颠覆前端的开发模式。主要特点:推崇函数式编程(纯组件),数据不可变以及单向数据流,当然需要双向的地方也可以手动实现,比如借助onChangesetState来实现。

  • 响应式原理不同

    • vue收集依赖,自动优化,数据可变。vue递归监听data的所有属性,直接修改。当数据改变时,自动找到引用组件重新渲染。
    • react基于状态机,手动优化,数据不可变。需要setState驱动新的state替换老的state。当数据改变时,以组件为根目录,默认全部渲染,所以React中会需要shouldComponentUpdate这个生命周期函数方法来控制。
  • 组件写法差异

    • vue推荐的是template的单文件组件格式,即html,js,css写在同一个文件中(支持JSX写法)
    • react推荐的是jsx + inline style,也就是把HTML和CSS全部写进去javaScript中,即all in js
  • diff算法

    • vue对比节点,当节点元素相同,但是className不同,认为是不同类型的元素,删除重建。
    • react则认为是同类型节点,只修改节点属性。
  • 渲染过程

    • Vue可以更快地计算出Virtual DOM的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。

    • React在应用的状态被改变时,全部子组件都会重新渲染。通过shouldComponentUpdate这个生命周期方法可以进行控制,但Vue将此视为默认的优化。

24. 如何选择react和Vue

在以下情况选择vue

  1. 最新文档和更简单的语法。
  2. 更小,更快,更灵活。
  3. 需要丰富的HTML模板,易于开发。

在以下的情况选择react

  1. 需要构建移动应用程序。
  2. 需要构建大型应用程序。
  3. 轻量级,易于版本迁移。

25. any、unknown、never、void区别

定义

  • any:用于描述任意类型的变量,不做任何约束,编译时会跳过对其的类型检查
  • unknown:表示未知类型,即表示写代码的时候还不知道具体会是怎么样的数据类型
  • never:用在不存在值的类型,常用于表示永不能执行到终点的函数返回值,例如抛出的异常或者函数中执行无限循环的代码(死循环)的函数返回值类型。
  • void:表示无任何类型,没有类型,例如没有返回值的函数的返回值类型

any 与 unknown 的区别

unknown 与 any 类似,但使用前必须进行断言或守卫

never 与 void 的区别

用于函数时,never表示函数用于执行不到返回值那一步(抛出异常或死循环)的返回值类型,即永不存在的值的类型,而void则表示没有返回值,不返回或返回undefined

如何使用

  • 能不用any就不用any
  • 声明时如果不确定具体的类型,则可以使用unknow代替,在使用时用类型断言或类型守卫进行类型收缩
  • never常用于构造条件类型来组合出更灵活的类型定义
  • void 常用于表示函数没有返回值

26. async 和 defer 区别,使用 async 需要注意什么?

相同点

  • 都是异步加载script的解决方案。

不同点

  • defer是“渲染完再执行”

  • async是“下载完就执行”。

  • 如有多个defer脚本,会按照它们在页面出现的顺序加载,

  • 如有多个async脚本是不能保证加载顺序的。

特别注意:async的无序性

扩展

webpack如何解决异步相互依赖的问题?

答:webpack是通过require.Onload方法,确保所有的chunk已经加载完,再去执行当前模块。require.Onload方法就是把每个模块依赖的chunk和回调都保存起来,并检查当前所有的模块,如果发现某个模块依赖的chunk都已经加载完,就执行其回调。每当某个chunk加载完,都会调用require.Onload,以便依赖她的模块马上执行。

27. Vue 的 nextTick 是用来做什么的?它的原理是什么?

作用

  • Vue中使用nextTick是为了获取更新之后的DOM

原理

  • nextTick本质上是对javaScript执行的事件循环机制的一种应用。简单来说就是宏任务与微任务的执行。

  • 常见的宏任务有 script, setTimeout, setInterval, setImmediate, I/O, UI rendering

  • 常见的微任务有 process.nextTick(Nodejs),Promise.then(), MutationObserver;

javaScript的执行顺序:同步 -> 异步 -> 微任务 -> 宏任务

28. Proxy 和 Object.defineProperty 区别

相同点

  • 通过数据劫持,实现双向数据绑定

不同点

Object.defineProperty()的问题有三个:

  • 不能监听数组的变化
  • 必须遍历对象的每个属性
  • 必须深层遍历嵌套对象

Proxy是对Object.defineProperty缺点的弥补。

  • 数组监听。
  • 对象整体监听,不需要遍历每一个属性。

29. VUE3.0 做了那些方面的优化?

  • **使用组合式API:**可以按照功能将代码分割开,方便维护,也方便复用。
  • **使用代理proxy:**可以直接监听数组,和整个对象,无论多深的属性都可以监听到。
  • **体积更小:**使用tree-shaking使得打包体积更小。
  • **diff算法优化:**在diff算法中增加了静态标记,作用是为会发生变化的地方添加一个flag标记,下次发生变化的时候直接找到该地方进行比较。
  • **静态提升:**对没有参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用,这样可以避免了重复的创建节点。
  • **事件监听缓存:**将不需要重复创建的方法单独提出,进行缓存,避免重复加载。
  • **SSR优化:**如果增加的静态内容过多,就会直接使用innerHTML的方法插入,而不会一个一个的创建节点。

30. Vue2 里面 data 为什么是一个 function ?

数据隔离,避免不同组件内数据相互影响。

31. Vue2属性发生变化,视图会同步渲染吗?

不会立即同步渲染。

vue实现响应式并不是数据发生变化之后DOM立即变化,而是按照一定的策略进行DOM更新。VUE在更DOM时是异步执行的,只要监听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。

如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环tick中,Vue 刷新队列并执行实际(已去重的)工作。

32. Vue 相关的性能优化

编码阶段

  • 路由懒加载
  • 第三方插件按需引入
  • 图片懒加载
  • v-if 和 v-show 区分使用场景
  • computed 和 watch 区分使用场景
  • 防抖、节流应用
  • v-for循环添加key,且避免同时使用v-if
  • 使用keep-alive对组件进行缓存。

打包优化

  • 代码、图片压缩
  • 提取公共代码(避免相同资源被重复加载)

用户体验

  • 骨架屏

33. 介绍一下 Vue 的主要原理

主要实现数据双向绑定。采用数据劫持结合发布者-订阅者模式的模式,通过Object.defineProperty()来劫持各个属性的setter与getter,在数据变动时发布消息给订阅者,触发相应监听回调。来实现数据的双向绑定

34. keepAlive是什么?

keep-alive是用来缓存组件的;<keep-alive>Vue中内置的一个抽象组件,它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。

如果一个组件既在 exclude 又在 include 会被缓存吗?

不会缓存。include和exclude同时出现 exclude的优先级高于include

35. 怎么把 Vue2 项目升级到 Vue3

  • 第一步:创建一个新的vue3 + TS项目,安装完成基础的依赖
  • 第二步:整体项目移植,目前vue3也是兼容了Options写法,这样代码结构可以暂时不做改变,后期逐步改变。
  • 第三步:把vue3中已经不再支持的API和语法进行修改替换,包括过滤器filter,v-model的用法也改变了等。
  • 第四部:把项目中使用到的第三方插件和UI框架替换到vue3版本,对用的用法可能也要进行修改。
  • 第五步:确保项目代码编译无误之后,需要检查代码中的业务是否正确,避免对公司项目迁移造成稳定性破坏。
  • 第六步:使用 TypeScript 重构 JS 代码,TypeScript 比 JavaScript 多了静态类型检查,也增加了一些新的语法,是给项目锦上添花。但是这一步会比较耗时(因为相当于修改把JS代码都要过一遍),但是项目中可以同时存在JS 和 TS,所以可以逐步替换。

36. JS中如何去重

  • 数组去重:使用Set
  • 对象去重:使用 filterr + Map(方法不唯一)

对象去重示例

使用filter和Map(强烈推荐)

function uniqueFunc(arr, uniId){
  const res = new Map();
  return arr.filter((item) => !res.has(item[uniId]) && res.set(item[uniId], 1));
}

使用reduce(一般推荐)


function uniqueFunc2(arr, uniId){
  let hash = {}
  return arr.reduce((accum,item) => {
    hash[item[uniId]] ? '' : hash[item[uniId]] = true && accum.push(item)
    return accum
  },[])

37. flex:1 的含义。

flex:1即为flex-grow:1,经常用作自适应布局,将父容器的display:flex,侧边栏大小固定后,将内容区flex:1,内容区则会自动放大占满剩余空间。

原理 flex属性 是 flex-growflex-shrinkflex-basis三个属性的缩写。

  • flex-grow:定义项目的的放大比例
  • ``flex-shrink:`定义项目的缩小比例
  • flex-basis:定义在分配多余空间之前,项目占据的主轴空间,浏览器根据此属性计算主轴是否有多余空间。相当于设置初始值

38. React hooks 和 Vue3 的 compose api 有什么区别?

  1. composition api 中的 setup只会被调用一次; react hooks 中的函数会被多次调用;

  2. react hooks 需要useMemo useCallback ;

  3. composition api不需要保证顺序, react hooks 要保证 hooks 顺序 一致

  4. ref toRef toRefs reactive 比起 useState 太繁琐了...

两者的注意事项

 react hooks注意事项:

  1. useState初始化, 只有第一次有效

  2. useEffect 内部不能修改state

  3. useEffect 可能出现死循环 ( 依赖注入是引用类型 就会这样 )

 composition api注意事项:

  1. 不建议和options api共用

  2. 小型项目,业务逻辑简单可以用 options api, 没必要 composition api

  3. composition api 属于高阶技巧了, 抽离函数 实现组件逻辑复用

39. link和script标签的加载过程中会对DOM树构建有什么影响?

<script>标签会阻塞DOM的解析和渲染;

带src属性的<script>标签会触发页面paint,渲染此<script>标签之前的元素,但也有一定的条件:

  • <script>标签是在<body>中的,<head>中的不会触发paint;
  • <script>标签之前的<link>标签需加载完毕。

inline的<script>标签不会触发页面paint,页面必须等到脚本执行完毕,且DOM Tree和CSSOM Tree解析完毕后才会渲染;

<link>标签不会阻塞DOM的解析;

<link>标签会阻塞DOM的渲染;

<link>标签同时还会阻塞其之后的<script>标签的执行。

40. 如何优化一个网站的性能

  • 网站性能优化“六步法则”:一、网页内容优化;二、服务器优化;三、Cookies优化;四、 CSS优化;五、JS优化;六、图片优化。

(1)网页内容优化

1、网站尽量减少http的请求次数;

2、网站尽量减少DNS的查询次数;

3、网站尽量避免页面跳转、重定向;

4、网站尽量缓存AJax;

5、网站尽量减少DOM元素的数量;

6、网站尽量根据域名划分页面的内容;

7、网站尽量减少iframe的使用;

8、网站尽量避免404错误;

9、网站的延迟加载;

10、网站的提前加载。

(2)服务器优化

1、网站使用CDN加速优化;

2、网站添加Expires或者Cache-Control报文头优化;

3、网站采用Gzip压缩优化;

4、网站配置Etags优化;

5、网站尽早flush刷新输出缓冲;

6、网站使用GET来完成AJAX请求;

7、网站避免空的图像来源,配置图片src属性。

(3)Cookies优化

1、网站减少Cookies的大小;

2、网站内容页面选择无Cookies域名。

(4)CSS优化

1、网站把样式表置于顶部;

2、网站避免使用CSS表达式(Expression);

3、网站用代替@import;

4、网站避免使用滤镜filter。

(5)JS优化

1、网站把JS脚本置于页面底部;

2、网站使用外部JavaScript和CSS;

3、网站削减JavaScript和CSS;

4、网站剔除重复的JS脚本;

5、网站减少DOM访问;

6、网站开发智能事件处理程序。

(6)图片优化

1、网站优化CSS Spirite;

2、网站不要在HTML中缩放图像;

3、网站中的favicon.ico要小而且可缓存。

后记

本文纯仅属于个人的一些简单的见解,比较浅显,若有不妥之处还请不吝赐教!!!(☆_☆)

如果本文对你有所帮助,欢迎点赞!!!

o( ̄▽ ̄)d