81、什么是BFC
BFC(Block Formatting Context)是CSS布局中一个非常重要的概念,它是由W3C CSS 2.1规范定义的一种布局模型或渲染机制。BFC可以理解为一个独立的渲染区域或者说是一个隔离了的布局环境,在这个环境中,其内部元素的布局不会受到外界的影响,并且遵循一套内部特有的布局规则。
具体来说,当一个元素生成了BFC时,该元素内部的块级盒子会按照一定规则进行布局:
- 垂直方向上的布局:BFC中的块盒会在垂直方向上一个接一个地放置,彼此之间的margin会发生折叠(仅限相邻垂直margin),而且每个盒子的顶部始于包含块的顶部,底部则根据其内容和高度计算结束位置。
- 浮动元素包含:BFC可以包含其内部的所有浮动元素,即使这些浮动元素浮动出了BFC之外,也不会影响到BFC外部元素的布局。
- 外边距重叠消除:BFC可以阻止其内部元素的垂直外边距发生折叠,这对于防止外边距塌陷很有帮助。
- 内部绝对定位:BFC中绝对定位元素的位置计算也会受到这个区域的限制。
以下情况会触发BFC的创建:
- 根元素
<html>
- float属性不为
none
的元素 position
值为absolute
或fixed
的元素display
属性值为inline-block
,table-cell
,table-caption
,flex
, 或inline-flex
的元素overflow
值不是visible
的元素(如:overflow
为auto
、scroll
或hidden
)
BFC在解决布局问题中扮演着关键角色,例如清除浮动、防止外边距折叠、保持多列布局的边界独立等。通过理解和应用BFC,开发者能够更好地控制页面布局行为。
82、前端如何处理大量数据
前端处理大量数据时,需要考虑的主要问题是性能优化以及用户体验。以下是一些有效的策略和方法:
- 分页:
- 对于列表类数据,最常用的策略是分页加载,只显示当前页面的数据,当用户滚动到底部时,通过AJAX异步加载下一页数据,这样避免了一次性加载所有数据导致页面卡顿或内存溢出。
- 无限滚动(Infinite Scroll / 懒加载):
- 类似于分页,但更加流畅,当用户滚动到视窗底部时自动加载更多的数据,保持用户体验的同时降低一次性加载的压力。
- 虚拟滚动(Virtual Scrolling):
- 只渲染可视区域内的DOM节点,滚动时动态计算应该显示的数据部分并实时更新DOM,显著减少DOM节点数量。
- 数据分段处理:
- 将大数据集分割成小块,每次只处理和渲染一小部分数据,可以使用
createDocumentFragment()
来创建DOM片段,然后一次性将片段插入到DOM树中,减少渲染次数。
- 将大数据集分割成小块,每次只处理和渲染一小部分数据,可以使用
- 服务端/客户端过滤与排序:
- 在请求数据时就由服务器预先处理好分页、过滤和排序,减轻客户端负担;也可以在客户端处理,但要注意性能开销。
- 缓存和复用:
- 对于重复使用的数据,可以采用LRU(最近最少使用)缓存策略,将已加载过的数据缓存在内存中,减少不必要的网络请求。
- 数据预处理与压缩传输:
- 后端可以对数据进行压缩,前端接收后再解压,减少网络传输时间和带宽占用。
- 图表组件优化:
- 如果数据用于图表展示,可以选择高效的图表库,支持大数据量下的动态渲染和缩放,如Highcharts、ECharts等,它们通常自带大数据量的处理机制。
- Web Worker:
- 使用Web Worker进行复杂的计算任务,将CPU密集型工作转移到后台线程中,避免阻塞主线程,影响页面响应。
- React/Vue/Angular等框架的优化:
- 利用现代前端框架的虚拟DOM特性,结合shouldComponentUpdate生命周期钩子函数或React.memo、Vue的computed属性等机制,确保只有状态变化的部分重新渲染。
总之,前端处理大量数据的核心思想是尽可能减少DOM操作,合理分配计算资源,并结合合理的网络策略,使得数据加载和渲染过程平滑且不影响用户体验。
83、CSS函数有哪些
CSS 中有许多内置函数,它们用于处理颜色、尺寸、形状、渐变以及其他各种样式效果。以下是一些常见的CSS函数:
- 颜色处理函数
rgb()
,rgba()
:基于红绿蓝三原色及可选透明度创建颜色。hsl()
,hsla()
:基于色相、饱和度、亮度及可选透明度创建颜色。color()
(CSS Color Module Level 4):兼容多种颜色格式。currentColor
:引用元素当前的字体颜色。var()
:使用CSS变量创建颜色或其他值。
- 尺寸和单位转换函数
calc()
:计算长度、频率、角度、时间、百分比、数值等值。clamp()
(CSS Values and Units Module Level 4):限制值在指定的最小值和最大值之间。min()
和max()
:取多个值中的最小值或最大值作为最终计算结果。
- 滤镜和图像处理函数
blur()
,brightness()
,contrast()
,grayscale()
,hue-rotate()
,invert()
,opacity()
,saturate()
,sepia()
:CSS滤镜函数,用于调整图像效果。drop-shadow()
:添加阴影效果给元素。url()
:引入外部资源,如背景图片。
- 渐变函数
linear-gradient()
:创建线性渐变背景。radial-gradient()
:创建径向渐变背景。conic-gradient()
(CSS Images Module Level 4):创建锥形渐变背景。
- 几何函数
repeat()
、space
、round
、no-repeat
等关键字配合background-repeat
和background-size
属性创建背景图的重复模式。contain
和cover
关键字配合background-size
创建适应容器的背景图片大小。
- 其他函数
attr()
:获取HTML元素的属性值并应用于CSS样式中。env()
和var()
类似,但用来获取环境变量的值。calc()
也可用于创建复杂动画的关键帧(@keyframes)。escape()
和unescape()
(CSS Text Module Level 4草案):对字符串进行URL编码和解码。
随着CSS新特性的不断推出,未来可能会有更多的函数加入进来,以上列举的是目前较常用的一些CSS函数。
84、JS操作DOM的方法有哪些
JavaScript 操作 Document Object Model (DOM) 的方法非常丰富,涵盖了从查找、创建、修改到删除节点的各种操作。以下是主要的操作类别及其相关方法:
- 获取节点:
document.getElementById(id)
:通过ID获取唯一元素。document.getElementsByName(name)
:获取具有相同name属性的一组元素集合。document.getElementsByTagName(tagName)
:获取指定标签名的所有元素集合。document.getElementsByClassName(className)
:获取具有指定类名的所有元素集合。document.querySelector(selector)
:根据CSS选择器获取第一个匹配的元素。document.querySelectorAll(selector)
:根据CSS选择器获取所有匹配的元素集合。
- CSS 选择器: 这两种方法利用CSS选择器来定位元素:
element.querySelector(selector)
:在指定元素下查找第一个匹配的选择器的元素。element.querySelectorAll(selector)
:在指定元素下查找所有匹配的选择器的元素集合。
- 文档结构导航:
node.parentNode
:获取当前节点的父节点。node.childNodes
:获取当前节点的所有子节点(包括文本节点和元素节点)。node.firstChild
/node.lastChild
:获取当前节点的第一个/最后一个子节点。node.nextElementSibling
/node.previousElementSibling
:获取当前节点的下一个/上一个同级元素节点。node.firstElementChild
/node.lastElementChild
:获取当前元素节点的第一个/最后一个子元素节点。node.childElementCount
:获取当前元素节点的子元素个数。
- 创建节点:
document.createElement(tagName)
:创建一个新的元素节点。document.createTextNode(text)
:创建一个包含文本内容的新文本节点。document.createComment(data)
:创建一个注释节点。document.createDocumentFragment()
:创建一个文档片段节点,可以批量添加子节点。
- 添加节点:
parentNode.appendChild(node)
:在父节点的子节点列表末尾添加新节点。parentNode.insertBefore(newNode, referenceNode)
:在参考节点之前插入新节点。
- 删除节点:
parentNode.removeChild(node)
:从父节点中移除指定的子节点。
- 修改节点:
element.setAttribute(name, value)
:设置元素的属性值。element.removeAttribute(name)
:移除元素的属性。element.getAttribute(name)
:获取元素的属性值。element.innerHTML
:读取或设置元素内部的HTML内容。element.textContent
或element.innerText
:读取或设置元素内的纯文本内容(不包括HTML标签)。element.style.property
:访问和修改元素的内联样式。
- 事件操作:
element.addEventListener(type, listener[, options])
:添加事件监听器。element.removeEventListener(type, listener[, options])
:移除事件监听器。element.dispatchEvent(event)
:手动触发事件。
以上列出的方法可以帮助开发者灵活地操作页面上的DOM元素,并构建动态交互的Web应用程序。随着时间的推移,现代浏览器还引入了更多的API和优化方法,比如MutationObserver观察DOM变化,Shadow DOM进行组件化封装等。
85、Vue中监听一个对象的方法
在 Vue.js 中,要监听一个对象(无论是普通对象还是响应式对象)的变化,你可以使用 watch
选项。以下是如何在 Vue 组件中监听对象变化的几种常见方法:
方法1:浅层监听整个对象
如果你只需要监听对象本身的替换而不是对象内部属性的更改,可以直接监听整个对象:
Javascript
1export default {
2 data() {
3 return {
4 myObject: { a: 1, b: 2, c: 3 }
5 };
6 },
7 watch: {
8 myObject: {
9 handler(newValue, oldValue) {
10 console.log('myObject changed');
11 },
12 // 默认情况下,Vue 不会对对象内部属性的变化进行深度监听
13 // 若要监听对象内部属性的变更,则需设置 deep: true
14 // deep: true
15 }
16 }
17};
方法2:深度监听对象内部属性的变化
若你需要监听对象内部任意属性的更改,需要开启深度监听(deep watch):
Javascript
1export default {
2 data() {
3 return {
4 myObject: { a: 1, b: 2, c: { d: 4 } }
5 };
6 },
7 watch: {
8 myObject: {
9 handler(newValue, oldValue) {
10 console.log('myObject or any of its nested properties changed');
11 },
12 deep: true // 开启深度监听
13 }
14 }
15};
方法3:监听对象特定属性的变化
如果你想单独监听对象内部某个特定属性的变化,可以直接监听那个属性:
Javascript
1export default {
2 data() {
3 return {
4 myObject: { a: 1, b: 2, c: 3 }
5 };
6 },
7 watch: {
8 'myObject.a': {
9 handler(newValue, oldValue) {
10 console.log('myObject.a property changed');
11 },
12 // 即使这里没有明确指明 deep: true,
13 // 如果 myObject.a 是另一个对象,Vue 仍会进行浅层监听。
14 // 若要深度监听 myObject.a 内部的属性变化,此处仍需设置 deep: true
15 deep: true // 若有必要
16 }
17 }
18};
方法4:结合 computed 属性进行监听
有时你可能希望基于对象属性创建一个计算属性,并在该计算属性的依赖发生变化时执行逻辑。虽然这不是直接监听对象的方式,但在实际开发中常用来间接达到监听的效果:
Javascript
1export default {
2 data() {
3 return {
4 originalObject: { a: 1, b: 2, c: 3 }
5 };
6 },
7 computed: {
8 derivedObject: {
9 get() {
10 return this.originalObject;
11 },
12 set(newValue) {
13 // 新值赋值前可以在此处执行逻辑
14 console.log('derivedObject was updated');
15 // 更新原始对象以触发响应式变化
16 this.$set(this.originalObject, ...);
17 }
18 }
19 },
20 watch: {
21 derivedObject: {
22 handler(newValue, oldValue) {
23 // 此处可监听计算属性的变更,但由于 computed 的 set 已经执行了逻辑,
24 // 通常此处无需额外处理
25 },
26 deep: true // 如果需要深度监听
27 }
28 }
29};
请注意,深度监听会带来一定的性能开销,因为Vue需要递归遍历对象的所有属性来检测变化。因此,在实际项目中,应当根据需求权衡是否启用深度监听。同时,对于非响应式对象或新添加的属性,可能需要使用 this.$set
或 Vue.set
方法使其变为响应式的,以便Vue能够正确追踪其变化。
86、堆和栈的区别
- 分配方式:栈内存由系统自动分配和管理,它的分配和释放速度比较快。而堆内存是由程序员手动分配和释放的,分配和释放速度相对较慢。
- 内存大小:栈是有限的,一般情况下比较小,而堆内存则相对较大,可以根据需求动态扩展。
- 数据存储:栈内存主要用于存储局部变量、函数调用、参数传递以及执行上下文等,它的存储方式是“先进后出”,也称为LIFO(Last In First Out);而堆内存用于存储动态分配的对象,对象的生命周期可以比栈内存更长。
- 访问方式:栈的访问速度较快,因为它使用的是直接指令操作,而堆则需要通过指针来进行操作,访问速度较慢。
- 分配效率:栈的分配效率高,因为系统只需要调整栈指针的位置即可,而堆的分配效率相对较低,系统需要遍历整个堆寻找合适的内存块。
总结起来,栈适用于存储局部变量和临时数据,分配效率高、速度快;而堆适用于存储动态分配的对象,大小灵活、生命周期长。了解堆和栈的区别有助于合理地使用内存,并避免出现内存溢出和内存泄漏等问题。
87、单页应用程序SPA的优缺点
单页应用程序(SPA)是一种Web应用程序的设计模式,它在加载时不会重新加载整个页面,而是通过Ajax请求动态地更新页面的内容。以下是SPA的一些优缺点:
优点:
- 用户体验优秀:由于SPA在加载时只需要加载一次页面资源,之后的页面切换都是通过异步请求数据来实现,因此可以提供更快的页面加载速度和更流畅的用户体验。
- 前后端分离:SPA通常采用前后端分离的架构,前端负责展示界面和用户交互,后端负责数据处理和业务逻辑,这样有利于团队协作和项目的维护。
- 减少服务器负载:由于SPA在一次加载后可以在客户端进行页面的切换和更新,减少了对服务器的请求次数,可以降低服务器的负载压力。
- 可以实现跨平台开发:由于SPA通常使用HTML、CSS和JavaScript技术,可以通过各种框架和工具实现跨平台开发,例如React Native、Ionic等。
缺点:
- SEO不友好:由于SPA在加载时只加载一次页面,之后的页面切换都是通过JavaScript动态更新页面内容,搜索引擎爬虫无法获取到页面的完整内容,对SEO不友好。
- 初次加载时间较长:由于SPA需要加载大量的JavaScript代码和数据,在初次加载时可能会花费较长的时间,特别是对于移动设备和网络环境较差的用户。
- 对浏览器的兼容性要求高:由于SPA通常使用了较新的Web技术和API,对浏览器的兼容性要求较高,可能会导致在一些老旧的浏览器上无法正常运行。
- 安全性考虑:由于SPA通常将大量的业务逻辑和数据处理移至客户端,可能会增加一定的安全风险,例如容易受到XSS(跨站脚本攻击)等攻击。
综上所述,SPA适用于对用户体验要求较高、前后端分离清晰、不依赖于搜索引擎流量的应用场景,但需要注意其在SEO、初次加载时间和浏览器兼容性方面的一些缺点。
88、什么是MVVM
MVVM(Model-View-ViewModel)是一种软件架构模式,用于构建用户界面,特别是在基于GUI的应用程序中。MVVM模式将用户界面分成三个部分:
-
Model(模型):模型代表应用程序的数据和业务逻辑。在MVVM中,模型通常是一个简单的类或数据结构,负责管理数据的获取、存储和处理。
-
View(视图):视图是用户界面的结构和外观。它负责展示模型中的数据,并将用户的输入和操作传递给ViewModel处理。在MVVM中,视图通常是由XML、HTML、XAML等标记语言定义的。
-
ViewModel(视图模型):视图模型是视图和模型之间的连接器。它负责从模型中获取数据并将其转换为视图可以显示的格式,同时也负责接收来自视图的用户输入和操作,并将其传递给模型处理。ViewModel通常包含了应用程序的业务逻辑和状态管理。
MVVM的核心思想是通过将视图和模型之间的耦合度降低,使得它们可以独立地进行开发和测试。ViewModel充当了中介者的角色,将视图与模型解耦,同时也使得视图的重用和测试变得更加容易。
MVVM常用于基于Web的应用程序、桌面应用程序以及移动应用程序等场景,特别是在需要处理复杂交互和数据绑定的情况下,可以提高开发效率和代码的可维护性。常见的MVVM框架包括AngularJS、Vue.js、React.js等。
89、JS强制类型转换为false的有哪些
undefined
null
false
(本身就是一个布尔值)0
(数字0,包括正0和负0)NaN
(非数字值)- 空字符串 (
""
)
90、可以获取对象原型的方法有哪些
-
__proto__
属性:var obj = {}; console.log(obj.__proto__); // 直接访问对象的 __proto__ 属性获取原型对象
注意:虽然大多数现代浏览器支持
__proto__
,但它并不是ECMAScript标准的一部分,直到ES6才被正式标准化,但仍不推荐在生产环境中直接使用,因为它有可能在某些环境下不可用或行为不一致。 -
Object.getPrototypeOf()
函数:var obj = {}; console.log(Object.getPrototypeOf(obj)); // 使用Object.getPrototypeOf方法获取原型对象
这是标准且安全的方式来获取对象的原型。
-
构造函数的
prototype
属性:
如果你拥有对象的构造函数,并且知道它是通过该构造函数实例化的,那么可以通过构造函数来获取原型对象:function MyConstructor() {} var obj = new MyConstructor(); console.log(MyConstructor.prototype); // 这将返回MyConstructor函数的原型对象,同时也是obj的原型
-
Reflect.getPrototypeOf()
函数:
ES6引入了Reflect API,其中Reflect.getPrototypeOf()
是Object.getPrototypeOf()
的一个更形式化的版本:var obj = {}; console.log(Reflect.getPrototypeOf(obj)); // 返回对象的原型
总结来说,推荐使用Object.getPrototypeOf()
或Reflect.getPrototypeOf()
来确保跨环境兼容性和安全性。而在实际开发中,如果你已经知道对象是由哪个构造函数创建的,则可以通过构造函数的prototype
属性来查看或修改原型。不过,这并不适用于那些没有明确构造函数的对象。