「2023」前端面试知识点复盘——浏览器篇

写在前面

哈喽,小伙伴们,我是泽南👨‍🎓。近期我会整理一系列针对前端面试的知识点供大家复习,不管你是准备跳槽还是正在面试,都赶快来恶补一下~

本系列文章主要以知识总结为主,不是面试题哈,主要目的是方便自己复习以及为大家提供便利,所以大部分知识点来源于网上,侵权联系删除。如有不对的地方欢迎大家评论区批评指正,一起进步!

此系列会陆续更新如下文章:

「2023」前端面试知识点复盘------浏览器篇

「2023」前端面试知识点复盘------计算机网络篇 (整理中。。。)

「2023」前端面试知识点复盘------JS篇 (整理中。。。)

「2023」前端面试知识点复盘------CSS篇 (整理中。。。)

「2023」前端面试知识点复盘------React篇 (整理中。。。)

「2023」前端面试知识点复盘------工程化篇(整理中。。。)

一、介绍一下你对浏览器内核的理解?

首先列举常见的浏览器内核都有些什么

浏览器/RunTime 内核(渲染引擎) JavaScript 引擎
Chrome webkit->blink V8
FireFox Gecko SpiderMonkey
Safari Webkit JavaScriptCore
Edge EdgeHTML Chakra(for JavaScript)
IE Trident JScript(IE3.0-IE8.0)
Opera Presto->blink Linear A(4.0-6.1)/ Linear B(7.0-9.2)/ Futhark(9.5-10.2)/ Carakan(10.5-)
Node.js - V8

再说说对浏览器内核的理解

浏览器内核是指浏览器用来解释和渲染网页的核心引擎。浏览器内核主要包括两部分:渲染引擎JavaScript 引擎

渲染引擎是负责解析 HTML 和 CSS 等网页元素,然后将它们转换为可视化的页面。渲染引擎通常会先解析 HTML,构建出 DOM(文档对象模型)树,然后根据 CSS 样式信息,计算出每个元素在页面上的位置和尺寸,最终将所有元素渲染到屏幕上。常见的渲染引擎包括 Blink、Gecko、WebKit 等。

JavaScript 引擎则负责解释和执行 JavaScript 代码,为网页提供动态交互和数据处理能力。常见的 JavaScript 引擎包括 V8、SpiderMonkey、JavaScriptCore 等。

最开始渲染引擎和 JS 引擎并没有区分的很明确,后来 JS 引擎越来越独立,内核就倾向于只指渲染引擎。

二、浏览器缓存机制

强缓存

使用强缓存策略时,如果缓存资源有效,浏览器会 从本地读取缓存资源并返回200 ,不必再向服务器发起请求。强缓存策略可以通过两种方式来设置,分别是 http 头信息中的 Expires 属性和 Cache-Control 属性。

注意: 当两种方式一起使用时,Cache-Control 的优先级要高于 Expires

  • ExpiresHTTP/1.0 的产物。值代表的是服务端的时间,并且 Expires 受限于本地时间,如果修改了本地时间,可能会造成缓存失效。
  • Cache-Control 出现于 HTTP/1.1,优先级高于 Expires 。该属性值表示资源会在 多少秒后过期,需要再次请求。
Cache-Control 属性

Cache-Control 首部字段是 HTTP/1.1 中定义缓存的字段,其用于控制缓存的行为,可以组合使用多种指令,多个指令之间可以通过 "," 分隔,例如:

yaml 复制代码
Cache-Control: max-age:0, s-maxage=3600, public
Cache-Control: no-cache

Cache-Control可取值:

  • private: 仅浏览器可以缓存
  • public:浏览器和代理服务器都可以缓存
  • max-age=xxx 过期时间,单位为秒
  • s-maxage指令 与 max-age 不同之处在于,其只适用于公共缓存服务器,比如资源从源服务器发出后又被中间的代理服务器接收并缓存。当使用 s-maxage 指令后,公共缓存服务器将直接忽略 Expiresmax-age指令的值
  • no-cache 不进行强缓存,但会有协商缓存
  • no-store 不强缓存,也不协商缓存
no-cache、no-store 的区别
  • no-cacheno-store 这两个指令在请求响应中都可以使用
  • no-store 是真正的不进行任何缓存,告知服务器和缓存服务器,我请求、响应的内容里有机密信息;
  • no-cache请求头中被使用时,表示强制使用协商缓存
  • no-cache响应头中被返回时,表示缓存服务器不能对资源进行缓存,客户端可以缓存资源,但每次使用缓存资源前都必须先向服务器确认其有效性

协商缓存

强缓存失效后,浏览器在请求头中携带响应的缓存Tag来向服务器发送请求,服务器根据对应的tag,来决定是否使用缓存。

在使用协商缓存时,会先向服务器发送一个请求,如果资源没有发生修改,则请求返回304状态,让浏览器使用本地缓存。如果资源发生修改,则返回修改后的内容

命中协商缓存条件:

  • Cache-Control: no-cache
  • max-age时间过期

缓存分为两种,「Last-Modified」「ETag」 。两者各有优势,并不存在谁对谁有绝对的优势,与上面所讲的强缓存两个Tag所不同。

下面详细说明 「Last-Modified」「ETag」 各自优势

  • 精准度上,ETag优于Last-Modified。由于 ETag 是按照内容给资源上标识,因此能准确感知资源的变化。而 Last-Modified 就不一样了,它在一些特殊的情况并不能准确感知资源变化,主要有两种情况:
    • 编辑了资源文件,但是文件内容并没有更改,这样也会造成缓存失效。
    • Last-Modified 能够感知的单位时间是秒,如果文件在 1 秒内改变了多次,那么这时候的 Last-Modified 并没有体现出修改了。
  • 性能上,Last-Modified优于ETag,也很简单理解,Last-Modified仅仅只是记录一个时间点,而 Etag需要根据文件的具体内容生成哈希值。
  • 另外,如果两种方式都支持的话,服务器会优先考虑ETag
用户行为对浏览器缓存的影响
  • 打开网页,地址栏输入地址: 查找 disk cache 中是否有匹配。如有则使用;如没有则发送网络请求。
  • 普通刷新 (F5):不使用强缓存,会判断协商缓存;且因为 TAB 并没有关闭,因此 memory cache 是可用的,会被优先使用(如果匹配的话)。其次才是 disk cache
  • 强制刷新 (Ctrl + F5):浏览器不使用缓存,服务器直接返回 200 和最新内容
为什么需要浏览器缓存?

使用浏览器缓存,有以下优点:

  • 减少了服务器的负担,提高了网站的性能
  • 加快了客户端网页的加载速度
  • 减少了多余网络数据传输

总结

  • 强缓存优先级高于协商缓存
  • 强缓存中Cache-Control 的优先级要高于 Expires;协商缓存的Last-Modified和ETag各有优势,但是服务器优先考虑ETag
  • 协商缓存不论命中与否都会发送一次请求
  • 强缓存返回200,协商缓存命中返回304
  • Ctrl+F5 会强制刷新会跳过所有缓存,而F5刷新跳过强缓存,但是会检查协商缓存。

三、浏览器的渲染和资源解析过程

浏览器渲染过程:

  • 构建DOM树:浏览器从上到下解析 HTML 文档生成DOM节点树;
  • 构建CSSOM树:浏览器解析遇到样式时,会进行异步下载,下载完成后构建 CSSOM树;
  • 值得一提的是,浏览器解析过程中遇到 图片时,会进行异步下载;当遇到不带 asyncdeferscript 时,会阻止解析HTML并进行下载和执行;
  • 并且CSSDOM渲染,JSDOM解析之间是有阻塞关系的;
  • 构建渲染树:根据DOM节点树和CSSOM树构建渲染树(Render);
  • 布局(Layout):根据渲染树将DOM节点树每一个节点布局在屏幕上的正确位置;【回流发生在这个阶段】
  • 绘制(Paint):绘制所有节点,为每一个节点适用对应的样式,绘制到屏幕上;【重绘发生在这个阶段】

浏览器渲染优化

  • 优化javaScriptJavaScript会阻塞HTML的解析,改变JavaScrip加载方式。

    • JavaScript放到body最后面
    • 尽量使用异步加载JS资源,这样不会阻塞DOM解析,如deferasync
  • 优化CSS加载,

    • CSS样式少,使用内嵌样式
    • 导入外部样式使用link,而不是@import,因为它会阻塞渲染。
  • 减少回流重绘

    • 避免频繁操作样式
    • 避免频繁操作DOM
    • 复杂动画使用定位脱离文当流
    • 使用transform替代动画

浏览器解析过程:

  • 浏览器开始解析HTML,此时document.readystateloading
  • 解析中遇到不带asyncdeferscript脚本时,需要等待 script脚本 下载完成并执行后,才会继续解析 HTML
  • 当文档完成解析,document.readyState变成 interactive,触发 DOMContentLoaded事件
  • 此时文档完全解析完成,浏览器可能还在等待如图片等内容加载,等这些内容完成载入并且所有异步脚本完成载入和执行,document.readyState变为 completewindow 触发 load 事件

浏览器解析 不同资源 时的行为

  • 浏览器解析遇到 CSS样式资源 时,CSS会异步下载,不会阻塞浏览器构建DOM树,但是会阻塞渲染,在构建渲染树时,会等css下载解析完毕后才进行(防止css规则不断变化)

  • 浏览器解析遇到 JS脚本资源 时,需要等待JS脚本下载完成并执行后才会继续解析HTML;但是当脚本加上deferasync时又不一样,defer是延迟执行,async是异步执行;

  • CSS加载会阻塞后面的的JS语句的执行,因为HTML5标准中有一项规定,浏览器在执行Script脚本前,必须保证当前的的外联CSS已经解析完成,因为JS可能会去获取或者变更DOMCSS样式,如果此时外联CSS还没解析好,获取到的结果就是不准确的;

  • 解析遇到 Img图片 时,直接异步下载,不会阻塞解析;下载完毕后用图片替换原有src的地方;

  • 总结:

    • CSS 会阻塞浏览器渲染;
    • JS 会阻塞浏览器解析;
    • CSS 会阻塞后面的JS执行;
    • IMG 不会阻塞;
async 和 defer 的解析过程
  • 浏览器解析到带 async 属性的 script 标签时,不会中断 html 解析,而是并行下载脚本;当脚本下载完成后,中断解析并执行脚本;
  • 浏览器解析到带 defer 属性的 script 标签时,不会中断 html 解析,而是并行下载脚本;当浏览器解析完HTML时、DOMContentLoaded 事件即将被触发时,此时再执行下载完成的脚本;
async 和 defer 的区别
  • asyncdefer 都仅对外部脚本有效
  • async 标志的脚本文件一旦加载完成就立即执行;而 defer 标志的脚本文件会在 HTML解析完成且DOM构建完毕后再执行;(也就是说defer是延迟执行,async是异步执行)
  • 如果有多个js脚本async标记的脚本哪个先下载结束,就先执行那个脚本。而defer标记则会按照js脚本书写顺序执行。
  • 如果同时使用asyncdefer属性,defer不起作用,浏览器行为由async属性决定。
  • DOMContentLoaded 事件会等待 defer 的脚本执行完后才触发;

css 加载会造成阻塞吗?

  • 在 Chrome 上,css 加载不会阻塞 HTML 解析,但会阻塞 DOM 渲染。

    • html 和 css 是同时解析的,所以不阻塞 HTML 解析。
    • html 生成 dom,css 生成 cssom,两者结合才能生成 render tree 渲染树,所以阻塞 DOM 渲染
  • css 加载会阻塞后面 js 的执行。 由于 JavaScript 是可操纵 DOM 和 css 的,如果在修改这些元素属性同时渲染界面,会造成冲突。

    • 为了防止渲染出现不可预期的结果,浏览器设置 GUI 渲染线程与 JavaScript 引擎为互斥的关系。所以 css 会阻塞后面 js 的执行。

四、Cookie、LocalStorage、SessionStorage区别

两个都可以用来存私密的东西比如用户身份,但是cookie数据保存在客户端,session数据保存在服务器端。

Cookie

  • 有效期通过ExpiresMax-Age两个属性来设置
  • 大小只有4kb
  • 跨域不能共享
  • 不安全,容易被劫持
  • 只存在请求头中
  • cookievalue如果用于保存用户登录态,应该将该值加密
  • http-only 属性设置了不能通过 JS 访问 Cookie,减少 XSS 攻击
  • secure 属性设置只能在https请求中携带
  • SameSite 属性规定浏览器不能在跨域请求中携带 Cookie,减少 CSRF 攻击
  • domain标识指定了哪些主机可以访问该Cookie的域名。如果设置为.google.com,则所有以google.com结尾的域名都可以访问该Cookie。注意第一个字符必须为.
分类 生命周期 存储容量 存储位置
cookie 默认保存在内存中,随浏览器关闭失效(如果设置过期时间,在到过期时间后失效) 4KB 保存在客户端,每次请求时都会带上
localStorage 理论上永久有效的,除非主动清除。 4.98MB(不同浏览器情况不同,safari 2.49M) 保存在客户端,不与服务端交互。节省网络流量
sessionStorage 仅在当前网页会话下有效,关闭页面或浏览器后会被清除。 4.98MB(部分浏览器没有限制) 同上

五、什么是同源策略?

同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说 Web 是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源指的是:协议端口号域名必须一致。

同源策略又分为以下两种:

  • DOM 同源策略:禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。
  • XMLHttpRequest 同源策略:禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。

六、如何解决跨越问题?

  • CORS
    • 跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain)上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域HTTP 请求。
  • JSONP
    • jsonp的原理就是利用 <script>标签没有跨域限制,通过<script>标签src属性,发送带有callback参数的GET请求,服务端将接口返回数据拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。
  • nginx 代理跨域:
    • nginx代理跨域,实质和CORS跨域原理一样,通过配置文件设置请求响应头Access-Control-Allow-Origin...等字段。
  • nodejs 中间件代理跨域,通过node开启一个代理服务器。

七、浏览器安全

XSS(跨站脚本攻击)

XSS 攻击指的是跨站脚本攻击,是一种代码注入攻击 。攻击者通过在网站注入恶意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如 cookie 等。

XSS 分为三种:存储型反射型文档型:

  • 存储型指的是恶意脚本会存储在目标服务器上,当浏览器请求数据时,脚本从服务器传回并执行。
  • 反射型指的是攻击者诱导用户访问一个带有恶意代码的 URL 后,服务器端接收数据后处理,然后把带有恶意代码的数据发送到浏览器端,浏览器端解析这段带有 XSS 代码的数据后当做脚本执行,最终完成 XSS 攻击。
  • 文档形XSS 攻击其实也是恶意脚本被作为请求URL的参数;浏览器解析后作为脚本执行,和反射形的区别在于:由前端JS取出 URL 中的恶意代码并执行

避免方式

  • 最普遍的做法就是转义过滤:对引号,尖括号,斜杠进行转义,让代码在html解析的过程中无法执行;过滤就是把 script标签给删除;
  • 利用 HttpOnlycookie 设置 httponly 后,会禁止 javascript 脚本来访问 cookie,这样,XSS攻击之后也无法获取用户的cookie
  • 其次就是使用 CSPCSP 也就是浏览器内容安全策略;只允许加载指定域的脚本及样式;

CSRF(跨站请求伪造)

CSRF 攻击的本质是利用 cookie 会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。

CSRF 攻击分为三种: GET 类型POST 类型链接类型

  • GET 类型的 CSRF 攻击,比如在网站中的一个 img 标签里构建一个请求,当用户打开这个网站的时候就会自动发起提交。
  • POST 类型的 CSRF 攻击,比如构建一个表单,然后隐藏它,当用户进入页面时,自动提交这个表单。
  • 链接类型的 CSRF 攻击,比如在 a 标签的 href 属性里构建一个请求,然后诱导用户去点击。

避免方式

  • 添加验证码验证
  • 使用token验证
  • 限制 cookie 不能作为被第三方使用
  • 进行同源检测

八、说说进程和线程

1、进程和线程的概念

  • 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位
  • 线程被包含在进程之中,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
  • 进程和线程之间的关系有以下四个特点:
    • (1)进程中的任意一线程执行出错,都会导致整个进程的崩溃。
    • (2)线程之间共享进程中的数据。
    • (3)当一个进程关闭之后,操作系统会回收进程所占用的内存, 当一个进程退出时,操作系统会回收该进程所申请的所有资源;即使其中任意线程因为操作不当导致内存泄漏,当进程退出时,这些内存也会被正确回收。
    • (4)进程之间的内容相互隔离。 进程隔离就是为了使操作系统中的进程互不干扰,每一个进程只能访问自己占有的数据,也就避免出现进程 A 写入数据到进程 B 的情况。正是因为进程之间的数据是严格隔离的,所以一个进程如果崩溃了,或者挂起了,是不会影响到其他进程的。如果进程之间需要进行数据的通信,这时候,就需要使用用于进程间通信的机制了。

2、进程间有哪些通讯方式

  • 信号:信号是进程间通信唯一的异步通信机制,因为可以在任何时候发送信号给某一个进程
  • 匿名管道:是一个内核缓冲区,进程以先进先出的方式从缓冲区中存取数据,管道一端的进程在缓冲区的末尾写数据,管道另一端的进程在缓冲区的头部读数据;只能用于父子关系的进程
  • 命名管道:正常的匿名管道需要父子关系,而命名管道提供了一个路径名与之相连,从而以文件的形式存在于文件系统中;就不受父子关系的限制;
  • 消息队列:消息队列本质上是保存在内核当中的消息链表
  • 共享内存:共享内存可以使得多个进程可以直接读写在同一块内存空间中,这是效率最高的进程间通信方式。
  • SocketSocket可以让跨网络的不同主机之间进行通讯,还可以在同主机上进程间通讯;

重排(回流)和重绘有什么区别

概念

  • 重排(回流):当渲染树中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。每个页面至少需要一次的回流,也就是页面第一次加载的时候。
  • 重绘:当页面元素样式的改变并不影响他在文档流中的位置时,这些属性只是影响元素的外观,风格,而不会影响布局的,浏览器会将新样式赋予给元素并重新绘制它,这个过程称之为重绘。

触发条件

引发重排的原因:

  • 页面首次渲染
  • 浏览器窗口大小发生改变
  • 元素尺寸或位置发生改变
  • 元素内容变化(文字数量或图片大小等)
  • 元素字体大小变化
  • 添加或者删除可见的 DOM 元素
  • 激活 CSS 伪类(:hover)
  • 设置 style 属性
  • 查询某些属性或调用某些方法

引发重绘的原因:

  • 字体颜色改变
  • 边框样式改变
  • 背景颜色、图片、定位、尺寸改变
  • 外边框颜色、样式
  • 外发光改变

如何避免触发回流和重绘

  • 避免频繁使用 style,而是采用修改class的方式。
  • 将动画效果应用到position属性为absolutefixed的元素上。
  • 使用 display: noneDOM离线处理,减少回流重绘次数。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘
  • 对于 resizescroll等进行防抖/节流处理。
  • 利用 CSS3transformopacityfilter这些属性可以实现合成的效果,也就是GPU加速。

前端路由

  • 在单页面web网页中, 有两种实现方式

    • hash模式:修改 URLhash 值,触发 hashchange 事件去渲染不同的内容,
    • history模式:利用history API 实现 URL地址改变,触发popstate事件渲染不同的内容

hash模式

1. 原理

​ 使用window.location.hash属性及窗口的onhashchange事件,可以实现监听浏览器地址hash值变化,执行加载相应的内容。

  1. 什么是hash值

    hash指的是地址中#号以及后面的字符,也称为散列值。hash也称作锚点,本身是用来做页面跳转定位的。如http://localhost/index.html#abc,这里的#abc就是hash;hash模式会失去原生锚点定位能力

  2. hash值不会发送到服务器

    hash值是不会随请求发送到服务器端的,所以改变hash,不会重新加载页面;

  3. 监听 hashchange 事件

    监听 window 的 hashchange 事件,当散列值改变时,可以通过 location.hash 来获取和设置hash值

  4. hash值变化反应到地址栏

    location.hash值的变化会直接反应到浏览器地址栏;

触发 hashchange 事件的几种情况
  • 浏览器地址栏 hash 值的变化(包括浏览器的前进、后退导致的),会触发 onhashchange 事件
  • html<a> 标签的属性 href 可以设置为页面的元素ID,点击后自动跳转并设置 hash

history模式

1. 原理
  • history 对象保存了当前窗口访问过的所有页面网址。history 对象发生改变时,只会改变页面的路径,不会刷新页面。
  • 每当 history 对象出现变化时,就会触发 popstate事件。
  • window.history 属性指向 History 对象,它表示当前窗口的浏览历史。当发生改变时,只会改变页面的路径,不会刷新页面。
  • History 对象保存了当前窗口访问过的所有页面网址。通过 history.length 可以得出当前窗口一共访问过几个网址。
  • 由于安全原因,浏览器不允许脚本读取这些地址,但是允许在地址之间导航。
  • 浏览器工具栏的前进后退按钮,其实就是对 History 对象进行操作。
2. 改变url方法

在 history 路由中,我们一定会使用window.history中的方法,常见的操作有:

  • back():后退到上一个路由;
  • forward():前进到下一个路由,如果有的话;
  • go(number):进入到任意一个路由,正数为前进,负数为后退;
  • pushState(obj, title, url):前进到指定的 URL,不刷新页面;
  • replaceState(obj, title, url): 用 url 替换当前的,不刷新页面;
  • popstate():调用History.back()History.forward()History.go()方法时才会触发。
popstate()

仅仅调用pushState()方法或replaceState()方法 ,并不会触发该事件。那该怎么办?如果我们能罗列出所有可能改变 history 的途径,然后在这些途径一一进行拦截,不也一样相当于监听了 history 的改变吗

对于一个应用而言,url 的改变只能由以下 3 种途径引起:

  • 点击浏览器的前进或者后退按钮;
  • 点击 a 标签;
  • 在 JS 代码中直接修改路由

第 2 和第 3 种途径可以看成是一种,因为 a 标签的默认事件可以被禁止,进而调用 JS 方法。关键是第 1 种,HTML5 规范中新增了一个 onpopstate 事件,通过它便可以监听到前进或者后退按钮的点击。

实现原理

hash模式 和 history模式 详细对比

hash模式 和 history模式 使用场景

  • 一般场景下,hash 和 history 都可以,除非你更在意颜值,# 符号夹杂在 URL 里看起来确实有些不太美丽。
  • 如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用history.pushState API 来完成URL 跳转而无须重新加载页面。不过这种模式要玩好,还需要后台配置支持。
  • 所以呢,要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。

事件机制

事件流

事件流分为三个阶段:捕获阶段目标阶段冒泡阶段

过程如下:

  1. 捕获阶段:事件从最外层的节点,也就是文档对象开始,逐级向下传播,直到事件的目标节点上。
  2. 目标阶段:事件到达目标节点,触发目标节点上的事件处理函数。
  3. 冒泡阶段:事件从目标节点开始,逐级向上传播,直到到达最外层节点(文档对象)

事件冒泡、事件捕获

  • 冒泡事件:是指子元素向父元素传递的过程(从里到外)
  • 捕获事件:是指父元素向子元素传递的过程(从外到里)

如何阻止事件

  • 阻止冒泡捕获事件: e.stopPropagation();e.stopImmediatePropagation()

    • 但是前者只会阻止冒泡和捕获
    • 而后者除此之外还会阻止该元素的其它事件发生;(比如元素绑定了多个捕获事件)。
  • 阻止默认事件: e.preventDefault();(比如a标签的跳转事件)

对事件委托的理解

利用浏览器事件冒泡机制。事件在冒泡的过程中会传到父节点,并且父节点可以通过事件对象获取到目标节点,可以吧子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件

事件循环

JavaScript的任务分为两种 同步异步

  • 同步任务:在主线程上排队执行的任务,只有一个任务执行完毕,才能执行下一个任务,
  • 异步任务:不进入主线程,而是放在任务队列中,若有多个异步任务则需要在任务队列中排队等待,任务队列类似于缓冲区,任务下一步会被移到执行栈然后主线程执行调用栈的任务。

事件循环:

  • 同步任务异步任务分别进入不同的执行"场所"; 同步任务进入主线程,异步任务进入Event Table并注册回调函数。
  • 当指定的事情完成时,Event Table会将这个函数移入任务队列(task quene),等待主线程的任务执行完毕;
  • 当栈中的代码执行完毕,执行栈(call stack)中的任务为空时,就会读取任务队列(task quene)中的任务,去执行对应的回调;
  • 如此循环,就形成js的事件循环机制(Event Loop)

宏任务 (MacroTask|Task)微任务 (MicorTask)

  • 宏任务:script全部代码setTimeoutsetIntervalI/OUI渲染
  • 微任务:Promise.thenProcess.nexTick(Node独有)MutationObserver

执行顺序

  • 执行宏任务中的同步代码,遇到宏任务或微任务,分别放入对应的任务队列,等待执行。
  • 当所有同步任务执行完毕后,执行栈为空,首先执行微任务队列中的任务
  • 微任务执行完毕后,检查这次执行中是否产生新的微任务,如果存在,重复执行步骤,直到微任务执行完毕。
  • 开始下一轮Event Loop,执行宏任务中的代码

为什么JavaScript是单线程的,与异步冲突吗?

补充:JS中其实是没有线程概念的,所谓的单线程也只是相对于多线程而言。JS的设计初衷就没有考虑这些,针对JS这种不具备并行任务处理的特性,我们称之为"单线程"。

JS的单线程是指一个浏览器进程中只有一个JS的执行线程,同一时刻内只会有一段代码在执行。

举个通俗例子,假设JS支持多线程操作的话,JS可以操作DOM,那么一个线程在删除DOM,另外一个线程就在获取DOM数据,这样子明显不合理,这算是证明之一。

来看段代码👇

javascript 复制代码
function foo() {
    console.log("first");
    setTimeout(( function(){
        console.log( 'second' );
    }),5);
}
 
for (var i = 0; i < 999; i++) {
    foo();
}

打印结果就是首先是很多个first,然后再是second。

异步机制是浏览器的两个或以上常驻线程共同完成的,举个例子,比如异步请求由两个常驻线程,JS执行线程和事件触发线程共同完成的。

  • JS执行线程发起异步请求(浏览器会开启一个HTTP请求线程来执行请求,这时JS的任务完成,继续执行线程队列中剩下任务)
  • 然后在未来的某一时刻事件触发线程监视到之前的发起的HTTP请求已完成,它就会把完成事件插入到JS执行队列的尾部等待JS处理

再比如定时器触发(settimeout和setinterval) 是由浏览器的定时器线程 执行的定时计数,然后在定时时间把定时处理函数的执行请求插入到JS执行队列的尾端(所以用这两个函数的时候,实际的执行时间是大于或等于指定时间的,不保证能准确定时的)。

所以这么说,JS单线程与异步更多是浏览器行为,之间不冲突。补充说明:浏览器是多进程

相关推荐
上海运维Q先生32 分钟前
面试题整理14----kube-proxy有什么作用
运维·面试·kubernetes
叫我菜菜就好36 分钟前
【Flutter_Web】Flutter编译Web第三篇(网络请求篇):dio如何改造方法,变成web之后数据如何处理
前端·网络·flutter
NoneCoder41 分钟前
CSS系列(26)-- 动画性能优化详解
前端·css·性能优化
滚雪球~42 分钟前
@vue/cli启动异常:ENOENT: no such file or directory, scandir
前端·javascript·vue.js
GDAL1 小时前
vue3入门教程:ref函数
前端·vue.js·elementui
GISer_Jing1 小时前
Vue3状态管理——Pinia
前端·javascript·vue.js
好开心331 小时前
axios的使用
开发语言·前端·javascript·前端框架·html
Domain-zhuo1 小时前
Git常用命令
前端·git·gitee·github·gitea·gitcode
开发者每周简报2 小时前
求职市场变化
人工智能·面试·职场和发展
菜根Sec2 小时前
XSS跨站脚本攻击漏洞练习
前端·xss