2024最新前端面试题

一、css

1、说一下css的盒模型

2、css选择器的优先级

3、隐藏元素的方法有哪些

febfb4b196f946f7ab9ee738575f94ea.jpg

4、px和rem的区别是什么

8c36d62412f54e0488809d40234b0466.jpgf7fc04f3edd84144b4b93b1ed13712fb.jpg 

5、重绘和重排(回流)的区别

浏览器渲染机制

f65a33b1194a4fb58aa6e87a14c90e28.jpg

6、让一个元素水平垂直居中的方式1adb3754b05341d58a5cf3b952f5d66f.jpg 定位+margin

c8a5526f57934db9a8e2802502614942.jpg

定位+transform

4363101856134cb298fd2fb273524ee0.jpg

flex布局

4f45226c4b4048dcb81f115402fd68c9.jpg

7、css的哪些属性可以继承?哪些不可以继承?

987ba9d5233f4c4db20c4a22809eeceb.jpg

  • 能继承的属性

  1. 字体系列属性:font、font-family、font-weight、font-size、font-style;
  2. 文本系列属性: 2.1)内联元素:color、line-height、word-spacing、letter-spacing、 text-transform; 2.2)块级元素:text-indent、text-align;
  3. 元素可见性:visibility
  4. 表格布局属性:caption-side、border-collapse、border-spacing、empty-cells、 table-layout;
  5. 列表布局属性:list-style
  • 不能继承的属性

  1. display:规定元素应该生成的框的类型;
  2. 文本属性:vertical-align、text-decoration;
  3. 盒子模型的属性:width、height、margin 、border、padding;
  4. 背景属性:background、background-color、background-image;
  5. 定位属性:float、clear、position、top、right、bottom、left、min-width、 min-height、max-width、max-height、overflow、clip;

8、有没有用过预处理器

b76fd9adc21d4f24be6cb57e3dfbc26d.jpg

二、js

1、js由哪三部分组成

2、js有哪些内置对象

3、操作数组的方法有哪些

83c49820a8b14b388555b5267dd22290.jpg 4、js对数据类的检测方式有哪些5a3eeb888a8245cfb485f69a4383c77b.jpg

 0e504993b4d347a38faf2e0c37c9d0f5.jpg

694ba6b646e64724a1a17eb96b6b2f75.jpg

判断数组的几种方法

数组是属于Object类型的,也就是引用类型,所以不能使用 typeof 来判断其具体类型。下面这些方法是判断数组的几种方法

1、instanceof运算符

主要是判断某个实例(arr)是否属于某个对象。

let arr = [1,2,3];

console.log(arr instanceof Array); //true

2、constructor

判断实例(arr)的构造函数是否等于某个对象。

let arr = [1,2,3];

console.log(arr.constructor == Array); //true

3、isArray

ES5新增数组方法,判断数组是不是数组。

let arr = [1,2,3];

console.log(Array.isArray(arr)); //true

4、Object.getPrototypeOf()

Object.getPrototypeOf()方法返回指定对象的原型,然后和Array的原型对比。

let arr = [1,2,3];

console.log(Object.getPrototypeOf(arr) == Array.prototype); //true

5、Array原型链上的isPrototypeOf

Array.prototype表示Array的构造函数的原型;

isPrototypeOf()方法可以判断一个对象是否存在于另一个对象的原型链上。

let arr = [1,2,3];

console.log(Array.prototype.isPrototypeOf(arr)); //true

6、Object.prototype.toString.call()

把对象转化成字符串和一个已知的对象进行对比。

let arr = [1,2,3];

console.log(Object.prototype.toString.cal(arr) == '[object Array]'); //true

5、说一下闭包、闭包有什么特点

6、前端的内存泄漏怎么理解

4480e08d161f4d87beb6f7c174c25c73.jpg

7、事件委托是什么

8、基本数据类型和引用数据类型的区别

4d187d0eff954dc5937ba9cb7848e23a.jpg

9、说一下原型链

b2184801118347949afbb2b2c7c7237b.png

10、new操作符具体做了什么

2d1105403a9e42c39d790801e9bc5b03.png

0b2ba7df2d654b749869d978d81d9e63.png

11、js是如何实现继承的

66fcddeaa4124eb38e303ec7f6265dcc.png

7137e2d9f97240ebb6774eb5e2d36de7.png

ca7e52bb350740599f3747b9088ffa07.png

22de7f6074d2400b8c6b8bb78057976f.png

0ccf9a0f849c46a2a31c5b9dc63fc152.png917fb21275554d8fa36511d01cd976c2.pnga38c81bdd2c842ceb7bd879987fa6afa.png

12、js的设计原理是什么

887a541ee55141a2be7c40914871cb5a.png

13、js中关于this指向的问题

8c9b6e27be24488a88bec001afa33fa5.png

JavaScript 的 this 原理 - 阮一峰的网络日志

14、script标签里的async和defer有什么区别

5c0ecd43092248929ccb411d108c3ecf.png

15、setTimeout最小执行时间

492ee0ba78a0489ea3d0508556acc470.png

16、ES6和ES5有什么区别

883a7a4259534f68b98d7b4d9c7e63b9.png

17、ES6的新特性有哪些

5623ce0859d84427a1135b95b0ef91af.png9b1124adc4b94b56a70ef8d257d750e3.png

884f2cdb8d7b4e8f9783958c0e3429e7.png32949a97009d4eef9cfb12a46f2e04e2.png16b336174dbf4ab388e5c583fe03ea61.png

18、call、apply、bind三者的区别

9e258fac5a384e3690f791df18cc729b.png

19、用递归的时候有没有遇到什么问题

20f1dba238c148c2abff6d41b3579fe5.jpg

fbd2c1941fb24509bbee9776eb3e18f7.jpg

26369dd98b0d4395a190d6926bc7a89a.jpg

20、*set和map的区别

Set和Map的主要区别在于数据结构、操作方式、应用场景等。

  • 数据结构。Set是一种存储唯一值的集合,每个元素只出现一次,不重复;Map是一种键值对集合,每个键与一个值相关联,键是唯一的,值可以重复。
  • 操作方式。Set提供迭代器接口,可以使用for…of循环或forEach方法进行迭代;Map也提供迭代器接口,可以使用for…of循环或forEach方法进行迭代。
  • 应用场景。Set适用于需要存储一组唯一值且不关心顺序的场景,如去重、判断元素是否存在等;Map适用于需要将值与特定键关联的场景,如存储键值对配置信息、构建字典、缓存等。
  • 性能。对于大型数据集合,Set的查询性能较好;Map的查询性能取决于键值对的数量。
  • 修改限制。Set的迭代器是const的,不允许修改元素的值;Map允许修改value,但不允许修改key。

21、说一下事件循环

234d7be85c2046d3a5e89d5348dd512d.jpg

JavaScript事件循环机制是一种用于处理异步任务的系统,它确保代码能够顺序执行,同时处理异步操作如定时器和事件监听。事件循环机制主要由以下三个部分组成:

  1. 调用栈:用于存储主线程代码的执行顺序。
  2. 任务队列:用于存储异步任务(如定时器或事件监听器)的回调函数。
  3. 事件循环线程:负责循环监听任务队列中是否有任务需要执行。如果有,则将其添加到调用栈中执行。

当代码遇到一个异步任务时,该任务的回调函数会被添加到任务队列中。当主线程中的代码执行完毕,事件循环线程会从任务队列中取出一个任务并添加到调用栈中执行。这个过程会一直重复,直到任务队列中没有任何任务为止。需要注意的是,任务队列中的任务是按照先进先出的原则执行的。

在浏览器环境中,主线程运行在浏览器渲染进程中,而渲染进程中还有一个I/O线程和网络进程,它们会产生新的任务。这些任务会被I/O线程放入消息队列中,由渲染进程维护。主线程的事件循环线程会不断地从消息队列中取出新的任务去执行,以处理由I/O线程和其他进程产生的异步任务。

总结来说,JavaScript的事件循环机制通过一个循环系统来处理主线程内部产生的任务以及由其他进程产生的任务,确保代码能够顺序执行并处理异步操作。

*宏任务和微任务的区别

宏任务和微任务的区别主要在于它们执行的顺序优先级12345

宏任务通常包括整体代码、定时器(例如`setTimeout`和`setInterval`)、网络请求、用户交互事件(例如点击和滚动)以及浏览器渲染任务(例如页面重绘或重新布局)。这些任务会在事件循环的每个阶段执行一次,而微任务则包括Promise的`resolve`或`reject`回调、MutationObserver回调以及某些特定环境(如Node.js的`process.nextTick`)。微任务通常在当前宏任务执行完毕后立即执行,不会加入到事件队列中,因此在下一个宏任务开始之前执行。这意味着微任务具有更高的优先级,可以在用户交互或渲染操作之前得到及时处理,例如更新DOM或处理异步操作的结果。

总结来说,宏任务是事件循环中的较大任务,而微任务是较小的任务,它们的执行顺序不同,微任务的优先级高于宏任务

22、ajax是什么?怎么实现的

39d81ea633a940818a043aaa51979e6a.jpg

23

54086421d9f24b52b716676e3e633391.jpg

 24

af7d7b0d8d74459c8b06978a111bf8d8.jpg

 25

32ca7edc4e174f91b593a7447963c21b.jpg

26

cff2ef907fe949d489c43c3c9eb9a381.jpg

 27

a93943a4d28148a481e1ab603e6494cc.jpg

 28

059844ed98d944f88efbd65eeb520f7a.jpg

 29

05413c5b44a3447ab78c3e697f4e3139.jpg

 30

9aa4fc8042134d7a8de31278a8c6ceee.jpg

三、vue相关

分享当下较新的30道Vue面试题!-CSDN博客

1、Vue的事件绑定原理

  • 原生DOM 的绑定:Vue在创建真实DOM时会调用 createElm ,默认会调用 invokeCreateHooks 。会遍历当前平台下相对的属性处理代码,其中就有 updateDOMListeners 方法,内部会传入 add() 方法

  • 组件绑定事件,原生事件,自定义事件;组件绑定之间是通过Vue中自定义的 $on 方法实现的。(可以理解为:组件的 nativeOnOn 等价于 普通元素on 组件的on会单独处理)

2、vue是如何实现响应式数据的呢?(响应式数据原理)❗

Vue2 Object.defineProperty 重新定义 data 中所有的属性, Object.defineProperty 可以使数据的获取与设置增加一个拦截的功能,拦截属性的获取,进行依赖收集。拦截属性的更新操作,进行通知。

具体的过程:首先Vue使用 initData 初始化用户传入的参数,然后使用 new Observer 对数据进行观测,如果数据是一个对象类型就会调用 this.walk(value) 对对象进行处理,内部使用 defineeReactive 循环对象属性定义响应式变化,核心就是使用 Object.defineProperty 重新定义数据。

42c09e1258e6723ddc2e9eafc4f8a8c5.jpeg

🌸刚刚如果你说了对象的检测,然后又没说清楚数组的处理的话,我就会问下面这个问题

那vue中是如何检测数组变化的呢?

数组就是使用 object.defineProperty 重新定义数组的每一项,那能引起数组变化的方法我们都是知道的,pop 、push 、shift 、unshift 、splice 、sort 、reverse 这七种,只要这些方法执行改了数组内容,我就更新内容就好了,是不是很好理解。

  1. 是用来函数劫持的方式,重写了数组方法,具体呢就是更改了数组的原型,更改成自己的,用户调数组的一些方法的时候,走的就是自己的方法,然后通知视图去更新。

  2. 数组里每一项可能是对象,那么我就是会对数组的每一项进行观测,(且只有数组里的对象才能进行观测,观测过的也不会进行观测)

vue3:改用 proxy ,可直接监听对象数组的变化。

3、为什么Vue采用异步渲染呢?

Vue 是组件级更新,如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染,所以为了性能,Vue 会在本轮数据更新后,在异步更新视图。核心思想nextTick 。

dep.notify() 通知 watcher进行更新,subs[i].update 依次调用 watcher 的 update ,queueWatcher 将watcher 去重放入队列, nextTick( flushSchedulerQueue )在下一tick中刷新watcher队列(异步)。

了解nextTick吗?

异步方法,异步渲染最后一步,与JS事件循环联系紧密。主要使用了宏任务微任务(setTimeoutpromise那些),定义了一个异步方法,多次调用nextTick会将方法存入队列,通过异步方法清空当前队列。

4、vue组件间传值的多种方式

Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。

(1)props / $emit 适用 父子组件通信

这种方法是 Vue 组件的基础,相信大部分同学耳闻能详,所以此处就不举例展开介绍。

(2)ref 与 $parent / $children 适用 父子组件通信

ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例

$parent / $children:访问父 / 子实例

(3)EventBus ($emit / $on) 适用于 父子、隔代、兄弟组件通信

这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。

(4)$attrs/$listeners 适用于 隔代组件通信

$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。

$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件

(5)provide / inject 适用于 隔代组件通信

祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。

(6)Vuex 适用于 父子、隔代、兄弟组件通信

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。

Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

5、diff算法

面试进阶核心,10张图,带你吃透 “Diff” 算法核心原理(图文详解)_diff算法原理-CSDN博客

时间复杂度: 个树的完全diff 算法是一个时间复杂度为 O(n*3) ,vue进行优化转化成 O(n) 。

理解:

  • 最小量更新,key 很重要。这个可以是这个节点的唯一标识,告诉 diff 算法,在更改前后它们是同一个DOM节点

    • 扩展 v-for 为什么要有 key ,没有 key 会暴力复用,举例子的话随便说一个比如移动节点或者增加节点(修改DOM),加 key 只会移动减少操作DOM。

  • 只有是同一个虚拟节点才会进行精细化比较,否则就是暴力删除旧的,插入新的。

  • 只进行同层比较,不会进行跨层比较。

diff算法的优化策略:四种命中查找,四个指针

  1. 旧前与新前(先比开头,后插入和删除节点的这种情况)

  2. 旧后与新后(比结尾,前插入或删除的情况)

  3. 旧前与新后(头与尾比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)

  4. 旧后与新前(尾与头比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)

6、Vue性能优化

编码优化

  • 事件代理

  • keep-alive

  • 拆分组件

  • key 保证唯一性

  • 路由懒加载、异步组件

  • 防抖节流

Vue加载性能优化

  • 第三方模块按需导入( babel-plugin-component )

  • 图片懒加载

用户体验

  • app-skeleton 骨架屏

  • shellap p壳

  • pwa

SEO优化

  • 预渲染

四、react相关

1、*常用的hooks

useState、useEffect、useMemo、useCallback、useSelector、useLocation、useParams、useSearchParams、useReducer、useDispatch、useContext、useRef、useLayoutEffect。。。

2、*useCallback和useMemo区别

useCallbackuseMemo的区别主要在于它们缓存的内容和优化场景不同。以下是详细介绍:

  • 使用场景不同。useCallback主要用于优化传递给子组件的回调函数,避免不必要的重新创建和渲染;而useMemo主要用于优化计算操作,避免不必要的重复计算。
  • 缓存内容不同。useCallback缓存的是函数本身;而useMemo缓存的是回调函数中return回来的值。
  • 依赖性不同。两者都依赖于依赖项的变化来触发重新计算或缓存的更新,但useCallback更侧重于函数本身是否需要更新,而不是依赖项的直接变化。

此外,使用useCallback时,建议与React.memo一起使用,以确保最佳性能。

3、*不能在循环中使用hooks的原因

为什么hooks不能在循环、条件或嵌套函数中调用_hooks 为什么不能放在条件判断里-CSDN博客
Hooks为什么不能写在条件语句或循环语句里_hooks为什么不能写在条件语句中-CSDN博客

为什么 Hooks 必须写在函数的顶部呢?

原因是因为 React 需要保证在每次组件渲染时,Hooks 的执行顺序都是一致的。

如果将 Hooks 写在循环或条件语句中,那么每次渲染时,Hooks 的执行顺序都可能会发生变化。例如,当循环重新运行时,React 将无法确定哪个 Hook 应该先运行,哪个应该后运行。这样会导致组件状态发生不可预测的变化,从而影响组件的正确性。

PS:为了保证执行顺序及代码易读性

4、*循环中没有key会怎样

2

5、受控组件和非受控组件

在React中,表单元素(如input、select、textarea)可以被设计为受控组件或非受控组件。

受控组件:表单数据由React状态中的单一真实来源控制。每当表单的数据变化时,React状态也会相应更新。可以通过配合e.target.value给react变量赋值。

非受控组件是一种不由React状态控制的组件,表单数据由DOM本身处理。即不受setState()的控制,与传统的HTML表单输入相似,input输入值即显示最新值。
在非受控组件中,可以使用一个ref来从DOM获得表单值。

五、常见项目问题

1、文件、图片上传时格式转换(如何把数据传给后端的)

js前端技巧之图片格式转换(File、Blob、base64)_javascript技巧_脚本之家

1. BLOB 与 File

BLOB 转 File

1

const file = new File([blob], fileName, { type: fileType, lastModified: Date.now() });

File 转 BLOB

1

const blob = URL.createObjectURL(file);

2. BLOB 与 base64

BLOB(url) 转 base64

1

2

3

4

5

6

7

8

9

10

11

12

13

const image = new Image();

image.src = imgBlob;

image.onload = () => {

  // 构建canvas节点

  const canvas = document.createElement('canvas');

  canvas.width = image.width;

  canvas.height = image.height;

  const context = canvas.getContext('2d');

  context.drawImage(image, 0, 0, image.width, image.height);

  // 转换

  const imgBase64 = canvas.toDataURL();

  console.log(imgBase64);

};

base64 转 BLOB

1

2

3

4

5

6

7

8

9

10

11

12

13

// 分割base64

const temp = base64Data.split(',');

// 获取类型

const mime = arr[0].match(/:(.*?);/)[1];

// 解码使用 base-64 编码的字符串

const raw = window.atob(temp[1]);

const rawLength = raw.length;

// base64文件数据读取

const uInt8Array = new Uint8Array(rawLength);

for (let i = 0; i < rawLength; i += 1) {

  uInt8Array[i] = raw.charCodeAt(i);

}

const blob = new Blob([uInt8Array], { type: mime });

3. File 与 base64

File 转 base64

1

2

3

4

5

6

const reader = new FileReader();

reader.readAsDataURL(file);

reader.onload = function (e) {

  // e.target.result 即为base64结果

  console.log(e.target.result);

};

base64 转 File

1

2

3

4

5

6

7

8

9

10

11

12

13

// 分割base64

const arr = base64Data.split(',');

// 获取类型

const mime = arr[0].match(/:(.*?);/)[1];

// 解析base字符串

const bstr = atob(arr[1]);

const n = bstr.length;

// base64文件数据读取

const u8arr = new Uint8Array(n);

while (n--) {

  u8arr[n] = bstr.charCodeAt(n);

}

const file =  new File([u8arr], filename, { type: mime });

2、*upload上传大文件时如何提升用户体验

前端文件流、切片下载和上传:优化文件传输效率与用户体验-CSDN博客

3、

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值