事件机制
事件触发有三个阶段
window往事件触发处传播(事件捕获)
传播到事件触发处时触发注册事件
从事件触发处往window传播,遇到注册得冒泡事件触发(事件冒泡)
addEventListener参数,第一个是事件类型,第二个是回调函数,第三个是:
可以为布尔true捕获,fasle冒泡
也可以为对象
{
capture,
once,
passive
}
事件只触发在目标上,使用 stopPropagation 阻止事件得进一步传播,捕获和冒泡都可以阻止,stopImmediatePropagation 也可以阻止事件,还可以阻止目标执行别的注册事件。
事件代理,通过给父节点注册事件,优点:
节省内存
不用给子节点注销事件
跨域
同源策略:协议、域名、端口
JSONP
利用 script 标签没有跨域限制得漏洞,通过script标签指向需要访问的地址并提供一个回调函数来做接收数据。
function jsonp(url, jsonpCallback, success) {
let script = document.createElement('script')
script.src = url
script.async = true
script.type = 'text/javascript'
window[jsonpCallback] = function(data) {
success && success(data)
}
document.body.appendChild(script)
}
jsonp('http://xxx', 'callback', function(value) {
console.log(value)
})
CORS
CORS需要浏览器和后端同时支持,IE8、9需要通过XDomainRequist来实现。
浏览器会自动进行CORS通信,服务端设置 ACccess-Control-Allow-Origin 就开启CORS了,该属性表示哪些域名可以访问资源,通配符就是所有域名都可以访问到资源。
document.domain
该方式只能用于二级域名相同的情况,比如a.test.com和b.test.com
需要给页面添加 document.domain = "test.com" 表示二级域名都相同就可以实现跨域。
postMessage
该方式用于获取嵌入页面中得第三方页面数据
window.parent.postMessage('message', 'http://test.com')
var mc = new MessageChannel()
mc.addEventListener('message', event => {
var origin = event.origin || event.originalEvent.origin
if (origin === 'http://test.com') {
console.log('验证通过')
}
})
Event Loop
JS是单线程,和浏览器交互如果是多线程会混乱(删节点,加节点)
JS在执行时会产生执行环境,并顺序加入到执行栈中,异步的代码会被挂起并加入到Task(有多种Task)队列,一旦执行栈执行完为空,EventLoop就会从Task队列中拿出需要执行的异步代码放入执行栈中执行,本质上JS的异步还是同步。
不同的任务源会分配不同的Task队列中,任务源分为:
微任务 microtask || jobs
宏任务 macrotask || task
微任务包括 process.nextTick ,promise ,Object.observe ,MutationObserver
宏任务包括 script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering
所以正确的一次 Event loop 顺序:
执行同步代码,这属于宏任务
执行栈为空,查询是否有微任务需要执行
执行所有微任务
必要的话渲染 UI
然后开始下一轮 Event loop,执行宏任务中的异步代码
console.log('script start')
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')
Node中的 EventLoop
分为六个阶段,按照顺序反复运行:
timers
执行setTimeout和setInterval,时间范围[1, 2147483647],不在范围将被设置为1
I/O
执行除了close事件、定时器、setImmediate的回调
idle, prepare
内部实现
poll
执行到点的定时器
执行poll队列中的事件
如果poll中没有定时器
如果有setImmediate需要执行就进入check阶段
如果没有等待回调被加入到队列并立即执行回调
如果有别的定时器需要被执行,会回到timer阶段执行回调
check
执行setImmediate
close callbacks
执行close事件
setTimeout(() => {
console.log('setTimeout')
}, 0)
setImmediate(() => {
console.log('setImmediate')
})
var fs = require('fs')
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout')
}, 0)
setImmediate(() => {
console.log('immediate')
})
})
Node 中的 process.nextTick 会先于其他 microtask 执行。
存储
cookie
服务器生成,可以设置过期时间,4K,与服务端通信每次携带header中
localStorage
除非被清理不然一直存在,5M
sessionStorage
页面关闭就清理,5M
indexDB
除非被清理不然一直存在,无限
cookie属性
value 如果用于保存用户登陆状态,应该加密
http-only 不能通过JS访问Cookie,减少XSS攻击
secure 只能在HTTPS协议的请求中携带
same-site 规定浏览器不能在跨域请求中携带Cookie,减少CSRF攻击
Service Worker
本质上充当 Web 应用程序与浏览器之间的代理服务器,也可以在网络可用时作为浏览器和网络间的代理,创建有效的离线体验,拦截网络请求并基于网络是否可用以及更新的资源是否驻留在服务器上来采取适当的动作,允许访问推送通知和后台同步 API,通常用来做缓存文件提高首屏速度。
if (navigator.serviceWorker) {
navigator.serviceWorker
.register('sw.js')
.then(function(registration) {
console.log('service worker 注册成功')
})
.catch(function(err) {
console.log('servcie worker 注册失败')
})
}
self.addEventListener('install', e => {
e.waitUntil(
caches.open('my-cache').then(function(cache) {
return cache.addAll(['./index.html', './index.js'])
})
)
})
self.addEventListener('fetch', e => {
e.respondWith(
caches.match(e.request).then(function(response) {
if (response) {
return response
}
console.log('fetch source')
})
)
})