写在前面
哈喽,小伙伴们,我是泽南👨🎓。近期我会整理一系列针对前端面试的知识点供大家复习,不管你是准备跳槽还是正在面试,都赶快来恶补一下~
本系列文章主要以知识总结
为主,不是面试题哈,主要目的是方便自己复习以及为大家提供便利,所以大部分知识点来源于网上,侵权联系删除。如有不对的地方欢迎大家评论区批评指正,一起进步!
此系列会陆续更新如下文章:
「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。
Expires
是HTTP/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
指令后,公共缓存服务器将直接忽略Expires
和max-age
指令的值no-cache
不进行强缓存,但会有协商缓存no-store
不强缓存,也不协商缓存
no-cache、no-store 的区别
no-cache
和no-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
树;- 值得一提的是,浏览器解析过程中遇到 图片时,会进行异步下载;当遇到不带
async
和defer
的script
时,会阻止解析HTML
并进行下载和执行;- 并且
CSS
和DOM
渲染,JS
和DOM
解析之间是有阻塞关系
的;- 构建渲染树:根据
DOM
节点树和CSSOM
树构建渲染树(Render
);- 布局(
Layout
):根据渲染树将DOM
节点树每一个节点布局在屏幕上的正确位置;【回流发生在这个阶段】- 绘制(
Paint
):绘制所有节点,为每一个节点适用对应的样式,绘制到屏幕上;【重绘发生在这个阶段】
浏览器渲染优化
-
优化
javaScript
,JavaScript
会阻塞HTML的解析,改变JavaScrip
加载方式。- 将
JavaScript
放到body
最后面 - 尽量使用异步加载
JS
资源,这样不会阻塞DOM
解析,如defer
、async
- 将
-
优化CSS加载,
- CSS样式少,使用内嵌样式
- 导入外部样式使用
link
,而不是@import
,因为它会阻塞渲染。
-
减少回流重绘
- 避免频繁操作样式
- 避免频繁操作DOM
- 复杂动画使用定位脱离文当流
- 使用
transform
替代动画
浏览器解析过程:
- 浏览器开始
解析HTML
,此时document.readystate
为loading
- 解析中遇到不带
async
和defer
的script脚本
时,需要等待script脚本
下载完成并执行后,才会继续解析HTML
;- 当文档完成解析,
document.readyState
变成interactive
,触发DOMContentLoaded事件
- 此时文档完全解析完成,浏览器可能还在等待如图片等内容加载,等这些内容完成载入并且所有异步脚本完成载入和执行,
document.readyState
变为complete
,window
触发load
事件
浏览器解析 不同资源 时的行为
-
浏览器解析遇到
CSS样式资源
时,CSS
会异步下载,不会阻塞浏览器构建DOM
树,但是会阻塞渲染,在构建渲染树时,会等css
下载解析完毕后才进行(防止css
规则不断变化) -
浏览器解析遇到
JS脚本资源
时,需要等待JS脚本
下载完成并执行后才会继续解析HTML
;但是当脚本加上defer
与async
时又不一样,defer
是延迟执行,async
是异步执行; -
CSS
加载会阻塞后面的的JS
语句的执行,因为HTML5
标准中有一项规定,浏览器在执行Script
脚本前,必须保证当前的的外联CSS
已经解析完成,因为JS
可能会去获取或者变更DOM
的CSS
样式,如果此时外联CSS
还没解析好,获取到的结果就是不准确的; -
解析遇到
Img图片
时,直接异步下载,不会阻塞解析;下载完毕后用图片替换原有src的地方; -
总结:
CSS
会阻塞浏览器渲染;JS
会阻塞浏览器解析;CSS
会阻塞后面的JS
执行;IMG
不会阻塞;
async 和 defer 的解析过程
- 浏览器解析到带
async
属性的script
标签时,不会中断html
解析,而是并行下载脚本;当脚本下载完成后,中断解析并执行脚本; - 浏览器解析到带
defer
属性的script
标签时,不会中断html
解析,而是并行下载脚本;当浏览器解析完HTML
时、DOMContentLoaded
事件即将被触发时,此时再执行下载完成的脚本;
async 和 defer 的区别
async
和defer
都仅对外部脚本有效async
标志的脚本文件一旦加载完成就立即执行
;而defer
标志的脚本文件会在HTML解析完成且DOM构建完毕
后再执行;(也就是说defer
是延迟执行,async
是异步执行)- 如果有多个
js脚本
,async
标记的脚本哪个先下载结束,就先执行那个脚本。而defer
标记则会按照js
脚本书写顺序执行。 - 如果同时使用
async
和defer
属性,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
- 有效期通过
Expires
和Max-Age
两个属性来设置- 大小只有4kb
- 跨域不能共享
- 不安全,容易被劫持
- 只存在请求头中
cookie 要注意什么安全性?
cookie
的value
如果用于保存用户登录态,应该将该值加密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函数返回的数据。
- jsonp的原理就是利用
- nginx 代理跨域:
- nginx代理跨域,实质和CORS跨域原理一样,通过配置文件设置请求响应头
Access-Control-Allow-Origin
...等字段。
- nginx代理跨域,实质和CORS跨域原理一样,通过配置文件设置请求响应头
- nodejs 中间件代理跨域,通过node开启一个代理服务器。
七、浏览器安全
XSS(跨站脚本攻击)
XSS
攻击指的是跨站脚本攻击,是一种代码注入攻击 。攻击者通过在网站注入恶意脚本,使之在用户的浏览器上运行,从而盗取用户的信息如 cookie
等。
XSS
分为三种:存储型
,反射型
和 文档型
:
存储型
指的是恶意脚本会存储在目标服务器上,当浏览器请求数据时,脚本从服务器传回并执行。反射型
指的是攻击者诱导用户访问一个带有恶意代码的 URL 后,服务器端接收数据后处理,然后把带有恶意代码的数据发送到浏览器端,浏览器端解析这段带有 XSS 代码的数据后当做脚本执行,最终完成 XSS 攻击。文档形
的XSS
攻击其实也是恶意脚本被作为请求URL
的参数;浏览器解析后作为脚本执行,和反射形的区别在于:由前端JS
取出URL
中的恶意代码并执行
避免方式
- 最普遍的做法就是
转义
和过滤
:对引号,尖括号,斜杠进行转义,让代码在html
解析的过程中无法执行;过滤就是把script
标签给删除; - 利用
HttpOnly
:cookie
设置httponly
后,会禁止javascript
脚本来访问cookie
,这样,XSS
攻击之后也无法获取用户的cookie
; - 其次就是使用
CSP
:CSP
也就是浏览器内容安全策略;只允许加载指定域的脚本及样式;
CSRF(跨站请求伪造)
CSRF
攻击的本质是利用 cookie
会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。
CSRF
攻击分为三种: GET 类型
、POST 类型
、 链接类型
- GET 类型的 CSRF 攻击,比如在网站中的一个 img 标签里构建一个请求,当用户打开这个网站的时候就会自动发起提交。
- POST 类型的 CSRF 攻击,比如构建一个表单,然后隐藏它,当用户进入页面时,自动提交这个表单。
- 链接类型的 CSRF 攻击,比如在 a 标签的 href 属性里构建一个请求,然后诱导用户去点击。
避免方式
- 添加验证码验证
- 使用
token
验证 - 限制
cookie
不能作为被第三方使用 - 进行同源检测
八、说说进程和线程
1、进程和线程的概念
线程
是程序执行的最小单位,而进程
是操作系统分配资源的最小单位线程
被包含在进程之中,一个进程
中可以并发多个线程
,每条线程
并行执行不同的任务。- 进程和线程之间的关系有以下四个特点:
- (1)进程中的任意一线程执行出错,都会导致整个进程的崩溃。
- (2)线程之间共享进程中的数据。
- (3)当一个进程关闭之后,操作系统会回收进程所占用的内存, 当一个进程退出时,操作系统会回收该进程所申请的所有资源;即使其中任意线程因为操作不当导致内存泄漏,当进程退出时,这些内存也会被正确回收。
- (4)进程之间的内容相互隔离。 进程隔离就是为了使操作系统中的进程互不干扰,每一个进程只能访问自己占有的数据,也就避免出现进程 A 写入数据到进程 B 的情况。正是因为进程之间的数据是严格隔离的,所以一个进程如果崩溃了,或者挂起了,是不会影响到其他进程的。如果进程之间需要进行数据的通信,这时候,就需要使用用于进程间通信的机制了。
2、进程间有哪些通讯方式
信号
:信号是进程间通信唯一的异步通信机制,因为可以在任何时候发送信号给某一个进程匿名管道
:是一个内核缓冲区,进程以先进先出
的方式从缓冲区中存取数据,管道一端的进程在缓冲区的末尾写数据,管道另一端的进程在缓冲区的头部读数据;只能用于父子关系的进程
;命名管道
:正常的匿名管道
需要父子关系,而命名管道
提供了一个路径名与之相连,从而以文件的形式存在于文件系统中;就不受父子关系的限制;消息队列
:消息队列本质上是保存在内核当中的消息链表共享内存
:共享内存可以使得多个进程可以直接读写在同一块内存空间中,这是效率最高的进程间通信方式。Socket
:Socket
可以让跨网络的不同主机之间进行通讯,还可以在同主机上进程间通讯;
重排(回流)和重绘有什么区别
概念
重排(回流)
:当渲染树中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。每个页面至少需要一次的回流,也就是页面第一次加载的时候。重绘
:当页面元素样式的改变并不影响他在文档流中的位置时,这些属性只是影响元素的外观,风格,而不会影响布局的,浏览器会将新样式赋予给元素并重新绘制它,这个过程称之为重绘。
触发条件
引发重排的原因:
- 页面首次渲染
- 浏览器窗口大小发生改变
- 元素尺寸或位置发生改变
- 元素内容变化(文字数量或图片大小等)
- 元素字体大小变化
- 添加或者删除可见的 DOM 元素
- 激活 CSS 伪类(:hover)
- 设置 style 属性
- 查询某些属性或调用某些方法
引发重绘的原因:
- 字体颜色改变
- 边框样式改变
- 背景颜色、图片、定位、尺寸改变
- 外边框颜色、样式
- 外发光改变
如何避免触发回流和重绘
- 避免频繁使用
style
,而是采用修改class
的方式。 - 将动画效果应用到
position
属性为absolute
或fixed
的元素上。 - 使用
display: none
做DOM离线处理
,减少回流重绘次数。因为在display
属性为none
的元素上进行的DOM
操作不会引发回流和重绘 - 对于
resize
、scroll
等进行防抖/节流
处理。 - 利用
CSS3
的transform
、opacity
、filter
这些属性可以实现合成
的效果,也就是GPU
加速。
前端路由
-
在单页面web网页中, 有两种实现方式
hash模式
:修改URL
中hash
值,触发hashchange
事件去渲染不同的内容,history模式
:利用history API
实现 URL地址改变,触发popstate事件渲染不同的内容
hash模式
1. 原理
使用window.location.hash
属性及窗口的onhashchange
事件,可以实现监听浏览器地址hash
值变化,执行加载相应的内容。
-
什么是hash值
hash指的是地址中#号以及后面的字符,也称为
散列值
。hash也称作锚点,本身是用来做页面跳转定位的。如http://localhost/index.html#abc
,这里的#abc就是hash;hash模式会失去原生锚点
定位能力 -
hash值不会发送到服务器
hash值是不会随请求发送到服务器端的,所以改变hash,不会重新加载页面;
-
监听 hashchange 事件
监听 window 的
hashchange
事件,当散列值改变时,可以通过location.hash
来获取和设置hash值
; -
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 依赖的页面。
事件机制
事件流
事件流分为三个阶段:捕获阶段 、目标阶段 、冒泡阶段。
过程如下:
- 捕获阶段:事件从最外层的节点,也就是文档对象开始,逐级向下传播,直到事件的目标节点上。
- 目标阶段:事件到达目标节点,触发目标节点上的事件处理函数。
- 冒泡阶段:事件从目标节点开始,逐级向上传播,直到到达最外层节点(文档对象)
事件冒泡、事件捕获
冒泡事件
:是指子元素向父元素传递的过程(从里到外)捕获事件
:是指父元素向子元素传递的过程(从外到里)
如何阻止事件
-
阻止
冒泡
、捕获
事件: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全部代码
、setTimeout
、setInterval
、I/O
、UI
渲染 - 微任务:
Promise.then
、Process.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单线程与异步更多是浏览器行为,之间不冲突。补充说明:浏览器是多进程