前端面试题—面试题整理(6)

一 面试题汇总(Boss直聘分享-金山云外包2面)

  1. 深拷贝和浅拷贝,怎么实现一个深拷贝
  2. 如何实现一个new
  3. 怎么理解回流和重绘
  4. 前端如何做性能优化
  5. 平常是如何学习前端的?
  6. 微前端了解吗?
  7. nginx有了解吗,如何用nginx做重定向
  8. 跨域是什么?怎么解决?jsonp方向是如何做的
  9. 事件循环说说,宏任务和微任务有哪些
  10. this指向问题
  11. 了解axios的原理吗?怎么实现的
  12. js有哪些数据类型

二 面试题解答(仅供参考)

2.1 深拷贝和浅拷贝,怎么实现一个深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
深拷贝是指将一个对象完整地复制到另一个对象,包括对象的所有属性和嵌套对象,而不是复制对象的引用。
实现深拷贝需要遍历对象的所有属性,并递归地复制每个属性的值。
下面是两种常见的实现深拷贝的方法:

1-递归方法:

递归方法是一种常见的实现深拷贝的方式,它遍历对象的所有属性,并递归地复制每个属性的值。
需要判断属性值的类型,如果是对象类型,则递归调用深拷贝函数复制对象的属性;
如果是基本数据类型,则直接复制属性的值。
需要注意处理循环引用的情况,可以使用一个 Map 数据结构保存已经拷贝过的对象,
遇到循环引用时直接返回已拷贝过的对象。
示例代码:

function deepCopy(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;

// 如果已经拷贝过该对象,则直接返回拷贝后的对象
if (hash.has(obj)) return hash.get(obj);

let copy = Array.isArray(obj) ? [] : {};
// 将新创建的对象添加到哈希表中
hash.set(obj, copy);

for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
// 递归拷贝对象的每个属性
copy[key] = deepCopy(obj[key], hash);
}
}

return copy;
}

const obj1 = { a: 1, b: { c: 2 } };
const obj2 = deepCopy(obj1);
console.log(obj2); // { a: 1, b: { c: 2 } }

2-JSON 方法:

JSON 方法是一种简单的实现深拷贝的方式,可以利用JSON.stringify 和JSON.parse方法实现深拷贝。
首先将对象转换为 JSON 字符串,然后再将 JSON 字符串解析为新的对象,从而实现深拷贝。
这种方法的缺点是,它无法处理函数、正则表达式、Symbol 等特殊类型的属性,会丢失对象的原型链。
示例代码:

const obj1 = { a: 1, b: { c: 2 } };
const obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj2); // { a: 1, b: { c: 2 } }

这两种方法都可以实现深拷贝,可以根据具体情况选择使用哪一种方法。
递归方法更加灵活和通用,适用于复杂的对象结构;而 JSON 方法更加简洁和直观,适用于普通的对象。

2.2 如何实现一个new

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
实现一个 new 操作符的功能,主要包括以下几个步骤:

1.创建一个新的空对象。
2.将该空对象的原型指向构造函数的原型(即让新对象继承构造函数的原型)。
3.执行构造函数,将 this 指向新创建的对象,并传入构造函数的参数。
4.如果构造函数返回的是一个对象,则返回该对象;否则返回新创建的对象。

以下是一个简单的实现示例:

function myNew(constructor, ...args) {
// 创建一个新的空对象,并将其原型指向构造函数的原型
const obj = Object.create(constructor.prototype);

// 执行构造函数,将 this 指向新创建的对象,并传入参数
const result = constructor.apply(obj, args);

// 如果构造函数返回的是一个对象,则返回该对象;否则返回新创建的对象
return result instanceof Object ? result : obj;
}

// 示例构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}

// 使用 myNew 操作符创建对象
const person = myNew(Person, 'Alice', 30);
console.log(person instanceof Person); // true
console.log(person.name); // Alice
console.log(person.age); // 30

这样就实现了一个简单的 new 操作符的功能。需要注意的是,这只是一个简单的实现示例,
真实的 new 操作符还有更多的细节需要处理,比如原型链的继承、构造函数的返回值等。

2.3 怎么理解回流和重绘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
回流(Reflow)和重绘(Repaint)是浏览器渲染页面时的两个重要概念,它们之间有着密切的关系,但又有着不同的含义。

1-回流(Reflow):

回流是指当 DOM 的尺寸、结构或某些属性发生变化时,浏览器需要重新计算元素的几何属性
(如位置、大小),并重新布局页面的过程。
当页面中的 DOM 发生变化时,浏览器需要重新计算元素的布局,并重新绘制页面,
以确保页面的显示是最新的。这个过程就是回流。
回流是一种比较消耗性能的操作,因为它会导致页面的重新布局和重绘,
特别是在频繁触发回流的情况下会严重影响页面的性能。

2-重绘(Repaint):

重绘是指当元素的样式发生变化时,浏览器只需要重新绘制元素的内容,
而不需要重新计算元素的几何属性和页面的布局。
当页面中的元素的样式发生变化时,浏览器会重新绘制这些元素的内容,
以确保它们的样式是最新的。这个过程就是重绘。
重绘的性能消耗通常比回流要低,因为它只涉及到元素的样式变化,不涉及元素的布局和页面的重新布局。

2.4 前端如何做性能优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
前端性能优化是提高网站加载速度、用户体验以及搜索引擎排名的重要手段。
以下是一些常见的前端性能优化策略:

1-减少 HTTP 请求:

合并和压缩 CSS、JavaScript 文件。
将多个小图标合并为一张雪碧图。
使用字体图标替代图片图标。
减少页面中引用的外部资源。

2-优化图片:

使用适当的图片格式(如 JPEG、PNG、SVG、WebP)。
压缩图片大小,减少不必要的图片质量。
使用合适的尺寸,避免加载过大的图片。

3-缓存优化:

使用浏览器缓存和服务端缓存来存储静态资源。
使用 HTTP 缓存控制头(如 Cache-Control、ETag、Expires)来设置缓存策略。


4-延迟加载:

延迟加载非必要资源,如图片、视频、JavaScript 文件等。
使用异步加载或按需加载技术,减少页面首次加载时的资源数量。

5-优化 CSS 和 JavaScript:

将 CSS 样式表放在 <head> 标签中,避免渲染阻塞。
将 JavaScript 放在 <body> 标签底部,减少页面加载时的阻塞。
使用外部文件加载,减少页面大小。

6-代码优化:

减少页面重绘和回流,避免不必要的 DOM 操作。
使用事件委托和事件节流,减少事件绑定的开销。
避免使用过多的全局变量和闭包,优化内存使用。

7-使用 CDN 加速:

使用内容分发网络(CDN)来加速静态资源的加载,提高页面加载速度。

8-移动端优化:

使用响应式设计或者移动端专用的布局,提升移动设备上的用户体验。
使用移动端专用的图片格式(如 WebP)和尺寸,减少页面加载时间。

9-监控和优化工具:

使用性能监控工具(如 Lighthouse、WebPageTest、PageSpeed Insights)来评估页面性能,并进行相应的优化。

综上所述,前端性能优化是一个综合性的工作,需要综合考虑各种因素,
从资源优化、代码优化、网络优化等方面进行综合优化,以提高网站的加载速度和用户体验

2.5 平常是如何学习前端的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
学习前端的方式因人而异,但通常包括以下几个步骤和方法:

1-学习基础知识:

先从学习前端的基础知识开始,包括 HTML、CSS和JavaScript等基础语言,了解它们的语法、特性和用法。
可以通过阅读相关的书籍、教程或在线课程来学习基础知识,也可以通过参加培训班或学习小组来系统地学习。

2-练习项目:

学习完基础知识后,可以通过练习项目来巩固所学内容,提高编码能力和实践经验。
可以选择一些简单的项目或示例来练习,比如制作一个个人网站、一个简单的博客系统或一个小型的网页应用等。

3-阅读文档和源码:

前端领域的技术发展迅速,常常有新的技术和框架出现,因此需要不断地阅读官方文档和源码,
了解最新的技术和最佳实践。
对于常用的框架和库,比如 React、Vue、Angular等,可以深入阅读它们的官方文档和源码,
理解其设计思想和实现原理。

4-参与开源项目:

参与开源项目是一个很好的学习和提升自己的方式,可以通过贡献代码、提交bug报告或参与讨论等方式来积累经验和技能。
可以选择一些适合自己水平的开源项目,并积极参与其中,与其他开发者一起合作、交流和学习。
5-持续学习和实践:

前端技术发展迅速,需要不断地学习和更新自己的知识,掌握最新的技术和工具。
可以通过阅读博客、参加技术社区、关注前端技术的最新动态,保持对前端技术的敏感度和好奇心,不断地学习和实践。

总的来说,学习前端需要不断地学习、实践和积累经验,同时保持对前端技术的热情和好奇心,
不断地探索和尝试新的技术和工具。

2.6 微前端了解吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
微前端是一种前端架构模式,旨在解决大型单体应用的复杂性和团队协作的问题。
微前端将前端应用拆分成更小的、独立的功能模块,每个模块都由不同的团队开发、部署和维护。
这些独立的模块可以独立开发、测试和部署,从而提高团队的生产效率和灵活性。

微前端的核心思想是将整个前端应用拆分成多个微服务,每个微服务都有自己独立的代码库、构建流程和部署管道。
每个微服务可以使用不同的技术栈和框架,根据自己的特点选择合适的工具和技术。

微前端通常包括以下几个关键概念和技术:

1-组件化:将前端应用拆分成多个独立的组件,每个组件都由不同的团队开发和维护,实现代码的复用和解耦。

2-路由管理:使用统一的路由管理机制来管理各个微服务之间的跳转和通信,实现统一的导航和页面切换。

3-状态管理:使用统一的状态管理机制来管理应用的状态和数据流,确保各个微服务之间的状态同步和一致性。

4-模块化加载:使用模块化加载机制来动态加载和卸载各个微服务,实现按需加载和懒加载,提高页面加载速度和性能。

5-隔离性:保持各个微服务之间的隔离性,避免相互影响和依赖,确保微服务的独立性和可扩展性。

微前端架构模式可以帮助团队更好地分工协作、提高开发效率、降低维护成本,
并且更好地支持持续集成和持续交付(CI/CD)等现代软件开发实践。
近年来,微前端逐渐成为前端领域的热门话题,受到越来越多开发者和团队的关注和实践。

2.7 nginx有了解吗,如何用nginx做重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Nginx是一个高性能的开源Web服务器,也可以作为反向代理服务器、负载均衡器和HTTP缓存服务器等。
通过Nginx,你可以实现各种网络服务的部署和管理。

要使用Nginx进行重定向,可以通过配置Nginx的server块来实现。
以下是一个简单的例子,演示如何将所有请求重定向到另一个URL:

server {
listen 80;
server_name example.com;

return 301 https://www.example.com$request_uri;
}
在上面的配置中:

listen指令指定Nginx监听的端口(80是HTTP默认端口)。
server_name指令指定Nginx监听的域名。
return指令指定了重定向的响应码(301表示永久重定向),并指定了重定向的目标URL。
如果你希望对特定的URL进行重定向,可以使用location块来实现。
以下是一个例子,演示如何将所有/old-page的请求重定向到/new-page:

server {
listen 80;
server_name example.com;

location /old-page {
return 301 /new-page;
}
}
在上面的配置中,location指令指定了匹配的URL路径,return指令指定了重定向的目标URL。

2.8 跨域是什么?怎么解决?jsonp方向是如何做的

1
2
3
4
5
6
7
8
9
跨域(Cross-Origin Resource Sharing,CORS)是指在 Web 开发中,
浏览器的同源策略(Same-Origin Policy)限制了在一个源(域名、协议、端口)下的
JavaScript 代码只能访问同源的资源,而无法直接访问其他域名下的资源。
跨域问题通常是指在前端页面中,无法通过 XMLHttpRequest 或 Fetch API 直接访问不同源的数据资源,因此需要采取一些方法来解决

以下是一些解决跨域问题的常见方法
1-服务器端设置 CORS 头部
2-JSONP(JSON with Padding)
3-CORS 代理

2.9 事件循环说说,宏任务和微任务有哪些

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
事件循环(Event Loop)是JavaScript运行时环境中的一种机制,用于处理异步任务的执行顺序和调度。
在浏览器环境中,事件循环负责处理事件队列中的任务,确保任务按照一定的顺序执行,
从而保证程序的正确性和可预测性。

事件循环包含了两个主要的概念:宏任务(macrotask)和微任务(microtask)。

1-宏任务(macrotask):

宏任务是由浏览器提供的异步任务,比如setTimeout、setInterval、requestAnimationFrame、I/O 操作等。
宏任务会被放入宏任务队列中,事件循环会从宏任务队列中取出任务并执行,直到队列为空。

2-微任务(microtask):

微任务是在当前任务执行结束后立即执行的任务,不需要等待事件循环的下一个轮次。
微任务主要包括 Promise 的回调函数、MutationObserver 的回调函数等。
微任务会被放入微任务队列中,在当前任务执行完毕后立即执行微任务队列中的所有任务,然后再执行宏任务队列中的任务。

事件循环的执行顺序如下:

首先执行当前的宏任务(如执行一段同步代码)。
当前宏任务执行完毕后,执行微任务队列中的所有微任务。
执行完微任务后,检查是否有新的宏任务加入队列,如果有,则执行新的宏任务。
重复以上步骤,直到宏任务队列和微任务队列都为空。
在每一次事件循环中,微任务的优先级高于宏任务,即微任务会优先于宏任务执行。
这意味着在当前宏任务执行完毕后,会优先执行微任务队列中的任务,然后再执行下一个宏任务。

总的来说,事件循环是 JavaScript 异步编程的核心机制之一,
理解事件循环对于理解 JavaScript 异步编程非常重要。
通过合理地利用宏任务和微任务,可以实现更高效、更可靠的异步代码。

2.10 this指向问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
JavaScript 中的 this 关键字指向当前函数执行的上下文,它的值取决于函数的调用方式。
this 的指向有以下几种情况:

1-全局上下文中:

在全局上下文中(即在任何函数外部),this 指向全局对象(在浏览器环境中为 window 对象,
在 Node.js 环境中为 global 对象)。

2-函数调用中:

在函数调用中,this 的指向取决于函数的调用方式。
如果函数被作为对象的方法调用,this 指向调用该方法的对象。
如果函数被普通函数调用,this 指向全局对象(在严格模式下为 undefined)。
如果函数被 call、apply 或 bind 方法调用,this 指向指定的对象。
如果函数被箭头函数调用,this 指向定义时的外层作用域的 this 值,而不是运行时的上下文。

3-构造函数中:

在构造函数中,this 指向新创建的实例对象。

4-DOM 事件处理函数中:
在 DOM 事件处理函数中,this 指向触发事件的元素。

5-原型链中:

在原型链中,this 指向调用该方法的对象。

6-严格模式下:

在严格模式下,如果函数被普通函数调用且没有明确指定 this,this 的值为 undefined。

由于 this 的指向取决于函数的调用方式,因此在编写 JavaScript 代码时,
需要注意函数的调用方式,确保 this 的指向符合预期。在需要保持一致的 this 上下文时,
可以使用 bind、call 或 apply 方法来显式指定 this 的值。

2.11 了解axios的原理吗?怎么实现的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js 环境中发起 HTTP 请求。
它的设计理念是简洁、直观且易于使用,提供了丰富的功能和选项,比如拦截器、请求取消、自动转换数据等。
Axios 的原理主要涉及以下几个方面:

1-基于 Promise:

Axios 使用Promise对象管理异步操作,可以使用 .then() 和 .catch() 方法处理请求的响应和错误。

2-底层使用 XMLHttpRequest 或 Fetch API:

在浏览器环境中,Axios 默认使用 XMLHttpRequest 对象来发送 HTTP 请求。
在支持的现代浏览器中,也可以选择使用 Fetch API 来发送请求。
在 Node.js 环境中,Axios 使用 Node.js 内置的 http 和 https 模块来发送请求。

3-拦截器:
Axios 提供了拦截器(interceptors)功能,可以在请求和响应发送和接收阶段添加自定义的中间件函数,
用于处理请求、响应或错误,比如在请求发送前添加认证信息、在响应接收后统一处理错误等。

4-数据转换:
Axios 支持自动转换请求和响应的数据格式,
可以根据 Content-Type 自动将数据格式化为 JSON、URL 编码或 FormData 格式。

5-取消请求:
Axios 提供了取消请求的功能,可以通过创建 CancelToken 实例来取消正在进行的请求,
避免不必要的网络流量和资源消耗。

6-并发请求:

Axios 支持发送并发请求,可以使用 axios.all() 和 axios.spread() 方法来处理多个请求的并发执行和结果合并。

7-错误处理:
Axios 提供了丰富的错误处理机制,可以通过 .catch() 方法捕获请求过程中的各种错误,
比如网络错误、超时错误、服务器错误等,并根据需要进行适当的处理。

Axios 的实现基于上述原理和功能,通过封装底层的 HTTP 请求库,
提供了更加简单、灵活和功能丰富的 HTTP 请求和响应处理功能,
使得在浏览器和 Node.js 环境中进行 HTTP 请求变得更加便捷和可靠。

2.12 js有哪些数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
JavaScript 中有以下几种基本数据类型和一种复杂数据类型:

1-基本数据类型:

字符串(String):用于表示文本数据,使用单引号 ' 或双引号 " 包裹。
数字(Number):用于表示数值,包括整数和浮点数。
布尔值(Boolean):用于表示逻辑值,只有两个取值,即 true 和 false。
空值(Null):用于表示空值,只有一个取值 null。
未定义(Undefined):用于表示未定义的值,只有一个取值 undefined。
Symbol:ES6 新增的数据类型,用于创建唯一的符号值。

2-复杂数据类型:

对象(Object):用于表示复杂数据结构,可以包含多个属性和方法。对象是由一对花括号 {} 包裹的键值对集合。

JavaScript 中的数据类型可以根据其特性分为两种:基本数据类型和复杂数据类型。
基本数据类型是不可变的,它们的值在内存中是固定的;
而复杂数据类型是可变的,它们的值可以包含多个属性和方法,可以动态地修改和扩展

三 图片

四 参考

  • ChatGPT3.5