浏览器引擎
浏览器引擎包括排版引擎(浏览器内核,渲染引擎)和JS引擎(JS解析器)
一、Trident (IE内核)
许多壳浏览器都采用Trident内核,其中部分浏览器的新版本是“双核”甚至是“多核”,其中一个内核是Trident,然后再增加一个其他内核。国内的厂商一般把其他内核叫做“高速浏览模式”,而Trident则是“兼容浏览模式”,用户可以来回切换。
IE的JS引擎先后是JScript 、Chakra。
二、Gecko(Firefox内核,开源)
Netscape浏览器是Netscape公司已经停止的项目,Netscape公司被美国在线收购后,已经解散。Mozilla基金会是Netscape公司成立的一个组织,支持和领导开源的Mozilla项目,Firefox浏览器是这个项目中的一个部分。
Firefox的JS引擎先后是SpiderMonkey、TraceMonkey、JaegerMonkey。
三、WebCore(Safari内核,开源)
WebCore是苹果的Safari浏览器使用的排版引擎。
Safari的JS引擎先后是JavaScriptCore、Nitro。WebCore和JavaScriptCore是Webkit浏览器引擎的两个组件,均是从KDE的KHTML及KJS引擎衍生而来,它们都是自由软件。
四、Blink(Chrome引擎,开源)
Blink是Google开发的WebCore的分支。
Chrome的JS引擎是V8。
HTTP
一、请求
1、请求行 GET www.baidu.com/index.html HTTP/1.1 (Method Request-URI HTTP-Version)
请求方法GET、POST、HEAD、PUT、DELETE,语义不同,表现不同。
2、请求头
Cache(缓存)
If-Modified-Since:Wed, 11 Feb 2015 04:12:02 GMT 值为该请求上一次响应的得到的Last-Modified,服务器段据此判断文件是否改变过,没改变则返回304
If-None-Match:W/"2275-1423627922000" 值为该请求上一次响应的得到的ETag,服务器收到If-None-Match后,对比新计算的tag,判断是否返回304
Client(客户端信息)
Accept:*/* 接受何种MIME类型(文档类型,格式为:类/子类)的响应
Accept-Encoding:gzip,deflate,sdch 接受何种压缩编码的响应
Accept-Language:en-US,en;q=0.8 浏览器支持的语言
User-Agent: AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.72 浏览器版本、内核
X-Requested-With:XMLHttpRequest 标明是ajax异步请求
Cookie:locale=zh_CN; language=zh_CN; Secure=true; HttpOnly=true; JSESSIONID=E3C4995099A245125E1E8D4C766891B2
Entity(实体)
Content-Length:73 请求体长度
Content-Type:application/json; charset=UTF-8 请求体的MIME类型,表单提交的Content-Type通常是application/x-www-form-urlencoded或multipart/form-data(有附件上传)
Miscellaneous(杂项)
Origin:https://www.sina.com 请求来源,只在POST请求中有
Referer:https://www.sina.com/index.html 请求详细来源
还有一些自定义的信息,如token
Transport(传输)
Connection:keep-alive 保持TCP链接,多个http请求,使用同一个TCP链接。close表示不保持
Host:https://www.baidu.com 请求目标域名
3、请求体
二、响应
1、状态行 HTTP/1.1 200 OK (HTTP-Version Status-Code Reason-Phrase)
状态码
1xx:指示信息--表示请求已接收,继续处理
2xx:成功--表示请求已被成功接收、理解、接受
3xx:重定向--要完成请求必须进行更进一步的操作
4xx:客户端错误--请求有语法错误或请求无法实现
5xx:服务器端错误--服务器未能实现合法的请求
常见状态代码、状态描述、说明:
200 OK //客户端请求成功
302 Moved Temporarily // 重定向,响应头里会有 Location,即重定向目标;在CDN 302调度中,CDN调度器会返回302指向一个CDN接入节点
304 Not Modified //响应体为空,客户端使用缓存
400 Bad Request //客户端请求有语法错误,不能被服务器所理解
401 Unauthorized //请求未经授权
403 Forbidden //服务器收到请求,但是拒绝提供服务
404 Not Found //请求资源不存在
500 Internal Server Error //服务器发生不可预期的错误
503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
2、响应头
Cache(缓存)
Date:Tue, 17 Feb 2015 03:57:40 GMT 响应时间
Vary:Accept-Charset, Accept-Encoding, Accept-Language, Accept 指明该响应依据请求的哪些字段产生,因此如果下一次的请求有个字段的值不同,就不会使用该响应作为缓存
Expires:Tue, 24 Feb 2015 05:04:40 GMT 缓存有效期
Cache-Control:max-age=604800 缓存时长(秒),优先级高于Expires
Set-Cookie: UserID=JohnDoe; Max-Age=3600 httpOnly; 每条cookie除了key-value,还有各自的域、寿命、httpOnly、secure等属性。
Entity(实体)
Content-Length:3237 响应体长度
Content-Type:application/json;charset=UTF-8 响应体MIME类型
ETag:W/"22667-1423627922000" 文件标识符
Last-Modified:Wed, 11 Feb 2015 04:12:02 GMT 返回的文件的最后修改时间
Miscellaneous(杂项)
Server:apache 服务器名称
Accept-Ranges:bytes 支持断点续传的粒度
X-Download-Options:noopen 禁止浏览器打开下载的文件
Security(安全)
X-Content-Type-Options:nosniff 禁止浏览器自动判断文档类型
X-Frame-Options:SAMEORIGIN 禁止响应被其他网站frame
X-XSS-Protection:1; mode=block 开启xss防护并通知浏览器阻止而不是过滤用户注入的脚本。0 – 关闭xss防护 1 – 开启xss防护
Access-Control-Allow-Origin:http://www.test.com 允许来自www.test.com的跨域请求
Access-Control-Allow-Credentials:true 允许跨域请求时带cookie
Transport(传输)
Connection:keep-alive
Location: xxxx 跳转目标
3、响应体
三、现象
1、浏览器是否使用缓存:
(1)、看缓存是否存在,存在则到第2步,不存在则到第3步。
(2)、看缓存是否过期(Expires、Cache-Control),不过期则使用缓存。无Expires、Cache-Control默认为过期,过期则到第3步。
(3)、发送请求,带上If-Modified-Since、If-None-Match,服务器判断资源是否发生变化,无变化,返回304,浏览器使用缓存。无If-Modified-Since、If-None-Match默认为变化,返回200,不使用缓存。
2、跳转或地址栏回车刷新与浏览器左上角刷新不一样
四、版本
HTTP 0.9,只有基本的文本 GET 功能。
HTTP 1.0,完善的请求/响应模型,并将协议补充完整。1996年发布
HTTP 1.1,在 1.0 基础上进行更新,增加了如 长久连接 keep-alive 与 chunked 等功能。1999 年发布
HTTP 2.0 语义与 1.1 兼容
1、多路复用流:多个请求或响应被拆分,在同一个连接上传输,到达目的地后再重新组装。资源合并、域名分区变得不重要。
2、优先权和依赖:客户端发出的请求带有 优先权和依赖 的信息
3、头部压缩:多个请求的头部 很多重复,可以进行压缩
4、服务器端推
默认连接方式
HTTP1.1下Connection: Keep-Alive
HTTP1.0下Connection: Close
五、安全策略
内容安全策略(Content Security Policy,CSP)
在 HTTP 响应头里设置 CSP
Content Security Policy: default-src 'self' // 所有资源 只能 来自 本域名
Content Security Policy: font-src 'a.com' // webFont 只能 来自 a.com
Content Security Policy: content-src 'https:' // ajax、websocket 只能通过https
还可以设置 style-src 、img-src、script-src、media-src、frame-src等
目录
传统浏览器处理依赖的文件路径算法如下:
如果依赖是相对路径, 那么该依赖是以当前文件作为参考路径的
build/src/index.html
<img src="./theme/default/images/home/user_status_gd.png">
那么user_status_gd.png的路径为:build/src/theme/default/images/home/user_status_gd.png
如果依赖是绝对路径,那么该依赖路径为
build/src/index.html
<img src="/theme/default/images/home/user_status_gd.png">
那么user_status_gd.png的路径为:contextname/theme/default/images/home/user_status_gd.png
如果直接以目录开始,那么该依赖的路径为
build/src/index.html
<img src="theme/default/images/home/user_status_gd.png">
那么user_status_gd.png的路径为:build/theme/default/images/home/user_status_gd.png
异步、延迟、承诺
延迟模块的API遵循 promise 规范
$.Deferred()
$.Deferred()返回一个deferred(延迟)对象
var deffered = jQuery.Deferred();
给deffered添加延迟处理函数(承诺),可以链式调用
deferred.done() 解决
deferred.fail() 拒绝
deferred.always() 结束(解决或拒绝)
deferred.progress()进度变化
deferred.then() 结束或进度变化
延迟判决
deferred.notify()、deferred.notifyWith(args) 延迟进度通知,回调progress、then
deferred.reject()、deferred.rejectWith(args) 延迟被拒绝,回调fial、always、then
deferred.resolve()、deferred.resolveWith(args)延迟解决,回调done、always、then
同时处理多个deffered,$.when(deffered1,deffered2).then(function(){});
var promise = deferred.promise();返回承诺对象
promise可像deffered一样添加承诺(延迟处理函数)
$.ajax()
$.ajax() 发出请求并返回一个jqXHR对象,它是一个deferred对象,和XHR对象一样拥有 readyState、responseText、status、statusText等属性。
var jqXHR = $.ajax({});
给jqXHR 添加回调函数,可以链式调用
jqXHR .done() :请求成功回调,取代了 jqXHR .success
jqXHR .always():请求结束回调,取代了jqXHR .complete
jqXHR .fail() :请求失败回调,取代了jqXHR .error
jqXHR同样拥有progress()、then()、promise()
同时处理多个jqXHR对象,$.when(jqXHR1 , jqXHR2).done(function(){})
$q.defer()
angularjs的$q.defer()返回一个延迟对象
var deferred = $q.defer();
延迟判决
deferred.notify(args) 延迟进度通知
deferred.resolve(args) 延迟解决
deferred.reject(args) 延迟被拒绝
var promise = deferred.promise;承诺对象
添加承诺(延迟处理函数)
promise.then(successCallback, errorCallback, notifyCallback) 按顺序添加承诺
promise.catch(errorCallback) 拒绝
promise.finally(callback) 始终(解决、拒绝、进展)
文件API
参考 https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
获取文件
1、从表单中读取
var file = document.getElementById("uploadImage").files[0];
2、本地拖入(见文章【HTML5 拖放】)
function drop(event)
{
event.preventDefault();
var file = event.dataTransfer.files[0];
}
3、从网络获取
// XMLHttpRequest 下载
var blob = new Blob([xhr.response], {type: 'image/png'});
// fetch 下载
let res = await fetch();
let blob = await res.blob();
// axios 下载
axios({
method: 'get',
responseType: 'blob',
});
使用文件
// FileReader 为 HTML5新增对象
var fileReader = new FileReader();
1、预览图片
// 读取后生成URL
fileReader.readAsDataURL(file);
// 读取完毕事件
fileReader.onload = function (fileEvent) {
// 预览图片
document.getElementById("uploadPreview").src = fileEvent.target.result;
};
2、上传(见文章【上传下载】)
let data = new FormData();
data.append("file", file);
let xhr = new XMLHttpRequest();
xhr.open('POST', 'http://**');
xhr.send(data);
xhr.onload = (request)=>{
console.log(request.target.response);
};
// 使用 axios
axios({
method: "post",
url: 'http://**',
data: form
}).then((response) => {});
3、读取文本
//读取文本
fileReader.readAsText(file);
//读取完毕事件
fileReader.onload = function (fileEvent) {
console.log(fileEvent.target.result);
};
4、下载到本地
var link = `<a href="data:text/csv;charset=utf-8,${content}" download="export.csv">download</a>`;
document.write(link);
URL.createObjectURL(blob);
window.open(blob)
事件
事件模型与事件委托
事件模型:捕获模型,冒泡模型;IE8以及之前不支持捕获模型
先捕获阶段,再冒泡阶段;addEventListener(“事件名”,方法名,事件模型 );第三个参数为true即在事件的捕获阶段执行
事件处理函数为先添加先执行,event.stopPropagation(); 停止冒泡,return false; 停止后续的所有响应
事件委托
意义:不用每个元素都去绑定事件,优化性能;新增元素不用单独绑定事件
JS事件委托
给父元素绑定事件,在事件处理函数中,判断e.target是哪个元素
jQuery事件委托
live 把td的事件委托到#info_table,第二个参数默认是document
$("td",$("#info_table")).live("click",function(){});
delegate实现同样的功能,优化于live
$("#info_table").delegate("td","click",function(){});
on实现同样的功能
$("#info_table").on("click","td",function(){});
组件自定义事件
var widget = {
_listener : {
"click": [func1, func2],
"change": [func3]
},
addEvent : function(type, fn) {
// 添加
},
fireEvent : function(type) {
// 触发
},
removeEvent : function(type, fn) {
// 删除
}
}
出错事件
window.addEventListener('error', function (e) { // 用于全局统一处理出错
var error = e.error;
console.log(error);
});
高频事件
scroll、resize、mouseover
防抖 underscore.debounce
var debounce = function(fn, delay){ // 将参数delay结合到原函数fn,得到一个新函数,这个过程称为柯里化
var timer; // 闭包变量
return function(){
// 将类数组对象转化为数组
var args = [].slice.call(arguments);
// 事件处理函数的this指向事件主体
var context = this;
// 重设定时器
clearTimeout(timer);
timer = setTimeout(_fn, delay);
function _fn(){
fn.applay(context, _args);
}
}
}
window.addEventListener("scroll", debounce(function(){// 事件处理函数}, 1000))
节流 underscore.throttle
目的:解决 debounce 可能导致多次事件触发都不会执行事件处理函数
方案:在 debounce 的基础上,如果这次事件距离上次处理的时间差大于一定值,则清除上个定时器,并马上执行处理函数,否 则设置定时器
请求动画帧 requestAnimationFrame
var rafThrottle = function(fn){
var isLocked;
return function(){
var _args = arguments;
var context = this;
if(isLocked) return;
isLocked = true;
requestAnimationFrame(function(){
isLocked = false;
fn.apply(context, _args);
});
}
}
加载,渲染,绘制
浏览器加载过程
(1)、浏览器通过DNS获取域名对应IP
(2)、建立TCP连接
(3)、发送http请求
(4)、html边下载边渲染
(5)、碰到内嵌JS 和 CSS,启动新的连接下载,阻塞渲染和JS执行(因此要把脚本放后面)
(6)、获取到CSS后,重新渲染
(7)、JS按标签顺序下载并执行
(8)、碰到内嵌img,启动新的连接下载,不会阻塞渲染
(9)、获取到img后,重新渲染
DNS: (1)客户机向本地域名服务器dns.company.com发出解析域名www.linejet.com的请求报文。 (2)本地域名服务器查询本地缓存, 假设没有该纪录, 则向根域名服务器NS.INTER.NET发出请求。 (3)根域名服务器查询本地记录得到如下结果:linejet.com NS dns.linejet.com (表示linejet.com域中的域名服务器为dns.linejet.com)
渲染
1、HTML代码转化成DOM
2、CSS代码转化成CSSOM(CSS Object Model)
3、cascade级联DOM和CSSOM,生成一棵渲染树(包含每个节点的视觉信息)
4、生成布局(layout),即将所有渲染树的所有节点进行平面合成
5、将布局绘制(paint)在屏幕上
页面发生变化时,要重新渲染reflow(重新生成布局并绘制)。有元素位置发生变化或滚动时,需要重排并重绘。元素位置都没发生变化时,只需重绘repain。
浏览器会将几个连续的改变累积起来,再一次重新渲染,以减少重新渲染的次数。
脚本读取位置,尺寸等信息时,浏览器不得不把之前累积的变化,重新渲染一次,再返回最新的位置和尺寸。
优化
1、读取位置和尺寸的脚本最好放在一起,不要与改变页面的脚本穿插
2、缓存位置和尺寸信息, 不要重复读取
3、对离线DOM 操作完后,再加入DOM树。不用加入后再操作。
4、对DOM的复杂改变,可以先隐藏,再操作,再显示
5、position属性为absolute或fixed的元素重排开销较小,动画最好设置在它们身上
6、window.requestAnimationFrame(function(){写操作}) ,把这次写操作放到下一次渲染
7、尽量避免写在HTML标签中写Style属性,会阻塞渲染
加载完成
当 HTML 文档解析绘制完成就会触发 DOMContentLoaded,而所有资源(图片和非阻塞脚本)加载完成之后,load 事件才会被触发。
另外需要提一下的是,我们在 jQuery 中经常使用的 $(document).ready(function() { // ...代码... }); 其实监听的就是 DOMContentLoaded 事件,而 $(document).load(function() { // ...代码... }); 监听的是 load 事件。
在chrome network 左下方能看到,打开overview能看到两条对应的线
编码、转码、转义
字符 =》二进制
字符集:规定字符 和 对应的码点
Unicode 是一个字符集,需要四个字节才能表示完所有字符。Unicode的前127个码点 与 ASCII字符集相同
双字节码点范围 /[\u0100-\uffff]/
中文码点范围 /[\u4e00-\u9fa5]/
字符编码:将字符的码点编入字节
UTF-32编码采用Unicode字符集,用四个字节编码一个字符
UTF-8编码采用Unicode字符集,变长编码方式,码点小的字符用一个字节编码,码点大的字符用2-4个字节编码。
字符转码:
获取十进制Unicode码点:
String.prototype.charCodeAr(index)、String.prototype.codePointAt(index):
获取十六进制Unicode码点:
escape: 获取Unicode码点,不转码范围{ ASCII字母 数字 @*/+ },用于字符串转码。unescape解码
获取十六进制UTF-8编码(两个16进制数对应一个字节,即每4比特转成一个1-16的数字):
encodeURI: 获取UTF-8编码,不转码范围{ ASCII字母 数字 ~!@#$&*()=:/,;?+' },用于URI转码。decodeURI解码
encodeURIComponent: 获取UTF-8编码,不转码范围 { ASCII字母 数字 ~!*()' },用于URI的组成部分转码,转码范围比encodeURI大一些。decodeURIComponent解码
二进制 =》字符
Base64编码:每6比特转成一个1-64的数字,再用这个数字在字母表(包含大小写字母数字+/)上找到一个字母
btoa: 对单字节字符进行Base64转码。atob解码
转义
"&单词;" 转成 "特殊字符",浏览器自动完成。浏览器没有自带的转义和反转义API
encodeHTML: function(a) {
return String(a).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
},
decodeHTML: function(a) {
var b = String(a).replace(/"/g, '"').replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
return b.replace(/&#([\d]+);/g, function(d, c) {
return String.fromCharCode(parseInt(c, 10))
})
},
进程与线程
浏览器进程
1、Brower进程:负责Tab之间的公共功能
2、Render进程:一个Tab对应一个进程,也可能几个进程被合并
3、GPU进程:负责3D绘制
4、第三方插件进程
Render进程
1、GUI渲染线程:负责布局、绘制、重绘Repain、回流Reflow
2、JS引擎线程:解析、运行JS,一个Render进程只有一个JS引擎线程(单线程)
3、事件触发线程
4、定时触发线程,由setInterval 和 setTimeout 创建
5、异步触发线程
6、web work线程,是JS引擎线程的子线程,不会阻塞JS引擎
三种触发线程适时将回掉函数添加到 JS引擎线程的待处理任务队列中,由JS引擎依次执行
JS引擎事件轮询
一个tick(JS引擎从任务队列中提取一个任务(宏任务,macrotask,task) -> 执行宏任务 -> 执行当前tick添加的微任务(microtask, job)-> GUI渲染引擎 repain\reflow) -> 下一个tick
原生 promise resolve/reject 时,会把 then注册的回调函数添加到微任务队列中
vue中的$nextTick 就是下一个宏任务
setTimeout,即使延迟0毫秒,也是到下一个宏任务进行
常见问题
为什么要用setTimeout模拟setInterval?
setInterval准时把回调函数添加到 JS引擎的任务队列,这时候可能上一次添加的任务还没被处理
面试题
setTimeout(() => {
console.log(4);
}, 0);
new Promise(resolve=>{
console.log(1);
for(let i =0;i<10000;i++){
i == 9999 && resolve();
}
console.log(2)
}).then(()=>{
console.log(5)
})
console.log(3)
// 1 2 3 5 4
正则表达式
POSIX(UNIX操作系统接口标准)定义了两种正则表达式语法,基本正则表达式(Basic Regular Expression,BRE)和扩展正则表达式(Extended Regular Expression,ERE)
正则表达式的用途
用于查找和替换:在一行里查找匹配模式的字符串 linux grep, linux sed, String.match, String.replace
用于检验:把源串当作一行,查找匹配模式的子串,存在匹配串则检验成功 RegExp.test
匹配并分组:String.match /g模式,返回所有匹配的串
String.match 非/g模式,返回第一个匹配的串 和 这个匹配串里的分组;分组即表达式里的()
基础正则表达式
支持:linux sed,linux grep
+、()、{}、?、+、| 得用 \ 转义,以下简化为不加 \
/^[^a-z]*.+\b(ss|st){1,3}\b[123]?$/
^表示匹配串得在行首,[^a-z]表示不能是小写字母,*表示0-n个,.表示任意字符(不能匹配换行符),+表示1-n个,\b表示单词边界,()表示组,|表示或(得用在小括号内),{1,3}表示1-3个,[123]表示取123任意一个,?表示0-1个,$表示匹配串得在行尾
常用类:数字\d(不包括小数点和负号),非数字\D,字母\w,非字母\W,空白符\s(包括换行符),非空白符\S
[\s\S]* 可以跨多行匹配
扩展正则表达式
支持:linux grep -E,linux egrep,JS
+、()、{}、?、+、| 都不用\转义
非贪婪模式
支持:JS
* 、+ 、{}默认为贪婪模式,即尽可能匹配更多的字符
JS支持非贪婪模式:*? 、+? 、{}?,匹配最少的字符
环视(零宽断言)
支持:JS
作为匹配条件(断言),但不作为匹配串的一部分(零宽)
(?<=B)A : 匹配串得匹配A,而且匹配串前的片段得匹配B
(?<!B)A : 匹配串得匹配A,而且匹配串前的片段得不匹配B
A(?=B) : 匹配串得匹配A,而且匹配串后的片段得匹配B
A(?!B) : 匹配串得匹配A,而且匹配串后的片段得不匹配B
模式修正符
支持:linux sed,JS
/g 全行匹配多个,匹配串之间不会交叉,有/g则String.match返回一个数组
/i 不区分大小写
/m 多行匹配,JS默认为单行匹配,即把源串只有一个结束符,多行匹配则能匹配多个结束符$