1. Event Loop
(1) 为什么会须要事件循环
JavaScript是单线程,意味着所有的工作都须要排队,前一个工作完结,才会执行后一个工作。如果前一个工作耗时很长,后一个工作就不得不始终等着。为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking)等,必须应用事件循环(Event Loop);
(2) 事件循环
event loop会始终运行,来执行进入队列的宏工作。一个event loop有多种的宏工作源,这些宏工作源保障了在本工作源内的程序。然而浏览器每次都会抉择一个源中的一个宏工作去执行。这保障了浏览器给与一些宏工作(如用户输出)以更高的优先级。
* 首先会执行同步代码,**这属于宏工作*** 当执行完所有的同步代码后,执行栈为空,查问是否有异步代码须要执行* 执行完所有的微工作,如果微工作的执行中又退出了新的微工作,也会在这一步执行* 当执行完所有的微工作后,开始下一轮的EventLoop,执行宏工作中的异步代码,也就是setTimeout中的回调函数
(3)宏工作
浏览器为了可能使得JS外部task与DOM工作可能有序的执行,会在一个task执行完结后,在下一个 task 执行开始前,对页面进行从新渲染 (task->渲染->task->...)
鼠标点击会触发一个事件回调,须要执行一个宏工作,而后解析HTMl。还有上面这个例子,setTimeout
setTimeout的作用是期待给定的工夫后为它的回调产生一个新的宏工作。这就是为什么打印‘setTimeout’在‘script end’之后。因为打印‘script end’是第一个宏工作外面的事件,而‘setTimeout’是另一个独立的工作外面打印的。
宏工作次要包含:I/O, setInterval,setTimeout,requestAnimationFrame
(4) 微工作
微工作通常来说就是须要在以后 task 执行完结后立刻执行的工作,比方对一系列动作做出反馈,或或者是须要异步的执行工作而又不须要调配一个新的 task,这样便能够减小一点性能的开销。只有执行栈中没有其余的js代码正在执行且每个宏工作执行完,微工作队列会立刻执行。如果在微工作执行期间微工作队列退出了新的微工作,会将新的微工作退出队列尾部,之后也会被执行。微工作包含了mutation observe的回调还有接下来的例子promise的回调。
微工作次要包含:MutationObserver,promise.then catch finally,process.nextTick()
百度面试题
console.log('script start')async function async1() { await async2() console.log('async1 end') } async function async2() { console.log('async2 end') } async1()setTimeout(function() { console.log('setTimeout')}, 0)new Promise(resolve => { console.log('Promise') resolve()}) .then(function() { console.log('promise1') }) .then(function() { console.log('promise2') }) console.log('script end')/** 代码后果 **/* script start* async2 end* Promise* script end* async1 end* promise1* promise2* setTimeout
字节面试题
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>挑战js面试题</title></head><body> <script> async function async1(){ console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2(){ console.log('async2'); } console.log('script start'); setTimeout(function(){ console.log('setTimeout'); },0) async1(); new Promise(function (resolve){ console.log('promise1'); resolve(); }).then(function(){ console.log('promise2'); }); console.log('script end'); </script></body></html>//执行后果*script start*async1 start*async2*promise1*script end*async1 end*promise2*setTimeout
2. dom操作
(1) 创立节点
createElement(标签名)
createTextNode(节点文本内容)
createDocumentFragment() 创立一个dom片段
(2) 插入节点
appendChild(节点)
insertBefore(a,b) a节点会插入到b节点之前
(3) 删除节点
removeChild(节点)
(4) 复制节点
cloneNode(),用于复制节点,接管一个布尔值参数,true示意深复制(复制节点,曾经其所有子节点),false示意浅复制(复制节点,不复制子节点)
(5) 替换节点
replaceChild 用于替换节点,接管两个参数,第一个参数,是要插入的节点,第二个参数,是要被替换的节点,返回的是被替换的节点
(6) 查找节点
getElementByTagName() 通过标签名称
getElementByName() 通过元素name
getElementById() 通过元素id,唯一性
3. 事件机制
(1)事件代理
把本来须要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理dom元素的事件冒泡。
(2)事件冒泡
一个事件触发后,会在子元素和父元素之间流传。
(3)三个阶段
事件流传个别分为三个阶段
- 捕捉阶段:从window对象传导到指标对象称为“捕捉阶段”,捕捉阶段不会响应任何事件。
- 指标阶段:在指标节点上触发,称为“指标阶段”
- 冒泡阶段:从指标节点传导回window对象,称为“冒泡阶段”。事件代理即是利用事件冒泡的机制把里层所须要响应的事件绑定到外层
(4)事件代理的益处
- 能够大量地节俭内存占用,缩小事件注册
- 能够实现当新增子对象时无需再次对其绑定
(5)根本实现
<ul id="link"> <li id="1">1</li> <li id="2">2</li> <li id="3">3</li><ul>document.addEventListener("click",function(event){ var target = event.target switch(target.id){ case 1: //do something break; case 2: //do something break; case 3: //do something break; }})
4.同源策略
浏览器的同源策略是浏览器平安的基石。同源是指:协定雷同,域名雷同,端口雷同
目前非同源,共有三种行为受到限制:
(1)Cookie、LocalStorage和IndexDB无奈读取
(2)DOM无奈取得:禁止操作非源页面的DOM与JS对象
(3)Ajax申请无奈发送:禁止应用XHR对象向不同源的服务器地址发动HTTP申请,即不能发送跨域ajax申请
5.跨域
什么是跨域:违反了同源策略
怎么解决?
- jsonp:利用<script>标签没有跨域限度的破绽,通过 <script>标签的src属性指向一个须要拜访的地址并提供一个回调函数来接管回调数据,script获取到的内容会被当做js脚本进行执行
- CORS:跨域资源共享(cross origin resource share),容许浏览器向跨域服务器发送ajax申请,实现CORS通信要害是服务器,服务端在响应头设置 Access-Control-Allow-Origin 就能够开启 CORS
- 反向代理:因为跨域是针对浏览器做出的限度,对后端没有影响,能够应用Nginx,Node Server,Apache等技术计划为申请做一个转发
- websocket:是HTML5一种新的协定,实现了浏览器与服务器的全双工通信,同时容许跨域通信,是服务器推送技术的一种很好的实现。
jsonp的实现原理:
如须要发送一个get申请http://sugarat.top/path1/path...
- 客户端注册一个全局办法function callbackFunName(res){}
- 服务端收到申请后获取到url上的参数
- 服务端返回字符串callbackFunName({"name":"sugar","age":18})
- 客户端当做js脚本间接解析执行,就调用了办法callbackFunName并把外面的{"name":"sugar","age":18} 当做一个对象进行了传递
6. 跨站
同站:只有两个 URL 的 eTLD+1 雷同即是同站,不须要思考协定和端口
eTLD: (effective top-level domain) 无效顶级域名,注册于 Mozilla 保护的公共后缀列表。例如.com、.co.uk、.github.io,.top
eTLD+1: 无效顶级域名+二级域名
7. 预检申请
应用后端开启CORS解决跨域的形式,浏览器会把申请分成两种类型:简略申请和简单申请
- 简略申请:申请形式仅限于:get,head,post。content-type仅限于:text/plain,multipart/form-data,application/x-www-form-urlencoded
- 简单申请:非简略申请即简单申请
对于简单申请,首先会发动一个预检申请,申请办法为options,通过该申请来判断服务器是否容许跨域
8. 浏览器内核
浏览器内核是浏览器的外围,能够分成两局部,渲染引擎和js引擎。最开始渲染引擎和js引擎并没有很显著的辨别,然而起初js引擎越来越独立,内核就偏向于只指渲染引擎。常见的浏览器内核能够分为四种:Trident,Gecko,Blink,Webkit。
总结:浏览器内核次要指的是浏览器的渲染引擎,2013 年以前,代表有 Trident(IE),Gecko(firefox),Webkit(Safari chrome 等)。2013 年,谷歌开始研发 blink 引擎,chrome 28 当前开始应用,国内各种 chrome系的浏览器(360、UC、QQ、2345 等等)也纷纷放弃 webkit,投入 blink 的怀抱。
9. 浏览器渲染机制
- 解决HTML构建DOM树
- 解决CSS构建CSSOM树
- 将DOM树和CSSOM树合并成渲染树
- 布局,依据渲染树计算每个节点的几何信息
- 渲染绘制
10.回流、重绘
实践上每一次DOM更改或者css几何属性批改,都会引起一次浏览器重排/重绘过程,如果是css非几何属性更改,只会引起重绘。
回流(重排):浏览器渲染页面默认采纳流式布局模型,当某一个DOM节点信息更改了,须要对DOM构造进行从新计算,从新布局界面,引发回流。
重绘:当页面中的元素款式扭转,但不影响他在文档流中的地位,引发重绘。
11. 浏览器缓存机制
浏览器的缓存能够分为强缓存和协商缓存。缓存策略都是通过 HTTP Header来实现的。
(1)强缓存
Expires:response header里的过期工夫,浏览器再次加载资源时,如果在这个工夫内,则命中强制缓存。
expires:Fri, 14 Apr 2017 10:47:02 GMT//示意资源在这个工夫生效
Cache-Control:当值设为max-age=300时,则代表在这个申请正确返回工夫(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存。cache-control:max-age=691200
Cache-Control字段:
public:能够被所有用户缓存,包含终端和CDN等两头代理服务器
private:只能被终端浏览器缓存,不容许 中继 缓存服务器进行缓存
no-cache:先缓存本地,然而在命中缓存之后必须与服务器验证缓存的新鲜度能力应用
no-store:不会产生任何缓存
(2)协商缓存
当第一次申请时服务器返回的响应头没有Cache-Control和Expires或者Cache-Control和Expires过期或者它的属性值设置为no-cache时,那么浏览器第二次申请的时候就会与服务器进行协商。
如果缓存和服务端资源的最新版本是统一的,那么就无需再次下载该资源,服务端间接返回304 Not Modified状态码,如果服务器发现浏览器中的缓存曾经是旧版本了,那么服务器就会把最新资源的内容返回给浏览器,状态码就是200。
协商缓存有两种:1)Etag+If-None-Match 2)Last-Modified+If-Modified-Since
Etag + If-None-Match
Etag是上一次加载资源时,服务器返回的response header中的值,是对该资源的一种惟一标识,只有资源有变动,Etag就会从新生成。浏览器在下一次加载资源向服务器发送申请时,会将上一次返回的Etag值放到request header里的If-None-Match里,服务器承受到If-None-Match的值后,会拿来跟该资源文件的Etag值做比拟,如果雷同则命中协商缓存。etag: "58bfb82f-400b8"
Last-Modified + If-Modified-Since
Last-Modified是该资源文件最初一次更改工夫,服务器会在response header里返回,同时浏览器会将这个值保存起来,在下一次发送申请时,放到request header里的If-Modified-Since里,服务器在接管到后也会做比对,如果雷同则命中协商缓存。last-modified: Fri, 14 Apr 2017 10:47:02 GMT
(3)浏览器缓存过程
- 浏览器第一次加载资源,服务器返回200,浏览器将资源文件从服务器上申请下载下来,并把response header及该申请的返回工夫一并缓存;
- 下一次加载资源时,先比拟以后工夫和上一次返回200时的时间差,如果没有超过cache-control设置的max-age,则没有过期,命中强缓存,不发申请间接从本地缓存读取该文件(如果浏览器不反对HTTP1.1,则用expires判断是否过期);如果工夫过期,则向服务器发送header带有If-None-Match和If-Modified-Since的申请;
- 服务器收到申请后,优先依据Etag的值判断被申请的文件有没有做批改,Etag值统一则没有批改,命中协商缓存,返回304;如果不统一则有改变,间接返回新的资源文件带上新的Etag值并返回200;
- 如果服务器收到的申请没有Etag值,则将If-Modified-Since和被申请文件的最初批改工夫做比对,统一则命中协商缓存,返回304;不统一则返回新的last-modified和文件并返回200;
12.本地存储计划
在浏览器端存储数据对咱们是很有用,这相当于赋予浏览器记忆的性能,能够记录用户的所有状态信息,加强用户体验。
浏览器常见的存储计划有三种
- Cookie
- web存储(localStorage 和 sessionStorage)
- indexDB
(1)Cookie
cookie会主动在web浏览器和web服务器之间流传,因而在服务器端脚本就能够存储一些罕用的数据,比方用户的登录状态。
长处:浏览器的兼容性很好
毛病:
- 存储量小
- 影响性能,Cookie会作为申请头发送,当cookie存储信息过多时,会影响效率
- 只能存储字符串
- 平安问题,cookie中的数据能够被任何人拜访,所以不能存储重要信息
(2)web存储
LocalStorage:本地存储
通过localStorage存储的数据时永久性的,除非咱们应用removeItem来删除或者用户通过设置浏览器配置来删除,负责数据会始终保留在用户的电脑上,永不过期。
localstorage的作用域限定在文档级别,同源能力共享。
SessionStorage:会话存储
sessionStorage只存储以后会话页的数据,当用户敞开以后会话页或者浏览器时,数据会被革除。
(3)IndexDB
长处:
- 领有更大的存储空间
- 可能解决更加简单的数据结构
- 领有更大的交互管制
毛病:
- 存储空间限度:一个独自的数据库我的项目的大小没有限度。然而可能会限度每个 IndexedDB数据库的大小
- 浏览器兼容性问题
- 受同源策略的限度
13. 页面循环系统
音讯队列和事件循环
14.性能优化
(1)渲染阻塞的优化计划
优化CSS:让css在特定条件下能力应用,这样这些资源在首次渲染时先不构建CSSOM树,只有在合乎特定条件下,才会让浏览器进行阻塞渲染,而后构建CSSOM树。
CSS的媒体查问就是用来实现这个性能的。应用媒体查问能够让css资源不在首次加载中阻塞渲染,但不论是哪种css资源,它们的下载申请都不会被疏忽,浏览器依然会先下载css文件。
<!-- 没有应用媒体查问,这个css资源会阻塞渲染 --><link href="style.css" rel="stylesheet"><!-- all是默认类型,它和不设置媒体查问的成果是一样的 --><link href="style.css" rel="stylesheet" media="all"><!-- 动静媒体查问, 将在网页加载时计算。依据网页加载时设施的方向,portrait.css 可能阻塞渲染,也可能不阻塞渲染。--><link href="portrait.css" rel="stylesheet" media="orientation:portrait"><!-- 只在打印网页时利用,因而网页首次在浏览器中加载时,它不会阻塞渲染。 --><link href="print.css" rel="stylesheet" media="print">
优化JavaScript:
应用async能够告诉浏览器该脚本不须要在在援用地位执行,这样浏览器能够持续构建DOM,
(2)其余优化办法
- 服务端在接管到申请时只响应HTML的初始局部,后续的HTML内容在须要时再通过AJAX获取。然而这个办法,不能用在CSS文件上,浏览器不容许CSSOM只构建局部内容。
- 对数据进行压缩,在进行压缩前,首先要对文件进行冗余压缩,即去除掉正文,空格符,换行符。在进行完冗余压缩后,再应用压缩算法,对数据自身进行压缩。例如GZIP(GZIP是一个能够作用于任何字节流的通用压缩算法)
- 应用缓存
- 资源预加载,pre-fetching是一种提醒浏览器事后加载用户之后可能会应用到的资源的办法。
应用dns-prefetch来提前进行DNS解析,以便之后疾速地拜访另一个主机名
<link rel="dns-prefetch" href="other.hostname.com">
应用prefetch属性能够事后下载资源,不过它的优先级是最低的
<link rel="prefetch" href="/some_other_resource.jpeg">
prerender能够事后渲染好页面并暗藏起来,之后关上这个页面会跳过渲染阶段,间接出现在用户背后
<link rel="prerender" href="//domain.com/next_page.html">
15.ajax
(1)xhr
XMLHttpRequest API的毛病:
- 不合乎关注点拆散的准则
- 配置和调用形式凌乱
- 基于事件的异步模型比拟凌乱
var xhr = new XMLHttpRequest();xhr.open('GET',url);xhr.responseType = 'json';xhr.onload = function(){ console.log(xhr.response);}xhr.onerror = function(){ console.log('xhr error');}xhr.send();
(2)fetch
fetch是原生的js,是一种http数据申请的形式,是XMLHttpRequest的一种代替计划。
长处:
- 语法简洁,更加语义化
- 基于规范Promise实现,反对async/await
fetch(url).then(response=>response.json()) .then(data=>console.log(data)) .catch(e=>console.log('error' + e));
16.路由原理
(1)hash
(2)history
参考文章:
https://juejin.cn/post/692673...
https://www.jianshu.com/p/f4b...