Skip to content
On this page

浏览器

浏览器安全

XSS跨站脚本攻击

定义

  • XSS攻击指的是跨站脚本攻击,是一种代码注入攻击。攻击者通过在网站注入恶意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如cookie等
  • XSS的本质是因为网站没有对恶意代码进行过滤,与正常的代码混合在一起了,浏览器没有办法分辨哪些脚本是可信的,从而导致了恶意代码的执行

攻击者可以通过XSS攻击进行如下操作:

  • 获取页面数据,如DOM、cookie、localStorage
  • DOS攻击,发送合理请求,占用服务器资源,从而让用户无法访问服务器
  • 破坏页面结构
  • 流量劫持

攻击类型

  • 存储型:指的是恶意脚本会存储在目标服务器上,当浏览器请求数据时,脚本从服务器传回并执行
  • 反射型:指的是攻击者诱导用户访问一个带有恶意代码的URL后,服务器端接收数据后处理,然后把带有恶意代码的数据发送到浏览器端,浏览器端解析这段带有XSS代码的数据后当做脚本执行,最终完成XSS攻击
  • DOM型:指的通过修改页面的DOM节点形成的XSS

防御

  • 对一些敏感信息进行保护,比如cookie使用http-only,使得脚本无法获取,也可以使用验证码,避免脚本伪装成用户执行一些操作
  • 建立一个白名单,告诉浏览器哪些外部资源可以加载和执行,从而防止恶意代码的注入攻击
  • 替换特殊符号,如< 变为&lt; ,>变为&gt; ,这样script就会变成&lt;script&gt; ,直接显示,而不会作为脚本执行,并且前端需要替换,后端也需要替换,都做总不会出错

CSRF攻击

定义

  • CSRF攻击指的是跨站请求伪造攻击,攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求
  • 如果用户在被攻击网站中保存了登录状态,那么攻击者就可以利用这个登录状态,绕过后台的用户验证,冒充用户向服务器执行一些操作
  • CSRF攻击的本质是利用cookie会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充

攻击类型

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

防御

  • 进行同源检测:服务器根据 http 请求头中 origin 或者 referer 信息来判断请求是否为允许访问的站点,从而对请求进行过滤
  • 使用Token验证:服务器向用户返回一个随机数 Token ,当网站再次发起请求时,在请求参数中加入服务器端返回的 token ,然后服务器对这个 token 进行验证
  • 对Cookie进行双重验证:服务器在用户访问网站页面时,向请求域名注入一个Cookie,内容为随机字符串,然后当用户再次向服务器发送请求的时候,从 cookie 中取出这个字符串,添加到URL 参数中,然后服务器通过对 cookie 中的数据和参数中的数据进行比较,来进行验证

网络劫持

  1. DNS劫持:比如输入京东,结果跳转到淘宝就是DNS劫持
  • 防范:DNS强制解析,通过修改运营商的本地DNS记录来引导用户流量到缓存服务器302跳转的方式,通过监控网络出口的流量,分析判断哪些内容是可以进行劫持处理的,再对劫持处理的内存发起302跳转的回复,引导用户获取内容
  1. HTTP劫持:访问谷歌但是一直有贪玩蓝月的广告就是HTTP劫持,由于HTTP是明文传输的,所以运营商会修改http响应内容(添加广告)
  • 防范:全站HTTPS

线程与进程

定义

  • 本质上来说,线程和进程都是CPU工作时间片的一个描述,线程是进程总更小的单位,进程是CPU调度的最小单位。

特点

  • 进程中的任意一个线程出错都会导致整个进程崩溃
  • 线程之间共享进程中的数据
  • 当一个进程关闭后,操作系统会回收进程所占用的内存
  • 进程之间的内容相互隔离

简单理解

  • 进程可以理解为是一个工厂有他自己独立的资源,这些独立的资源就是系统分配的内存
  • 而这些工厂之间是相互独立的,也就是说进程之间是相互独立的
  • 线程的话就可以理解为是工厂中的工人,多个工人协作完成任务,其实就是多个线程在进程中协作完成任务
  • 一个工厂里面有一个或者多个工人,也就是说一个线程有一个或者多个进程
  • 工人之间共享空间,其实就是同一个进程下的各个线程之间共享内存空间,包括代码段、数据集、堆等

区别

  • 进程可以看做是独立的应用,线程不可以
  • 资源方面:进程是cpu资源分配的最小资源,线程是cpu调度的最小单位
  • 通信方面:线程之间可以贡献同一进程中的资源,进程则相互隔离,通信需要借助进程间通信

进程之间的通信方式

  1. 管道通信:管道就是操作系统在内核中开辟的一段缓冲区,进程1可以将需要交互的数据拷贝到这段缓冲区,进程2就可以读取了
  2. 消息队列通信:消息队列就是一个消息的列表,用户可以在消息队列中添加消息、读取消息等
  3. 信号量通信
  4. 信号通信
  5. 共享内存通信
  6. 套接字通信

死锁

  • 所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。
  • 原因:竞争不可剥夺资源,例如:系统中只有一台打印机,可供进程P1使用,假定P1已占用了打印机,若P2继续要求打印机打印将阻塞。
  • 预防:资源一次性分配。

如何实现浏览器内多个标签之间内的通信

  • 使用websocket协议,因为 websocket 协议可以实现服务器推送,所以服务器就可以用来当做这个中介者,实现通信
  • 使用localStorage的方式,我们可以在一个标签页对 localStorage 的变化事件进行监听,然后当另一个标签页修改数据的时候,我们就可以通过这个监听事件来获取到数据

Chrome进程架构

An image 浏览器是多进程的,其实打开任务管理器就能看出来,如果有多个标签页,就有多个进程,当然浏览器有自己的优化机制,打开多个标签页之后,有些进程就会被合并。

浏览器包含的进程:

  • 1个浏览器主进程:主要负责界面显示,用户交互,子进程管理,提供存储等功能
  • 一个GPU进程:核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,V8引擎就是在这个进程中
  • 1个网络进程:Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求
  • 多个渲染进程:主要负责页面的网络资源加载,默认每个标签页一个
  • 多个插件进程:主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响

浏览器渲染进程的线程有哪些

  1. GUI渲染线程:负责渲染浏览器页面,解析HTML、CSS、构建DOM树等,当界面需要重绘或者回流时,该线程就会执行
  2. JS引擎线程:JS内核,负责处理JavaScript脚本程序,运行代码,一个Tab页中无论什么时候都只有一个JS引擎线程在运行JS程序,JS引擎线程跟GUI渲染线程是互斥的,所以如果JS执行时间过程,会造成页面渲染不连贯,导致页面渲染阻塞(JS阻塞页面加载),可以考虑将JS引入放在最后,或者使用异步加载JS
  3. 事件触发线程:事件触发线程属于浏览器而不是JS引擎,用来控制事件循环,负责管理事件队列,当事件符合触发条件时,将事件添加到待处理队列(宏任务)的队尾,等待JS引擎的处理
  4. 定时器触发线程:负责管理定时器(setTimeout、setInterval等),当定时器到期时,将定时器回调函数添加到待处理队列(宏任务)的队尾,等待JS引擎的处理
  5. 异步HTTP请求线程:负责发送异步请求(XMLHttpRequest等),XMLHttpRequest连接后通过浏览器新开一个线程请求,检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将回调函数放入事件队列中(待处理队列-宏任务或微任务),等待JS引擎空闲后执行

浏览器渲染

浏览器渲染的过程

  1. 首先解析收到的文档,根据文档构建一棵DOM树,DOM树是由DOM元素及属性节点组成的
  2. 然后对CSS进行解析,生成CSSOM规则书
  3. 根据DOM树和CSSOM规则树构建渲染树,渲染树的节点被称为渲染对象,渲染对象是一个包含有颜色大小等属性的矩形,渲染对象和DOM元素相对应,但是这种对应不是一对一的,不可兼得DOM元素不会被插入渲染树,还有一些DOM元素对应 几个课件元素,它们一般是一些具有复杂结构的元素,无法用一个矩形来描述
  4. 当渲染对象被创建并添加到树中,它们并没有位置和大小,所以当浏览器生成渲染树之后,就会根据渲染树来进行布局(回流),这一阶段浏览器要做的事情就是个弄清楚各个节点在也没能在的确切位置和大小,通常这个行为也称为自动重排
  5. 布局阶段结束后就是绘制阶段,遍历渲染树并调用渲染对象的paint方法将它们的内容显示在屏幕上,绘制使用UI基础组件

浏览器渲染的优化

从以下四个方面优化。

针对JavaScript

  • 因为JS会阻塞HTML解析和CSS解析,所以对其加载方式进行优化
  • 尽量放在body最后
  • body中间尽量不写script标签
  • 给JS标签添加async标签异步加载或者添加defer标签让其在DOM树解析好后加载

针对CSS

  • 使用@importGUI渲染线程会暂时停止渲染
  • 在开发过程中,导入外部样式使用link,而不用@import

针对DOM树CSSOM树

  • HTML文件代码层级不要太深
  • 使用语义化标签,避免不标准语义化的特殊处理

减少回流和重绘

  • 操作DOM时,尽量在低层级的DOM节点进行操作
  • 不使用table布局,因为一个小的改动会让整个table进行重新布局
  • 使用CSS表达式
  • 不频繁操作元素的样式,对于静态页面,可以修改类名,而不是样式
  • 使用absolute或则fixed使元素脱离文档流,这样发生变化不会影响其他元素
  • DOM的多个读写操作放在一起

什么是回流(重排)和重绘

  • 重排/回流(Reflow):当DOM的变化影响了元素的几何信息,浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。表现为重新生成布局,重新排列元素。
  • 重绘(Repaint): 当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。表现为某些元素的外观被改变。
  • 简单来说,DOM 的变化影响到了元素的几何属性比如宽高,浏览器重新计算元素的几何属性,其他元素的几何属性也会受到影响,浏览器需要重新构造渲染树,这个过程称之为重排,浏览器将受到影响的部分重新绘制在屏幕上的过程称为重绘。
  • 单单改变元素的外观,肯定不会引起网页重新生成布局,但当浏览器完成重排之后,将会重新绘制受到此次重排影响的部分。重排和重绘代价是高昂的,它们会破坏用户体验,并且让UI展示非常迟缓,而相比之下重排的性能影响更大,在两者无法避免的情况下,一般我们宁可选择代价更小的重绘。
  • 『重绘』不一定会出现『重排』,『重排』必然会出现『重绘』。

如何触发回流(重排)和重绘

  • 添加或者删除或更新可见的 DOM 元素
  • 通过display: none隐藏一个DOM节点-触发重排和重绘
  • 通过visibility: hidden隐藏一个DOM节点-只触发重绘,因为没有几何变化
  • 移动或者给页面中的DOM节点添加动画
  • 添加一个样式表,调整样式属性
  • 元素尺寸位置的改变
  • 浏览器页面初始化
  • 浏览器窗口大小发生改变,重排一定导致重绘,重绘不一定导致重排

如何避免回流(重排)和重绘

  • 集中改变样式,不要一条一条的修改DOM样式
  • 不要把DOM节点的属性值放在循环里当成循环变量
  • 不在布局信息改变时做 DOM 查询
  • 使用 csstext,className 一次性改变属性
  • 使用 fragment
  • 对于多次重排的元素,比如说动画,使用绝对定位脱离文档流,使其不影响其他元素

渲染过程中遇到JS文件会如何处理

JavaScript的加载、解析与执行会阻塞文档的解析,也就是说在构建DOM树的时候HTML解析器若遇到了JS,那么会暂停文档的解析,将控制器移交给JS引擎,当JS引擎运行完毕之后,再继续解析文档。

什么情况下会阻塞渲染

  • 渲染的前提是生成渲染树,所以HTML与CSS肯定会阻塞渲染,JS会停止构建DOM,可以添加async让JS异步加载
  • css加载不会阻塞DOM树解析(异步加载时DOM照常构建),但是会阻塞渲染树渲染(渲染时需等css加载完毕,因为渲染树需要css信息)

浏览器缓存

浏览器缓存过程

An image

  1. 浏览器第一次加载资源,服务器返回200,浏览器从服务器下载资源文件并缓存资源文件与resoponse header,以供下次下载时对比使用
  2. 下一次加载资源时,由于强缓存的优先级比较高,先比较当前时间与上一次返回200之间的时间差,如果没有超过Cache-Control设置的max-age,则没有过期,命中强缓存,直接从本地读取资源,如果浏览器不支持HTTP1.1,则使用 expires 头判断是否过期
  3. 如果资源已经过期,则表明强缓存没有被命中,则开始协商缓存,协商缓存是服务器端的缓存策略,向服务器发送带有If-None-Match和If-Modified-Since的请求
  4. 服务器收到请求之后,优先根据Etag的值判断被请求的问价你有没有修改,Etag值一致,则没有修改,命中协商缓存,返回304,如果不一致,说明有改动,返回新的资源文件并携带新的Etag,返回200
  5. 如果服务器收到的请求没有Etag,则将If-Modified-Since和被请求文件最后修改时间作对比,如果一致则命中协商缓存,返回304,不一致返回last-modified和文件并返回200

浏览器资源缓存的位置

  • ServiceWorker
  • MemoryCache
  • DiskCache

强缓存和协商缓存

前端缓存: An image

强缓存

使用强缓存策略时,如果缓存资源有效,则直接使用缓存资源,不必再向服务器发起请求。强缓存是利用HTTP返回头中的Expires或者Cache-Control来控制的,用来表示资源的缓存时间。

  • Expires:用来指定资源到期时间,是服务器的具体的时间点,由于是响应消息头字段,所以服务器响应给浏览器该字段告诉客户点过期时间前浏览器可以直接从浏览器缓存取数据,不需要再次请求。
  • Cache-Control:是一个相对时间,代表资源的有效期,与是与客户端的时间进行比较,所以客户端与服务端时间偏差也不会有什么影响。
    • no-cache:每次请求需要校验服务器资源的新鲜度
    • max-age=31536000:浏览器在一年之内都不需要向服务器请求资源
  • 一般两种同时使用,Cache-Control优先级高于expires。

特点:

  • 1.不会向服务器发送网络请求,直接从缓存中读取资源
  • 2.请求返回200的状态码
  • 3.在devtools的network选项卡可以看到size显示from disk cache或from memory cache

协商缓存

  • 协商缓存就是强缓存失效后,浏览器携带资源的缓存标识向服务器发起请求,由服务器根据缓存标识决定是否继续使用缓存的过程。
  • 当浏览器发起请求验证资源时,如果资源没有做改变,那么服务端就会返回304状态码,并且更新浏览器现有缓存有效期。
  • 当资源失效时,返回200状态码和最新的资源。
  • 协商缓存可以通过设置两种HTTP Header实现:Last-Modified、ETag。

浏览器本地存储

所谓的浏览器缓存指的是浏览器将用户请求过的静态(Web)资源(如html页面,图片,js,数据等),存储到电脑本地磁盘中,当浏览器再次访问时,就可以直接从本地加载,不需要再去服务端请求了。优点:

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

浏览器的缓存规则:新鲜度(过期机制)+ 校验值(验证机制)两个维度。

sessionstorage和localstorage和cookie的区别

共同点

三者都是浏览器的本地存储。

区别

  • cookie是由服务器端写入的,而sessionstorage和localstorage都是由前端写入的
  • cookie的生命周期是由服务器端在写入时就设置好的,localstorage是写入就一直存在,除非手动清除,sessionstorage是页面关闭的时候就会自动清除。
  • cookie的存储空间比较小大概4KB,sessionstorage和localstorage存储空间比较大,大概5M
  • sessionstorage和localstorage和cookie数据共享都遵循同源原则,sessionstorage还限制必须是同一个页面

session和cookie

我们知道HTTP协议是无状态的,一次请求完成,不会持久化请求与相应的信息。那么,在购物车、用户登录状态、页面个性化设置等场景下,就无法识别特定用户的信息。这时Cookie就出现了。

Cookie是客户端保存用户信息的一种机制,将服务器发送到浏览器的数据保存在本地,下次向同一服务器再发起请求时被携带发送。对于Cookie,可以设置过期时间。

通常,Cookie用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。这样就解决了HTTP无状态的问题。

Cookie主要用于以下方面:

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)
  • Cookie存储在客户端,这就意味着,可以通过一些方式进行修改,欺骗服务器。针对这个问题,怎么解决呢?那就引入了Session。

session

Session代表服务器和客户端一次会话的过程。

维基百科这样解释道:在计算机科学领域来说,尤其是在网络领域,会话(session)是一种持久网络协议,在用户(或用户代理)端和服务器端之间创建关联,从而起到交换数据包的作用机制,session在网络协议(例如telnet或FTP)中是非常重要的部分。

对照Cookie,Session是一种在服务器端保存数据的机制,用来跟踪用户状态的数据结构,可以保存在文件、数据库或者集群中。

当在应用程序的Web页之间跳转时,存储在Session对象中的变量将不会丢失,而会在整个用户会话中一直存在下去。当客户端关闭会话,或者Session超时失效时会话结束。

目前大多数的应用都是用Cookie实现Session跟踪的。第一次创建Session时,服务端会通过在HTTP协议中返回给客户端,在Cookie中记录SessionID,后续请求时传递SessionID给服务,以便后续每次请求时都可分辨你是谁。

区别

  • 作用范围不同,Cookie 保存在客户端(浏览器),Session 保存在服务器端。
  • 存取方式的不同,Cookie只能保存 ASCII,Session可以存任意数据类型,比如UserId等。
  • 有效期不同,Cookie可设置为长时间保持,比如默认登录功能功能,Session一般有效时间较短,客户端关闭或者Session超时都会失效。
  • 隐私策略不同,Cookie存储在客户端,信息容易被窃取;Session存储在服务端,相对安全一些。
  • 存储大小不同, 单个Cookie 保存的数据不能超过 4K,Session可存储数据远高于Cookie。

禁用cookie会怎样

  • 方案一:拼接SessionId参数。在GET或POST请求中拼接SessionID,GET请求通常通过URL后面拼接参数来实现,POST请求可以放在Body中。无论哪种形式都需要与服务器获取保持一致。这种方案比较常见,比如老外的网站,经常会提示是否开启Cookie。如果未点同意或授权,会发现浏览器的URL路径中往往有"?sessionId=123abc"这样的参数。
  • 方案二:基于Token(令牌)。在APP应用中经常会用到Token来与服务器进行交互。Token本质上就是一个唯一的字符串,登录成功后由服务器返回,标识客户的临时授权,客户端对其进行存储,在后续请求时,通常会将其放在HTTP的Header中传递给服务器,用于服务器验证请求用户的身份。

Web性能优化

  • 降低请求量:合并资源,减少HTTP请求数量,用minify/gzip压缩,lazyLoad
  • 加快请求速度:预解析DNS,减少域名数,并行加载,CDN分发
  • 缓存:HTTP缓存请求,离线缓存manifest,离线数据缓存localStorage
  • 渲染:JS、CSS优化,加载顺序,服务端渲染

浏览器同源策略

所谓的“同源”指的是“三个相同”:协议相同、域名相同、端口相同。只有这三个完全相同,才算是同源。

同源策略的目的:是为了保证用户信息的安全,防止恶意的网站窃取数据。

比如,用户访问了银行网站A,再去浏览其他网站,如果其他网站可以读取A的Cookie,隐私信息便会泄露。更可怕的是,通常Cookie还用来保存用户登录状态,会出现冒充用户行为。因此,"同源策略"是必需的,如果Cookie可以共享,互联网就毫无安全可言了。

同源策略保证了一定的安全性,但在某些场景下也带来了不便,比如常见的跨域请求问题。

在HTML中,<a>,<form>, <img>, <script>, <iframe>, <link>等标签以及Ajax都可以指向一个资源地址,而所谓的跨域请求就是指:当前发起请求的域与该请求指向的资源所在的域不一样。同源即同域,三项有一项不同便会出现跨域请求。

浏览器会对跨域请求做出限制,因为跨域请求可能会被利用发动CSRF攻击。

CSRF(Cross-site request forgery),即“跨站请求伪造”,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。CSRF攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。

针对跨域请求通常有如下方法:

  • 通过Jsonp跨域
  • 通过跨域资源共享(CORS)
  • Nginx反向代理解决跨域
  • NodeJs中间件代理跨域

跨域

跨域:当前页面中的某个接口请求的地址和当前页面的地址如果协议、域名、端口其中有一项不同,就说明该接口跨域了。
跨域限制的原因:浏览器为了保证网页的安全,出的同源协议策略。
跨域解决方案:

  • cors:通过设置后端允许跨域实现
  • nodejs中间件、nginx反向代理
  • JSONP
  • document.domain + iframe跨域
  • postmessage:H5新增API,通过发送和接收API实现跨域通信
  • 后端在头部信息里设置安全域名

跨域场景:前后端分离式开发、调用第三方接口。

JSONP

JSONP核心原理:利用script标签可以无视同源策略的性质,利用script访问服务器端的js文件,服务器端的js文件可以利用我们在访问时填写的url后面携带的参数,来返回不同的信息,因此这种方式只能适用于GET请求。

JSONP缺点:只支持GET请求,因为script标签只能使用GET请求,并且需要后端配合返回指定格式的数据。

js
const jsonp = ({ url, params, callbackName }) => {
    const generateUrl = () => {
        let dataSrc = ''
        for (let key in params) {
            if (params.hasOwnProperty(key)) {
                dataSrc += `${key}=${params[key]}&`
            }
        }
        dataSrc += `callback=${callbackName}`
        return `${url}?${dataSrc}`
    }
    return new Promise((resolve, reject) => {
        const scriptEle = document.createElement('script')
        scriptEle.src = generateUrl()
        document.body.appendChild(scriptEle)
        window[callbackName] = data => {
            resolve(data)
            document.removeChild(scriptEle)
        }
    })
}

CORS

  • 这个方案是在服务器端解决的,可以在服务器端设置cors
  • 设置响应头为 Access-Control-Allow-Origin:* 或者具体网址 来控制是那个具体网址可以访问或者是所有的都可以访问
  • 同时,我们的CORS请求分为两种,一种是简单请求,一种是预检请求:简单请求就是GET,POST,HEAD三种请求之一,而且HTTP头部字段为无自定义字段的请求,这种请求客户端只会与服务器之间进行一次通信;预见请求就是请求方式为GET,POST,HEAD之外的Mehod类型,并且请求头中包含了自定义头部,而且会向服务器发送application/json格式的数据,这种请求会发送两次请求在OPTION检测成功后再发送真正的请求

浏览器事件机制

什么是事件

事件是用户操作网页时发生的交互动作,比如 click/move, 事件除了用户触发的动作外,还可以是文档加载,窗口滚动和大小调整。事件被封装成一个 event 对象,包含了该事件发生时的所有相关信息( event 的属性)以及可以对事件进行的操作( event 的方法)。

如何阻止事件冒泡

event.stopPropagation()

事件代理

事件代理本质上是利用了浏览器事件冒泡的机制。因为事件在冒泡过程中会上传到父节点,父节点可以通过事件对象获取到目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件委托(事件代理)。

js
const div1 = document.getElementById('div1');
div1.addEventListener('click', (event) > {
    // 阻止默认行为防止连接进行跳转
    event.preventDefault();
    const target = event.target;
    // 获取点击的a标签
    if (target.nodeName = 'A') {
        alert(target.innerHTML);
   }
});

特点:

  • 减少内存消耗
  • 动态绑定事件

事件循环

An image 执行顺序:

  • 首先执行同步代码
  • 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
  • 执行所有微任务
  • 执行所有微任务,渲染DOM界面
  • 执行宏任务,如果里面有微任务则回到第三执行微任务的步骤
  • 继续下一轮事件循环

Node中的事件循环

浏览器垃圾回收机制

V8的垃圾回收机制

内存泄漏

以下操作会导致内存泄漏:

  • 使用未声明的变量而意外的创建了一个全局变量,而这个变量一直留在内存中无法回收
  • 设置了setInterval定时器,忘记取消
  • 获取DOM元素的引用,后面这个元素被删除,但是我们一直保留了这个引用,所以无法被回收
  • 不合理使用闭包,导致某些变量一直留在内存中