1.宏任务和微任务
js是一门单线程语言,所以同一时间只能执行一个任务。为了防止主线程不阻塞,开发者提出了事件循环(Event Loop)。
1.1任务执行的原则:
- js将任务分为了同步任务和异步任务。
- 同步任务都在主线程上执行,形成一个执行栈。
- 异步任务会进入到一任务列表注册他们,一旦异步任务有了执行结果,则将其放到一个任务队列中。
- 当同步任务执行完成后,此时主线程空闲,则将异步任务队列放入执行栈中,依次执行。
1.2 宏任务
每次执行栈执行的的代码都可以看做一个宏任务。
浏览器为了能够是js内部的宏任务和DOM任务有序执行。每次宏任务结束下次宏任务开始前都会进行一次页面重绘。
宏任务->页面重绘->宏任务
常见的宏任务包含:
script(整体代码)
setTimeout
setInterval
I/O
UI交互事件
postMessage
MessageChannel
setImmediate(Node.js 环境)
1.3 微任务
当前任务执行结束后立即执行的任务。当前任务结束后,下一个任务执行前(渲染之前)
常见的微任务包含:
Promise.then
Object.observe
MutaionObserver
process.nextTick(Node.js 环境)
宏任务和微任务被放在了不同的队列中。
当前宏任务执行完成后会检查微任务队列中是否存在任务,有则执行。
宏任务的优先级高于微任务
console.log("script start")
setTimeout(()=>{
console.log("异步宏任务")
},0)
new Promise((resolve)=>{
console.log("同步宏任务")
resolve()
}).then(()=>{
console.log("微任务1")
})
console.log("script end")
script start
同步宏任务
script end
微任务1
异步宏任务
解析:
- 将
script start
、Promise
、script end
放入主线程执行栈、setTimeout
放入异步宏任务队列。
Promise是立即执行函数,在其创建的时候就被执行了,只有在then()执行时才会被推入微任务队列
- 当
script start
执行完成后,立即执行new Promise
任务。 resolve
执行成功后,将then
放入微任务队列,同时,继续下一个宏任务。- 因为
script end
本身就在主线程上,在同步执行栈中,优先执行同步任务。 - 完成后发现微任务中有
then
任务,立即执行。 - 最后继续查找是否还有同步任务,没有了,所以开始执行异步宏任务
setTimeOut
。
1.4 事件循环(Event Loop)
事件循环是解决js单线程运行阻塞的一种机制。
2.new操作
new操作主要完成了以下4个步骤:
- 创建一个新的对象{}。
- 将构造函数的作用域赋给新的对象,即this指向新对象。
- 执行构造函数的代码,为新的对象添加属性。
- 返回这个新的对象。
3.XSS和CSRF攻击
3.1 XSS
XSS:(跨域脚本攻击)
攻击原理:
不需要做登陆验证,通过合法的操作(例如在输入框输入脚本,url添加脚本参数),向你的页面注入可执行脚本,从而破坏页面正常结构,或插入广告等恶意内容。
攻击方式:
- 反射性攻击(Reflected XSS):是指xss代码在请求的url中,而后提交到服务器,服务器解析后,XSS代码随着响应内容一起传给客户端进行解析执行。
- 存储型攻击(Stored XSS):Stored XSS和 Reflected XSS的差别就在于,具有攻击性的脚本被保存到了服务器端(数据库,内存,文件系统)并且可以被普通用户完整的从服务的取得并执行,从而获得了在网络上传播的能力。
- 基于DOM或本地的 XSS 攻击:DOM型 XSS其实是一种特殊类型的反射型 XSS,它是基于 DOM文档对象模型的一种漏洞。可以通过 DOM来动态修改页面内容,从客户端获取 DOM中的数据并在本地执行。基于这个特性,就可以利用 JS脚本来实现 XSS漏洞的利用。
防御措施:
- 输入过滤:前后端对传入数据进行校验和过滤。
- 实体转义:当需要往 HTML 标签之间插入不可信数据的时候,首先要做的就是对不可信数据进行 HTML Entity 编码。
- javaScript编码转义:对不可信的数据进行javaScript编码。
- Http Only cookie:将重要的cookie设置为 http only。
3.2 CSRF
CSRF:跨站请求伪造
攻击原理:
- 用户登录了授信网站A,并且在本地生成了cookie信息。
- 在不退出A网站的前提下,访问了危险网站B。
- 网站B接收到接收到用户请求后,返回一些攻击性的代码,然后在用户不知情的情况下,携带cookie向A网站发出请求,从而达到攻击的目的。
防御措施:
- 验证 HTTP Referer 字段
- 在请求地址中添加 token 并验证
- 在 HTTP 头中自定义属性并验证
4.作用域
作用域是存储和访问变量的一套规则,定义了变量的可访问性。常分为局部作用域、全局作用域、块级作用域。
编译过程一般分为三个步骤:词法分析、解析词法、代码生成。
4.1词法分析
通常会将代码解析成有意义的代码块,这些代码块叫做词法单元。
例如:
var a=2
解析后为:
var、a、=、2
4.2解析词法
将词法单元组成一个抽象语法树(AST)。
4.3 代码生成
将抽象语法树转化成可执行的代码,并将变量存储到内存中。
作用域就是负责维护所有声明的变量和查询操作而存在的。
4.5编译过程
以var a = 2
为例
- 编译器询问当前作用域中是否存在变量 a,若果存在忽略声明,否则在当前作用域中新建一个变量a。
- 编译器生成可执行的代码,js引擎询问当前作用域是否存在变量a,存在则直接使用,否则继续向上级作用域查找。
- 如果js引擎找到了变量a则将2赋值给它,否则抛出异常。
5.强缓存和协商缓存
5.1强缓存
当浏览器请求某个文件时,会根据response header中的缓存字段,并且判断其是否缓存超时,如果未超时,则不发起请求,直接利用浏览器缓存的资源,状态码:200。
强缓存的header字段有2个:
expires:它是http1.0时的提出的。它的值为一个绝对时间的GMT格式的时间字符串,如Mon, 10 Jun 2015 21:31:12 GMT,如果发送请求的时间在expires之前,那么本地缓存始终有效,否则就会发送请求到服务器来获取资源。
cache-control:max-age=number:它是http1.1时提出的。主要利用max-age的值来判断是否超时,其值是一个相对值。资源第一次的请求时间和Cache-Control设定的有效期,计算出一个资源过期时间,再拿这个过期时间跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存,否则未命中。
cache-control
有以下几个常用值:
no-cache
:不使用本地缓存,需要使用协商缓存,先与服务器确认返回的响应是否被更改,如果之前的响应中存在ETag,那么请求的时候会与服务器进行验证,未过期则不更新。no-store
:禁用浏览器缓存,用户的每次请求都将重新从服务器下载全新的资源。public
:可以被所有用户缓存,包含终端用户和CDN等中间代理服务器。private
:只能被终端用户的浏览器缓存,不允许代理服务器对其缓存。max-age
:缓存相对时间,与上一次资源变更时间相加,用于计算资源过期时间。
cache-control的优先级高于expires
5.2协商缓存
当强缓存未命中的时候,向服务端发送请求,根据响应头的信息,判端浏览器的缓存资源是否和服务器资源一致,如果一致,则仅返回一个请求头状态码304,如果不一致,则重新返回浏览器资源,同时修改请求头信息,状态码为200.
协商缓存是客户端与服务端公共约定的一套通讯标识,用于让服务端能清晰的判断请求资源是否可以缓存访问。协商缓存的字段是成对出现的,如果请求头不包含缓存字段,那么响应头也不会有。
常用的协商缓存字段有以下2对:
Last-Modified/If-Modified-Since
:这2者都是一个GMT格式的时间字符串。具体使用流程如下:- 浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在
respone header
加上Last-Modified
字段,其表示资源在服务器上的最后修改时间。 - 浏览器再次跟服务器请求这个资源时,在
request header
上加上If-Modified-Since
字段,其表示上一次请求资源时返回的Last-Modified
的值。 - 服务器根据浏览器传入的
If-Modified-Since
和服务器资源的最后更新时间进行比较,如果未变化,则返回304 Not Modified
,浏览器直接利用缓存,如果缓存未命中,则重新加载服务端的资源,且修改respone header
中的Last-Modified
。
- 浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在
*Etag/If-None-Match
:这两个值都是服务器生成的资源唯一标识,当资源变化时唯一标识也会变化。
判断流程与Last-Modified/If-Modified-Since
类似。
Etag
相较于Last-Modified
的优点:
- 一些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
- 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since能检查到的粒度是s级的,这种修改无法判断(或者说UNIX记录MTIME只能精确到秒);
- 某些服务器不能精确的得到文件的最后修改时间。
Last-Modified
与ETag
可以一起使用,但必须遵循缓存优先级。
cache-control
>expries
>etag
>last-modified
需注意:
- F5刷新并不会修改
expires/cache-control
,但是会对last-modified/etag
进行验证。- ctrl+F5强制刷新,对
expires/cache-control
和last-modified/etag
都没有影响,会完全利用浏览器缓存。- 其他刷新操作均会进行缓存校验