知识深度面试题
- 我的博客需要缩宽页面观看,图片无法均放,很抱歉。
1. JS内存泄漏如何检测?场景有哪些?
- JavaScript内存泄漏是指在不需要使用某个变量或对象的时候,它仍然存在于内存中,从而导致内存占用过高的问题。检测JavaScript内存泄漏可以使用以下方法:
-
内存快照:使用Chrome浏览器的开发者工具或Node.js的heapdump模块,生成内存快照并分析其内容,找出哪些对象占据了过多的内存空间。
-
资源监控:使用浏览器的Performance API或Node.js的process.memoryUsage()方法,监控应用程序的内存使用情况,以及资源加载和释放情况。
-
代码审查:通过静态或动态代码分析,检测是否存在明显的内存泄漏风险,例如未关闭文件、未清理定时器或事件监听器等。
-
内存剖析工具:使用专业的内存剖析工具如Memory Analyzer Tool(MAT)、Chrome DevTools中的Heap Profiler等,帮助开发人员找到内存泄漏的根本原因。
具体检测方法怎么使用,直接问chatgpt,它回答很全面
常见的JavaScript内存泄漏场景包括:
- 定时器未清除:在使用setInterval或setTimeout方法创建定时器时,如果未及时清除,会导致回调函数一直占用内存。例如:
var timer = setInterval(function() {
// do something
}, 1000);
// 及时清除定时器
clearInterval(timer);
在使用定时器之后,一定要记得及时清除。
- 闭包未释放:如果闭包中引用了外部的变量,并且该闭包不再需要使用时,外部变量仍然会被保留在内存中不释放。例如:
function init() {
var name = 'John';
return function() {
console.log(name);
};
}
// 访问闭包函数并返回一个新的函数
var sayHello = init();
// 执行完毕闭包函数之后,手动解除对name变量的引用
sayHello = null;
在使用闭包时,一定要确保不再需要引用外部变量时,解除闭包对该变量的引用
。
- 循环引用:当两个对象相互引用,并且没有及时解除引用关系时,会导致两个对象都无法被垃圾回收,造成内存泄漏。例如:
var obj1 = {
};
var obj2 = {
};
obj1.obj2 = obj2;
obj2.obj1 = obj1;
// 手动解除obj1对obj2的引用
obj1.obj2 = null;
避免循环引用的最佳方法是避免相互引用。如果无法避免,可以通过手动解除其中一个对象对另一个对象的引用来解决问题。
- DOM节点未移除:在动态生成DOM节点时,如果未及时从文档树中移除节点,会导致节点一直占据内存,无法被垃圾回收。例如:
var container = document.getElementById('container');
// 在循环中动态生成DOM节点
for (var i = 0; i < 1000; i++) {
var node = document.createElement('div');
container.appendChild(node);
} // 如果不及时从文档树中移除这些节点,会导致内存泄漏
// 在不需要节点时,手动从文档树中移除它们
while (container.firstChild) {
container.removeChild(container.firstChild);
}
在生成DOM节点时,一定要及时从文档树中移除节点。
- 大对象未释放:当程序中创建了大对象时(例如大数组),如果这些对象不再需要使用,但是仍然被保留在内存中,就会导致内存泄漏。例如:
var data = [];
// 创建1亿个随机数加入data数组
for (var i = 0; i < 100000000; i++) {
data.push(Math.random());
}
// 不再需要data数组时,手动将其置为null
data = null;
当不再需要大对象时,可以手动将它们置为null,以便让垃圾回收器及时回收它们的内存。
2. vue当中常见内存泄漏,及解决方法
<script>
export default {
name :'Memory Leak Demo',
data:{
return {
arr:[10,20,30] // 数组 对象
intervalId:0 // 接住定时器,防止内存泄漏
}
},
methods:{
},
mounted(){
// 全局变量,有内存泄漏
window.arr = this.arr
// 全局事件,有内存泄漏
window.printArr = () =>{
console.log(this.arr)
}
// 使用intervalId接住定时器防止内存泄漏
this.intervalId = setInterval(()=>{
console.log(this.arr)
},100)
},
// vue2中 使用 beforeDestroy
beforeUnmount(){
// 清除全局变量
window.arr = null
// 清除全局事件
window.printArr = null
// 清除定时器
if(this.intervalId){
clearInterval(this.intervalId)
}
}
}
</script>
<script>
export default {
name :'Memory Leak Demo',
data:{
return {
arr:[10,20,30] // 数组 对象
}
},
methods:{
printArr(){
// 事件
console.log(this.arr)
}
},
mounted(){
// 监听事件
window.addEventListener('resize',this.printArr)
},
// vue2中 使用 beforeDestroy
beforeUnmount(){
// 移除事件
window.removeEventListener('resize',this.printArr)
}
}
</script>
3. 简述 jS标记清除法?
标记清除(Mark and Sweep)是一种常见的垃圾回收算法,用于在动态内存分配的程序中自动识别和释放不再使用的内存。
- 这个算法有三个步骤:
-
标记阶段:从程序中已知的"根"对象开始,遍历所有能够访问到的对象,并将它们打上标记,表示它们正在被使用。
-
清除阶段:遍历整个堆(也就是程序使用的内存空间),并删除没有被标记的对象。
-
压缩阶段(可选):将所有剩余的对象移动到堆的一端,以便为新的对象腾出更多的空间。
举例来说,假设我们有一个 JavaScript 程序,其中使用了很多对象。当某个对象变得不再需要时,JavaScript 解释器会利用标记清除算法将其从内存中清除,以便该内存可以用于其他目的。标记清除算法的工作原理就是先标记所有当前正在使用的对象,然后删除没有被标记的对象。
4. 遍历数组, for 和 forEach哪个更快?
- 在 JavaScript 中,for 和 forEach 都可以用来遍历数组。
- 一般来说,使用 for 循环比 forEach 更快,因为 for 循环是原生的 JavaScript 语法结构,而 forEach 是一个函数调用。
- 在实际应用中,其差异并不大,对于小型数组,两种方法的性能几乎相同。
const arr = [1, 2, 3, 4, 5];
console.time('for loop');
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
console.timeEnd('for loop');
console.time('forEach loop');
arr.forEach((item) => {
console.log(item);
});
console.timeEnd('forEach loop');
- 我们使用 console.time() 和 console.timeEnd() 方法来测量 for 循环和 forEach 循环的运行时间。
- 我们将数组 arr 中的元素逐个输出到控制台。在运行这段代码后,我们发现 for 循环的运行时间要短于 forEach 循环的运行时间。
- 虽然 for 循环可能比 forEach 循环快,但是 forEach 循环更加简洁易读,因此在某些情况下,使用 forEach 可以提高代码的可读性和可维护性。
5. 在vue中Ajax应该放在那个生命周期?
-
created()生命周期函数:可以在组件实例被创建之后立即调用Ajax请求。在这个时候,组件的DOM已经准备好了,但尚未挂载到页面上。
-
mounted()生命周期函数:可以在组件挂载到页面上后立即调用Ajax请求。在这个时候,组件的DOM已经被渲染出来,并且可以通过选择器获取到DOM元素。
-
activated()生命周期函数:在组件被激活时调用,例如在使用keep-alive包裹的动态组件中。此时,组件的DOM已经渲染出来,可以执行Ajax请求等操作。
放在那个生命周期会更快
- 如果只考虑Ajax请求的速度,确实是在created()生命周期函数中进行Ajax请求会更快一些。因为在Vue组件的生命周期中,created()函数会在实例被创建之后立即调用,而mounted()函数则会在组件挂载到页面时才会被调用。
- 将Ajax请求放在
mounted()
里面会更快。因为在mounted()
生命周期函数中,组件已经被挂载到DOM树
上,此时可以立即进行DOM
操作,并且用户也能够看到渲染后的界面,这样可以提高用户体验。 - 而在
created()
生命周期函数中,组件实例已经被创建,但是它还没有被挂载到DOM树
上,此时不能保证DOM
元素已经完全准备好,如果涉及到DOM操作
可能会出现问题。
6. VUE3 Composition API生命周期有何区别?
7. 什么是 Vue-router 的 MemoryHistory (abstract)?
8. vue2 和 vue3中 diff 算法的区别?
-
vue2 的 diff 算法。 看以下chatgpt回答
-
vue3 的 diff算法
9. 移动端 H5 click 有 300ms 延迟,如何解决?
10. 网络请求中,token 和 cookie有什么区别?
11. script标签、 defer 和 async 有什么区别?(重要!)
12. :prefetch 和 dns-prefetch 有什么区别?preload和prefetch又有什么区别?
13. 前端攻击手段有哪些?如何预防?
XSS攻击
CSRF 攻击
点击劫持 攻击
DDoS 攻击
SQL 注入 攻击
总结
14. WebSocket 和 HTTP 有什么区别?
- 创建服务器,下载依赖包
- npm init -y
- npm install ws --save
- npm install nodemon --save-dev
- yarn dev 运行
多人通讯
15. WebSocket 和 HTTP 长轮询的区别?
16. 描述从输入 url到页面展示的完整过程?
17. 重绘repaint 重排reflow 有什么区别?
下面是详细解释,及优化方案。
18. 如何实现网页多标签通讯
19. 如何实现网页 和iframe之前的通讯?
20. H5页面如何进行首屏优化?
21. 后台一次性返回10万条数据。你该如何渲染
22. 前端常用的设计模式和使用场景?
23. 接上题,观察者模式和发布订阅模式的区别?
- 观察者模式:Subject 和 Observer直接绑定,没有中间媒介。如addEventListener
- 发布订阅:Publisher和Observer互相不认识,需要中间媒介Event Channel。如EventBus自定义事件。
24. 你在实际工作中,做过哪些Vue优化?
- 路由懒加载,项目比较大,拆分路由,保证首页先加载。
import {
createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('../views/Home.vue')
},
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue')
},
// ...
]
const router = createRouter({
history:createWebHistory(),
routes
})
export default router
25. 你在使用vue过程中遇到过哪些坑?
26. 如何统一监听Vue 组件报错?
27. 如果一个 H5 很慢,你该如何排查性能问题?
27. Lighthouse,非常流行的第三方性能评测工具。
- 支持 PC 和 移动端
- npm i lighthouse -g 项目中安装
- lighthouse https://需要测试的网址 --view --preset=desktop
- 默认测试移动端,需要测试PC端添加 --preset=desktop