javascript 中 forEach forof forin map 这几个遍历方法的区别在哪
- foreach map 都用于数据,但是map遍历后返回新的数组
- forof 用于遍历可迭代对象(数组,字符串, map, set 并且返回值),支持break continue return
- for in 用于遍历对象的属性,不做特殊处理会有原型链攀升的查找(hasOwnProperty)
1. 适用对象
foreach :Array 通过回调函数处理数组元素,不返回新数组
forof: ***可迭代对象***(数组、字符串、Map、Set 等)无法直接遍历普通对象(需通过 `Object.keys()` 转换)
forin : 用于对象或者其原型链上的可枚举属性的遍历: 返回key
map: 仅用于Array,遍历后返回新数组,不修改原数组,适合需要生成新数据的场景### ***返回值与控制语句***
2.### ***返回值与控制语句***
1. ***`forEach`***
- ***无返回值***,仅执行副作用操作(如修改原数组)。
- ***无法使用 `break`、`continue`***,需通过 `try...catch` 实现中断
1. ***`for...of`***
- ***支持 `break`、`continue` 和 `return`***,可灵活控制流程。
1. ***`for...in`***
- ***遍历顺序不确定***(对象属性无序),且可能包含非目标属性(如原型链属性)。
- 需搭配 `hasOwnProperty` 过滤非自身属性23。
1. ***`map`***
- ***返回新数组***,需通过 `return` 生成元素。
- 空数组不会触发回调。
3.***性能与使用场景***
1. ***性能对比***
- 原生 `for` 循环性能最优,`for...of` 次之,`forEach` 和 `map` 略低(但差距较小)。
- `for...in` 性能最低(需处理字符串键和原型链检查)24。
2. ***推荐场景***
- ***`forEach`***:仅需遍历数组且无需返回值时。
- ***`for...of`***:需遍历可迭代对象或使用流程控制语句时。
- ***`for...in`***:遍历对象属性(需注意过滤非自身属性)。
- ***`map`***:需生成新数组且保留原数组时。
判断一个对象为空
【如果存在不可枚举属性,那么就需要严格检测】
【均无法检测 Symbol 类型的键,需结合 Reflect.ownKeys()】
1. Object.entries().length === 0 es6【无法列举出不可枚举,需要配合Object.getOwnPropertyNames()】, 这种也是性能比较好,比较准确的
2. **`for...in` 配合 `hasOwnProperty`**
3.JSON.stringify() ,不过需要注意,在对象很大是会有性能问题,空对象很快,会忽略 `undefined`、函数、Symbol 值及数组中的特殊类型
用过 threejs 那些api
1. 首先是 场景的加载: THREE.Scene()
2.相机是有正交相机(OrthographicCamera)还有透视相机PerspectiveCamera
3. 渲染器 是 webGLRenderer
4. 几何体和材质: BoxGeometry、SphereGeometry,MeshBasicMaterial、MeshPhongMaterial
5. 动画和控制: OrbitControls 控制器,用于切换视角, 动画循环使用requestAnimationFrame,如5中的例子,而最新的动画系统可能涉及AnimationMixer和Clock
6. 加载器:,GLTFLoader、TextureLoader用于加载模型和纹理
7.Vector3、Matrix4、Quaternion等用于计算位置和旋转
beforeMount 与 created 核心区别
| 维度 | *created* | *beforeMount* |
|---|---|---|
| 触发时机 | 实例已完成数据观测、属性和方法的初始化,但尚未编译模板和挂载 DOM。 | 模板已编译为虚拟 DOM,但尚未挂载到真实 DOM 树中。 |
| 数据访问 | 可访问 data、methods 等响应式数据,适合初始化数据或发起异步请求。 | 同样可访问响应式数据,但此时数据已与模板绑定(如 {{ }} 占位符已被替换)37。 |
| DOM 操作 | 无法操作 DOM($el 属性为 undefined)78。 | 无法操作真实 DOM(虚拟 DOM 已生成,但未插入页面)。 |
| 应用场景 | 数据初始化、异步请求、非 DOM 相关的逻辑。 | 模板编译后的最后调整(如动态修改模板内容),但实际使用较少。 |
| 执行顺序 | 在 beforeMount 之前触发,属于初始化阶段。 | 在 created 之后、mounted 之前触发,属于挂载阶段。 |
commonJs esMoudle 中循环引用:
-
commonjs【运行时加载,代码执行到require,就会同步去加载这个模块】 中对于循环引用,比如a.js引用了 b.js,现从上往下执行,碰到require('b.js'),那么就会暂停执行a.js, 去执行b.js, 在b.js同理,从上往下执行,碰到a.js 暂停。直至两个模块执行完毕
-
es module【是编译时加载。编译前就会解析出依赖关系[这也是es6 或者插件机制可以做treeShaking的原因],所以import 会被提前执行,由于import会被提前执行,所以在循环引用这条链路】|||
-
esm的import命令是在编译阶段就执行,优先于自身内容执行。
esm并不关心是否存在循环引用,只是生成一个指向被加载模块的引用,代码未执行时,这个引用的值就是undefined。 -
esm 也是跟commonjs 一样,除了import,代码从上往下执行,碰到模块就暂停执行,转而去执行模块里的代码。但是esm 有时候a.js没有执行到导出,所以在b中引入a.js 得到的值是一个undefined。
-
webpack 的插件 circular-dependency-plugin 来排查项目中的循环引用问题非常方便
-
循环引用可能会导致内存栈溢出。但也不是一定会有问题,比如 b 导出了两个方法,a 引用的是 b 导出的方法 fn1,而 b 是在方法 fn2 里引用的
a,这种情况其实是不会有问题的。
但由于可能的风险,且难以发现,所以编写代码时还是尽量规避使用循环引用。
如何封装组件【面试官问我,我是如何封装组件的】
- 首先是要确定这个组件
- 组件的具体用途:是展示数据、处理用户输入,还是提供某种交互功能?
- 组件的使用场景:该组件会在什么样的页面或应用中使用?
- 组件的复用性:组件是否需要在多个不同的地方使用
2.设计组件的接口和属性是封装组件的第二步。接口和属性决定了组件的灵活性和易用性。通常需要考虑以下几个方面:
- Props:组件接收的属性,决定了组件的行为和外观。例如,一个按钮组件可能需要接收
type、size、disabled等属性。 - Events:组件需要触发的事件,通常用于父组件与子组件之间的通信。例如,按钮组件可能需要触发
click事件。 - Slots:插槽用于在组件内部定义可插入的内容。插槽可以使组件更加灵活和复用。
3.编写组件模板和样式
4.实现组件的逻辑功能
5.测试和优化组件
- 封装组件的最后一步是进行测试和优化。测试可以确保组件在各种场景下都能正常工作,优化则可以提高组件的性能和用户体验。测试可以包括单元测试、集成测试和手动测试等。优化则可以包括减少不必要的渲染、提高代码的可读性和维护性等。
Q: 如何封装Vue组件?
封装的理解:
- 页面上重复了,我封装方便一改多,这种一般和业务强耦合
- 对原有组件的增强。比如给组件库的某一组件,再包裹封装一层,以统一样式风格、补充功能
A: 封装Vue组件可以使代码更加模块化和可复用,以下是封装Vue组件的思路:
- 确定组件的功能和用途:首先要明确组件的功能和用途,确定组件需要具备哪些功能以及如何与其他组件进行交互。
- 编写组件的模板:使用Vue的模板语法编写组件的HTML结构,可以使用Vue的指令、插值表达式等来实现动态的数据绑定和事件绑定。
- 编写组件的样式:为组件编写样式,可以使用CSS或者预处理器如Sass、Less等来进行样式的定义和管理,保证组件的样式与整个应用的风格一致。
- 编写组件的逻辑:使用Vue的JavaScript代码编写组件的逻辑部分,可以在组件内部定义数据、计算属性、方法和生命周期钩子等,实现组件的交互和功能。
- 提供组件的接口:通过props属性可以向组件传递数据,通过事件可以向父组件发送消息,通过插槽可以实现组件内容的灵活定制,可以根据组件的用途和需求来设计和暴露适当的接口。
- 测试和优化组件:进行组件的测试和优化,确保组件的稳定性和性能,可以使用Vue的单元测试工具和性能分析工具进行测试和优化。
- 文档和示例:编写组件的文档和示例,以便其他开发者了解和使用组件,可以使用文档生成工具和示例代码库来简化文档和示例的编写。
Q: 为什么要封装Vue组件?
A: 封装Vue组件有以下几个好处:
- 模块化和可复用:封装Vue组件可以将功能和样式封装在一个独立的组件中,使代码更加模块化和可复用,提高开发效率。
- 组件化开发:Vue采用组件化开发的思想,将复杂的页面拆分为多个组件,每个组件负责一个小的功能模块,使代码更加清晰和易于维护。
- 提高可维护性:封装Vue组件可以将组件的逻辑和样式封装在一起,使代码更加可维护,便于后续的修改和扩展。
- 提高代码复用性:封装Vue组件可以将常用的UI组件封装起来,以便在不同的项目中复用,减少重复编写代码的工作量。
- 提高开发效率:封装Vue组件可以提高开发效率,开发者只需要关注组件的功能和接口,不需要关心组件的实现细节,可以快速构建复杂的页面。
Q: 如何提高封装Vue组件的质量?
A: 提高封装Vue组件的质量可以从以下几个方面入手:
- 代码规范:遵循一定的代码规范,如使用一致的命名规则、缩进风格、注释规范等,使代码更加易读和易于维护。
- 组件设计:合理设计组件的接口和功能,尽量将组件拆分为更小的、可复用的部分,保持组件的独立性和高内聚性。
- 错误处理:对于可能出现的错误进行适当的处理,如输入数据的校验、异常情况的处理等,提高组件的健壮性和容错性。
- 文档和示例:编写详细的文档和示例,描述组件的用途、使用方法和注意事项,方便其他开发者了解和使用组件。
- 测试和优化:进行组件的单元测试,确保组件的稳定性和正确性,同时进行性能优化,提高组件的性能和响应速度。
- 版本管理:使用版本管理工具如Git来管理组件的版本,方便追踪和回滚,以及与其他开发者进行协作和共享。
- 用户反馈:及时收集用户的反馈和建议,了解用户的需求和问题,对组件进行改进和优化。
常见的 宏任务 和微任务
浏览器事件机制
1.首先浏览器是单线程的,主线程一次只能执行一个任务,为了避免浏览器执行复杂任务或者耗时任务导致卡死,浏览器就引入了这个机制,事件循环的作用是协调同步任务和异步任务的执行,确保页面能够及时响应用户交互和更新。
事件循环的主要目标是让浏览器可以:
1. 同时处理多个任务。
2. 保页面响应性,避免页面被长时间阻塞。
3. 顺序执行代码,确保异步任务的执行在适当的时机。
### 事件循环的执行过程
1. **同步任务执行**:
- 当 JavaScript 代码开始执行时,所有同步任务会按顺序被压入执行栈并执行,直到执行栈为空。
1. **微任务的执行**:
- 执行完同步任务后,事件循环会检查微任务队列。如果微任务队列非空,事件循环会依次取出并执行队列中的所有微任务,直到队列为空为止。
- 微任务通常是由 `Promise`、`MutationObserver` 等触发的回调函数。
1. **宏任务的执行**:
- 当微任务队列为空时,事件循环会从消息队列中取出第一个宏任务(例如,`setTimeout`、`setInterval`、`click` 事件等),将其推入执行栈并执行。
- 执行宏任务的顺序是先来先服务,按照队列的顺序逐个执行。
1. **渲染更新**:
- 在执行宏任务后,浏览器会有一个渲染阶段,负责更新页面内容(比如重新渲染页面、更新样式等)。
- 渲染会在事件循环中的适当时机进行,以保证用户界面的更新和用户交互的响应性。
### 事件循环的执行顺序
假设有以下代码示例:
```
javascriptCopy Code
console.log('Start'); // 同步任务
setTimeout(() => { // 宏任务
console.log('Timeout');
}, 0);
Promise.resolve().then(() => { // 微任务
console.log('Promise');
});
console.log('End'); // 同步任务
```
执行过程:
1. 执行 `console.log('Start')`,打印 `'Start'`(同步任务)。
1. 执行 `setTimeout()`,它将回调函数放入宏任务队列。
1. 执行 `Promise.resolve().then()`,它将 `then` 回调放入微任务队列。
1. 执行 `console.log('End')`,打印 `'End'`(同步任务)。
1. 执行栈为空时,事件循环检查微任务队列并执行 `Promise` 回调,打印 `'Promise'`。
1. 微任务队列为空后,事件循环开始执行宏任务队列中的 `setTimeout` 回调,打印 `'Timeout'`。
最终输出:
```
Copy Code
Start
End
Promise
Timeout
```
### 微任务和宏任务的优先级
- **微任务**优先于**宏任务**执行,且每当事件循环回到主执行栈为空时,会立即执行微任务队列中的任务。
- 微任务包括 `Promise` 的 `.then()`、`.catch()`、`.finally()` 回调函数,`MutationObserver`,以及一些异步 API 提供的回调。
- 宏任务包括 `setTimeout`、`setInterval`、`setImmediate`、DOM 事件(如 `click`、`load`)等。
### 事件循环的应用和影响
1. **UI 更新与交互**:
- 在浏览器中,事件循环确保用户输入、页面渲染和异步任务(如网络请求、定时任务)能够按预期顺序执行,避免界面冻结或卡顿。
1. **性能优化**:
- 由于事件循环的执行顺序是严格的,开发者可以通过合理安排异步操作的顺序(例如,利用微任务队列来提高优先级)来优化应用的性能。
1. **避免阻塞**:
- 在执行长时间的计算或网络请求时,避免直接阻塞执行栈,改为通过异步操作处理,确保页面的响应性。
浏览器渲染原理
浏览器渲染原理
渲染: 将html 字符串经过复杂的逻辑处理,最终转化为屏幕上的像素点信息。
如何解析html:
1.首先生成对象结构树,包括css样式树 css om,dom节点树
至于为什么主线程遇到js时,必须暂停等带下载执行完毕再继续,是因为当前js可能会修改之前生成的dom,所以dom树的生成必须暂停。
2 样式计算 =》 会得到带有最终计算样式的dom树
3 布局
盒子模型: 行盒 /块盒 模型 .
- 分层
目前浏览器都会根据自己的判断,将页面上的元素分层,这样做的好处就是,当某些操作触发重绘时,不需要将页面全部元素都重新绘制,只需要找到对应的层,进行绘制,然后排布进去就OK。
css will-change: transform 这个css 属性告诉浏览器,设置了该元素的属性未来可能会发生变化,建议单独分层,便于后续的重新绘制。
5 绘制 根据绘制指令去完成绘制
6分块
7光栅化:
8 Draw 图像
总: