前端面试拼图-原理源码(4),前端面试资料集合

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

包括: Promise,async/await,process.nextTick,其中process.nextTick优先级最高

总之,在一次事件循环中,先执行当前执行栈中的代码,然后检查是否有微任务需要执行,如果有,则依次执行微任务,直到微任务队列为空;接着从宏任务队列中提取下一个宏任务进行执行。这样的设计保证了微任务能够在宏任务执行结束之后立即得到执行,从而更及时地处理异步操作的结果。

注:

任务队列是任务的集合,而不是队列。因为事件处理模型会选取第一个可执行任务开始执行,而不是队首的任务。

MDN上最新描述已经不再使用宏任务,沿用它是为了更容易理解

在 JavaScript 中通过 queueMicrotask() 使用微任务 - Web API 接口参考 | MDN一个微任务(microtask)就是一个简短的函数,当创建该微任务的函数执行之后,并且只有当 Javascript 调用栈为空,而控制权尚未返还给被用户代理用来驱动脚本执行环境的事件循环之前,该微任务才会被执行。事件循环既可能是浏览器的主事件循环也可能是被一个 web worker 所驱动的事件循环。这使得给定的函数在没有其他脚本执行干扰的情况下运行,也保证了微任务能在用户代理有机会对该微任务带来的行为做出反应之前运行。icon-default.png?t=N7T8https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_DOM_API/Microtask_guide

Node.js新版本,推荐使用setImmediate代替process.nextTick。

3. 遍历数组,for和forEach那个更快?

先上代码验证下,结论是用for通常会比forEach更快

const arr = []
for(let i = 0; i < 100*10000 ; i++) {
  arr.push(i)
}
const length = arr.length

console.time('for')
let n1 = 0
for (let i = 0; i < length; i++) {
  n1++
}
console.timeEnd('for')  // 3.838134765625 ms

console.time('forEach')
let n2 = 0
arr.forEach(() => n2++) 
console.timeEnd('forEach')  // 14.0830078125 ms

forEach每次都要创建一个函数来调用(函数需要对应的作用域,会有额外的性能开销),而for不会创建函数。

除此之外,使用for的优点还有:

  • 更多控制:使用for循环可以更灵活地控制循环过程,可以通过控制索引来实现跳过、中断、反向等操作。
  • 可中断性:for循环可以通过break语句或return语句随时中断循环,而forEach方法不支持中断循环,只能通过抛出异常来模拟中断。 虽然for循环在性能上可能更快,但在某些情况下,使用forEach方法可能更具可读性和简洁性
3. 虚拟DOM(vdom)真的很快吗?

虚拟DOM(virtual dom) 一种用于简化 UI 更新和渲染逻辑的技术, 主要优点在于简化了对真实 DOM 的频繁操作。我们知道Vue React等框架的核心价值在于实现了数据视图分离,数据驱动视图。这样前端程序员coding时只关注业务数据,而不用再关心DOM变化,它们实现数据驱动视图的技术方案就是vdom。

实际上,vdom并不快,JS直接操作DOM才是最快的;但是"数据驱动视图"需要合适的技术方案,不能全部DOM重建;vdom就是目前最合适的技术方案(并不是因为它快,而是合适)。

扩展:svelte框架就不用vdom

不同于React和Vue等框架,使用vdom来跟踪和管理应用程序的状态和视图更新。Svelte框架采用了一种称为“编译时”方法,通过在编译时将组件代码转换为高效的JavaScript代码,避免了传统vDOM框架中的运行时开销。这种方法使得Svelte成为一个快速、轻量级的前端框架选择。

5. Node.js如何开启多进程?进程如何通讯?

进程(Process) VS 线程 (Thread)

进程是指正在运行的程序的实例

每个进程都有自己的地址空间、内存、文件描述符、打开的文件等资源。进程是操作系统进行资源分配和调度的基本单位,可以独立运行、拥有自己的生命周期,并与其他进程隔离。

线程是进程的一个实体,是独立运行和独立调度的基本单位(CPU上真正运行的是线程)。一个进程可以包含多个线程,这些线程共享进程的资源,包括内存、文件句柄等。线程共享同一地址空间,可以访问相同的数据,因此线程之间通信更加方便高效。线程也被称为轻量级进程,因为创建和切换线程所需的开销比进程小得多。

JS是单线程的,但是可以开启多个进程执行,如WebWorker

为何需要多进程?

多核CPU,更适合处理多进程,而多进程同时执行多个任务,可以充分利用多核处理器的并行计算能力,提高系统的整体性能和效率

内存较大,多个进程才能更好的利用(单进程有内存上限,一般2G左右);

此外,多进程还可以实现任务隔离,提高安全性和稳定性

总之,"压榨"机器资源,更快,更节省

通过child_process.fork开启子进程

/**
* 主进程
*/
const http = require('http')
const fork = require('child_process').fork

const server = http.createServer(() => {
  if(req.url === '/get-sum') {
    console.info('主进程 id', process.pid)
    // 开启子进程
    const computeProcess = fork('./computer.js')
    computeProcess.send('开始计算')
    computeProcess.on('message', data => {
      console.info('主进程接收到信息:', data)
      res.end('sum is ' + data)
    })
    computeProcess.on('close', data => {
      console.info('主进程因报错而退出')
      computeProcess.kill()
      res.end('error')
    })
  }

})
server.listen(3000, () => {
  console.info('localhost: 3000')
})

/**
* computed.js 子进程,计算
*/
function getSum() {
  let sum = 0
  for (let i = 0; i <10000; i++) {
    sum += i
  }
  return sum
}
//子进程可以通过这种自定义事件的方式来监听
process.on('message', data => {
  console.log('子进程 id', process.pid)
  console.log('子进程接收到的信息‘, data)
  const sum = getSum()
  // 发送消息给主进程
  process.send(sum)
})

//localhost: 30000
// 主进程 id: 80780
// 子进程 id: 80781
// 子进程接收到的信息: 开始计算
// 主进程接收到的信息: 49995000

使用cluster方式开启多进程

/**
* cluster:多子进程,多个服务
* 根据CPU核数派生子进程,每个子进程创建一个Http服务
*/

const http = require('http')
const cpuCoreLength = require('os').cpus().length   //获取CPU核数
// 引入cluster
const cluster = require('cluster')

if(cluster.isMaster) {   // 是cluster的主进程
  for (let i = 0; i < cpuCoreLength; i++) {
    cluster.fork()   // 开启子进程
  }
  cluster.on('exit', worker => {   // worker表示当前进程的实例
    console.log('子进程退出')
    cluster.fork()  // 进程守护
  })
} else {
// 多个子进程会共享一个TCP连接,提供一份网络服务
  const server = http.createServer((rep, res) => {
    res.writeHead(200)
    res.end('done')
  })
  server.listen(3000)
}

总结:Node.js中开启子进程child_process.fork和cluster.fork;使用send和on传递消息

扩展:工作中实际使用PM2 完成进程守护和开启多个进程 PM2 是一个针对Node.js应用的进程管理工具,可以用于进程守护、负载平衡、日志管理等。通过PM2可以方便地启动、停止、重启应用程序,并监控应用程序的运行状态

6. 请描述js-bridge的实现原理?

什么是JS Bridge?

JSBridge是一种在Webview和Native之间进行通信的技术。JS 无法直接调用native API,需要通过一些特定"格式" 来调用,这些"格式"统称为JS-Bridge ,例如微信的JSSDK。基本实现原理:

  1. 在Native端中创建一个Javascript对象,通过WebView的addJavascriptInterface()方法将该对象注入到WebView中。
  2. 在Web端中通过Javascript代码调用该对象的方法,从而实现与Native的通信。
  3. 在Native端中捕获Javascript代码的调用请求,解析请求参数并执行相应的操作(可执行一些特定操作,如打开摄像头、访问设备传感器等)。
  4. 将操作结果通过Javascript代码的回调函数返回给Web端。
  5. 在Web端中通过回调函数获取到Native端返回的数据,并进行相应的处理。

通过这种方式,JSBridge实现了Webview和Native之间的双向通信,可以使得Web应用和Native应用之间实现无缝的交互。

JS Bridege的常见实现方式

WebView方式:通过WebView加载一个网页,然后在网页中通过JavaScript代码调用Native代码来实现功能。这种方式适用于需要在网页中嵌入Native功能的场景

URL Scheme:通过定义特定的 URL Scheme,JavaScript 可以通过修改页面链接的方式触发原生应用执行相应操作。原生应用可以通过拦截特定的 URL Scheme 来响应 JavaScript 的请求。

// 封装JS-bridge
const sdk = {
  invoke(url, data = {}, onSuccess, onError){
    const iframe = document.createElement('iframe')
    iframe.style.visibility = 'hidden'
    document.body.appendChild(iframe)
    iframe.onload = () => {
      const content = iframe1.contentWindow.document.body.innerHTML
      onSuccess(JSON.parse(content))
      iframe.remove()
    }
    iframe.onError = () => {
      onError()
      iframe.remove()
    }
    iframe.src = `my-app-name://${url}?data=${JSON.stringify(data)}`
  },
  fn1(data, onSuccess, onError) {
    this.invoke('api/fn1',data, onSuccess, onError)
  },
  fn2(data, onSuccess, onError) {
    this.invoke('api/fn2',data, onSuccess, onError)
  },
}

sdk.fn1()
7. requestIdleCallbcak和requestAnimationFrame有什么区别?

requestIdleCallback并不是一个常用的API,由于React 16版本之后引入React fiber引起关注。React fiber可以将组件树转换为链表,可以分段渲染;渲染时可以暂停,去执行其他高优先级任务,空闲时再继续渲染,这样就会存在如何判断空闲的问题?

requestIdleCallback该方法会在浏览器空闲时调用回调函数,即在主线程上没有其他高优先级任务需要执行时。它会尽量在空闲时间内执行,以避免对用户交互和页面渲染的干扰。

区别:

requestAnimationFrame每次渲染完都会执行,高优;requestIdleCallback空闲时才会执行,低优。

// 演示 requestIdleCallbcak和requestAnimationFrame
<button id= "btn1">change</button>
<div id="box"></div>

<script>
  const box = document.getElementById('box')
  document.getElementById('btn1').addEvenetListener('click', () => {
    let curwidth = 100
    const maxWidth = 400
    
    function addWidth() {
      curWidth = curWidth + 3
      box.style.width = `${curWidth}px`
      if(curWidth < maxWidth) {
          // window.requestAnimationFrame(addWidth)// 执行时间不用自己控制
          window.requestIdleCallback(addWidth)// 空闲时执行,此处一直空闲效果不明显
      }
    }
    addwidth()
  })
</script>

扩展:它们是宏任务还是微任务?

两者都是宏任务;要等到DOM渲染完才执行,肯定宏任务

window.onload=() => {
  console.info('start')
  setTimeout(() => {
    console.info('timeout')
  })
  window.requestAnimationFrame(() =>{
    console.info('requestAnimationFrame')
  })
  window.requestIdleCallback(() =>{
    console.info('requestIdleCallback')
  })
  console.info('end')
}
// start
// end
// timeout
// requestAnimationFrame  高优,不管位置总会在requestIdleCallback前执行
// requestIdleCallback

在 requestIdleCallback 中进行 DOM 操作可能导致页面再次重绘,因为此时页面布局已经完成,对 DOM 的修改可能会触发重新计算布局和绘制; 相比之下,requestAnimationFrame 更适合执行需要频繁重绘页面的任务,包括动画和其他与绘制相关的操作。在 requestAnimationFrame 回调函数中进行 DOM 操作可以确保这些操作在下一帧绘制之前完成,避免了不必要的重绘和性能损耗。

8. Vue每个生命周期都做了什么?

Vue 3生命周期:

beforeCreate:

创建一个空白的Vue实例;data method尚未被初始化,不可使用;

created:

Vue实例初始化完成,完成响应式绑定;data method都已经初始化完成,可调用

尚未开始渲染模板;此时可执行和JS模型有关,但是与DOM节点没有关系的事情

beforeMount:

编译模板,调用render生成vdom;还没有开始渲染DOM(vdom是JS级别)

mounted:

完成DOM渲染,组件创建完成;开始由"创建阶段"进入"运行阶段"(更新,销毁)

beforeUpdate:

data发生了变化后;准备更新DOM(尚未更新DOM)

updated:

data发生变化,且DOM更新完成;(不要在updated中修改data,可能会导致死循环

beforeUnmount:

组件进入销毁阶段(尚未销毁,可正常使用);可移除、解绑一些全局事件、自定义事件

unmounted:

组件被销毁;所有的子组件也都被销毁

扩展1:keep-alive组件的生命周期

onActiveed:缓存组件被激活,当组件被插入到 DOM 中时调用

onDeactived:缓存组件被隐藏,当组件从 DOM 中被移除时调用

<script setup>
import { onActivated, onDeactivated } from 'vue'

onActivated(() => {
  // 调用时机为首次挂载
  // 以及每次从缓存中被重新插入时
})

onDeactivated(() => {
  // 在从 DOM 上移除、进入缓存
  // 以及组件卸载时调用
})
</script>

扩展2:Vue什么时候有操作DOM比较合适?

mounted和updated都不能保证子组件全部挂载完成

使用$nextTick渲染DOM

扩展3:Ajax应该放在哪个生命周期?

推荐:mounted:将 Ajax 请求放在mounted生命周期钩子函数中的好处是,在页面渲染完成后立即发起请求,以获取数据并更新组件。这样可以确保组件被正确渲染后再进行数据请求,避免出现数据未就绪时的 UI 空白或异常情况.

最后

四轮技术面+一轮hr面结束,学习到了不少,面试也是一个学习检测自己的过程,面试前大概复习了 一周的时间,把以前的代码看了一下,字节跳动比较注重算法,面试前刷了下leetcode和剑指offer, 也刷了些在牛客网上的面经。大概就说这些了,写代码去了~

祝大家都能收获大厂offer~

篇幅有限,仅展示部分内容

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
.cn/img_convert/ac0b1c2376da47d727e0dc8a77e76478.png)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-xBoq4CY4-1713070807933)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 23
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值