真实企业面试题1(中小厂)

41 阅读14分钟

javascript 中  forEach  forof forin map  这几个遍历方法的区别在哪

  • foreach map 都用于数据,但是map遍历后返回新的数组
  • forof 用于遍历可迭代对象(数组,字符串, map, set 并且返回值),支持break continue return
  • for in 用于遍历对象的属性,不做特殊处理会有原型链攀升的查找(hasOwnProperty)
1. 适用对象
foreach :Array 通过回调函数处理数组元素,不返回新数组‌
forof: ***可迭代对象**‌*(数组、字符串、MapSet 等)‌无法直接遍历普通对象(需通过 `Object.keys()` 转换)‌
forin : 用于对象或者其原型链上的可枚举属性的遍历: 返回key
map: 仅用于Array,遍历后返回新数组‌,不修改原数组,适合需要生成新数据的场景‌### ***返回值与控制语句***
2.### ***返回值与控制语句***

1.  *‌**`forEach`**‌*

    -   *‌**无返回值**‌*,仅执行副作用操作(如修改原数组)‌。
    -   *‌**无法使用 `break``continue`**‌*,需通过 `try...catch` 实现中断‌

1.  *‌**`for...of`**‌*

    -   *‌**支持 `break``continue` 和 `return`**‌*,可灵活控制流程‌。

1.  *‌**`for...in`**‌*

    -   *‌**遍历顺序不确定**‌*(对象属性无序),且可能包含非目标属性(如原型链属性)‌。
    -   需搭配 `hasOwnProperty` 过滤非自身属性‌231.  *‌**`map`**‌*

    -   *‌**返回新数组**‌*,需通过 `return` 生成元素‌。
    -   空数组不会触发回调‌。
    
3.***性能与使用场景**‌*

1.  *‌**性能对比**‌*

    -   原生 `for` 循环性能最优,`for...of` 次之,`forEach` 和 `map` 略低(但差距较小)‌。
    -   `for...in` 性能最低(需处理字符串键和原型链检查)‌242.  *‌**推荐场景**‌*

    -   *‌**`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. 几何体和材质: BoxGeometrySphereGeometryMeshBasicMaterialMeshPhongMaterial
5. 动画和控制: OrbitControls  控制器,用于切换视角, 动画循环使用requestAnimationFrame,如‌5中的例子,而最新的动画系统可能涉及AnimationMixerClock
6. 加载器:,GLTFLoaderTextureLoader用于加载模型和纹理
7.Vector3Matrix4Quaternion等用于计算位置和旋转

beforeMount 与 created 核心区别

维度*created**beforeMount*
触发时机实例已完成数据观测、属性和方法的初始化,但尚未编译模板和挂载 DOM‌。模板已编译为虚拟 DOM,但尚未挂载到真实 DOM 树中‌。
数据访问可访问 datamethods 等响应式数据,适合初始化数据或发起异步请求‌。同样可访问响应式数据,但此时数据已与模板绑定(如 {{ }} 占位符已被替换)‌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,这种情况其实是不会有问题的。

但由于可能的风险,且难以发现,所以编写代码时还是尽量规避使用循环引用。

如何封装组件【面试官问我,我是如何封装组件的】

  1. 首先是要确定这个组件
  • 组件的具体用途:是展示数据、处理用户输入,还是提供某种交互功能?
  • 组件的使用场景:该组件会在什么样的页面或应用中使用?
  • 组件的复用性:组件是否需要在多个不同的地方使用

2.设计组件的接口和属性是封装组件的第二步。接口和属性决定了组件的灵活性和易用性。通常需要考虑以下几个方面:

  • Props:组件接收的属性,决定了组件的行为和外观。例如,一个按钮组件可能需要接收typesizedisabled等属性。
  • Events:组件需要触发的事件,通常用于父组件与子组件之间的通信。例如,按钮组件可能需要触发click事件。
  • Slots:插槽用于在组件内部定义可插入的内容。插槽可以使组件更加灵活和复用。

3.编写组件模板和样式

4.实现组件的逻辑功能

5.测试和优化组件

  • 封装组件的最后一步是进行测试和优化。测试可以确保组件在各种场景下都能正常工作,优化则可以提高组件的性能和用户体验。测试可以包括单元测试、集成测试和手动测试等。优化则可以包括减少不必要的渲染、提高代码的可读性和维护性等。

Q: 如何封装Vue组件?

封装的理解:

  • 页面上重复了,我封装方便一改多,这种一般和业务强耦合
  • 对原有组件的增强。比如给组件库的某一组件,再包裹封装一层,以统一样式风格、补充功能

A: 封装Vue组件可以使代码更加模块化和可复用,以下是封装Vue组件的思路:

  1. 确定组件的功能和用途:首先要明确组件的功能和用途,确定组件需要具备哪些功能以及如何与其他组件进行交互。
  2. 编写组件的模板:使用Vue的模板语法编写组件的HTML结构,可以使用Vue的指令、插值表达式等来实现动态的数据绑定和事件绑定。
  3. 编写组件的样式:为组件编写样式,可以使用CSS或者预处理器如Sass、Less等来进行样式的定义和管理,保证组件的样式与整个应用的风格一致。
  4. 编写组件的逻辑:使用Vue的JavaScript代码编写组件的逻辑部分,可以在组件内部定义数据、计算属性、方法和生命周期钩子等,实现组件的交互和功能。
  5. 提供组件的接口:通过props属性可以向组件传递数据,通过事件可以向父组件发送消息,通过插槽可以实现组件内容的灵活定制,可以根据组件的用途和需求来设计和暴露适当的接口。
  6. 测试和优化组件:进行组件的测试和优化,确保组件的稳定性和性能,可以使用Vue的单元测试工具和性能分析工具进行测试和优化。
  7. 文档和示例:编写组件的文档和示例,以便其他开发者了解和使用组件,可以使用文档生成工具和示例代码库来简化文档和示例的编写。

Q: 为什么要封装Vue组件?

A: 封装Vue组件有以下几个好处:

  1. 模块化和可复用:封装Vue组件可以将功能和样式封装在一个独立的组件中,使代码更加模块化和可复用,提高开发效率。
  2. 组件化开发:Vue采用组件化开发的思想,将复杂的页面拆分为多个组件,每个组件负责一个小的功能模块,使代码更加清晰和易于维护。
  3. 提高可维护性:封装Vue组件可以将组件的逻辑和样式封装在一起,使代码更加可维护,便于后续的修改和扩展。
  4. 提高代码复用性:封装Vue组件可以将常用的UI组件封装起来,以便在不同的项目中复用,减少重复编写代码的工作量。
  5. 提高开发效率:封装Vue组件可以提高开发效率,开发者只需要关注组件的功能和接口,不需要关心组件的实现细节,可以快速构建复杂的页面。

Q: 如何提高封装Vue组件的质量?

A: 提高封装Vue组件的质量可以从以下几个方面入手:

  1. 代码规范:遵循一定的代码规范,如使用一致的命名规则、缩进风格、注释规范等,使代码更加易读和易于维护。
  2. 组件设计:合理设计组件的接口和功能,尽量将组件拆分为更小的、可复用的部分,保持组件的独立性和高内聚性。
  3. 错误处理:对于可能出现的错误进行适当的处理,如输入数据的校验、异常情况的处理等,提高组件的健壮性和容错性。
  4. 文档和示例:编写详细的文档和示例,描述组件的用途、使用方法和注意事项,方便其他开发者了解和使用组件。
  5. 测试和优化:进行组件的单元测试,确保组件的稳定性和正确性,同时进行性能优化,提高组件的性能和响应速度。
  6. 版本管理:使用版本管理工具如Git来管理组件的版本,方便追踪和回滚,以及与其他开发者进行协作和共享。
  7. 用户反馈:及时收集用户的反馈和建议,了解用户的需求和问题,对组件进行改进和优化。

常见的 宏任务 和微任务

image.png

浏览器事件机制

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 布局   

 盒子模型:  行盒 /块盒 模型 .

  1. 分层 

目前浏览器都会根据自己的判断,将页面上的元素分层,这样做的好处就是,当某些操作触发重绘时,不需要将页面全部元素都重新绘制,只需要找到对应的层,进行绘制,然后排布进去就OK。

css  will-change: transform  这个css 属性告诉浏览器,设置了该元素的属性未来可能会发生变化,建议单独分层,便于后续的重新绘制。

5 绘制  根据绘制指令去完成绘制

6分块

 

 

7光栅化:

 

8 Draw 图像

总: