浏览器相关面试题
浏览器相关面试题
事件机制
注册事件
注册事件,我们一般使用addEventListener(name, callback, boolean)
函数,该函数支持三个参数,参数说明如下:
name
:代表待注册事件的名字,例如:click
或者mouseover
callback
:代表注册事件的回调函数boolean
:一个boolean
值,为true
代表事件捕获时触发,为false
时代表事件冒泡时触发。参数缺省时默认为false
// 一个注册事件的案例
// 点击DOM元素时。顺序打印出:捕获时触发 冒泡时触发
var box = document.getElementById('box');
box.addEventListener('click', () => {
console.log('捕获时触发');
}, true);
box.addEventListener('click',() => {
console.log('冒泡时触发');
}, false);
事件触发顺序
在浏览器中,事件的触发顺序一般而言依据:捕获->目标阶段->冒泡三个顺序。但事件的触发顺序并不总是按以上顺序执行,当我们给同一个DOM元素同时注册捕获和冒泡事件时,事件的触发顺序是按你注册事件的顺序来执行的。
// 点击DOM元素时。顺序打印出:冒泡时触发 捕获时触发
var box = document.getElementById('box');
box.addEventListener('click',() => {
console.log('冒泡时触发');
}, false);
box.addEventListener('click', () => {
console.log('捕获时触发');
}, true);
阻止事件冒泡: stopPropagation()
和stopImmediaPropagation()
方法都能阻止事件的向上冒泡,但这两者是有区别的:stopImmediaPropagation()
还能阻止目标执行别的注册事件。
// 阻止事件冒泡
// 1. 当不阻止冒泡时,window的click会触发
// 2. 当使用stopPropagation()时,window的click不会被触发
// 3. 当使用stopImmediatePropagation()时,DOM的捕获事件不会触发,window的click不会触发
var box = document.getElementById('box');
box.addEventListener('click',(event) => {
console.log('冒泡时触发');
// event.stopPropagation();
// event.stopImmediatePropagation();
}, false);
box.addEventListener('click', (event) => {
console.log('捕获时触发');
}, true);
window.addEventListener('click', (event) => {
console.log('子元素点击事件向上冒泡时触发');
})
跨域
同源策略
同源策略是指,一个源的客户端脚本在没有明确授权的情况下,不能访问另一个源的客户端脚本。当一个URL和另一个URL,只要协议、域名或者端口号有一个不同,则就会出现跨域。 解决跨域常用方法有:
- JSONP
- CORS
- document.domain
- postMessage
JSONP实现跨域
原理
JSONP实现跨域的原理是利用script
标签没有跨域限制,通过src
指向一个ajax
的URL,最后跟一个回调函数callback
// 一个JSONP跨域的案例
<script src="http://www.baidu.com/getUserInfo?name=张三&callback=jsonp"></script>
function jsonp() {
console.log('JSONP实现跨域');
}
// 实现自己的JSONP
var jsonp = function (url, data, callback) {
var cbName = 'callback_' + new Date().getTime();
var queryString = url.indexOf('?') == -1 ? '?' : '&';
for (var k in data) {
queryString += k + '=' + data[k] + '&';
}
queryString += 'callback=' + cbName;
var script = document.createElement('script');
script.src = url + queryString;
window[cbName] = function (data) {
callback(data);
document.body.removeChild(script);
};
document.body.appendChild(script);
}
// 实测
jsonp('http://api.douban.com/v2/movie/in_theaters', {'count': 1}, function (data) {
console.log(data)
})
CORS实现跨域
提示
CORS:CORS
需要浏览器和后端同时配合才能生效,后端通过设置Access-Control-Allow-Origin
就可以开启哪些域名可以使用CORS
跨域,在进行CORS
跨域请求时,会出现简单请求或者复杂请求。
CORS简单请求:当请求方式为get
,head
、post
之一并且Content-Type
为text/plain
、multipart/form-data
、application/x-www-form-urlencoded
三种之一时,就是简单请求。
CORS复杂请求: 当不符合简单请求时,就是复杂请求,对于复杂请求来说,首先会发送一个option
请求,用于知道服务器是否允许跨域请求。
document.domain实现跨域
提示
document.domain只能用于二级域名相同的情况下
// 域名a.test.com 和域名b.test.com
// 设置如下代码后,二级域名为test.com的网站都能实现跨域
document.domain = 'test.com'
postMessage
提示
postMessage
一般用于获取嵌套在页面中的第三方页面的数据,一个页面发送请求,另外一个页面判断来源并接受请求。
<body>
<iframe src="https://www.baidu.com" frameborder="0"></iframe>
</body>
// 父页面发送请求
window.frames[0].postMessage('getcolor','*');
// 父页面接受请求
window.addEventListener('message',function(e){
console.log(e.data); // 打印red
},false);
// 子页面发送请求
window.addEventListener('message',function(e){
window.parent.postMessage('red','*');
},false);
浏览器存储
提示
浏览器存储有如下四种方法,每种方法都有不同支持,具体特性请参考表格
- cookie
- localStorage
- sessionStorage
- indexDB
Cookie
设置cookie
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
var expires = "expires="+d.toUTCString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
setCookie('name', 'why', 30);
获取cookie
function getCookie(name) {
var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
if (arr = document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
setCookie('name', 'why', 30);
console.log(getCookie('name')); // 打印why
localStorage 和 sessionStorage
设置localStorage和sessionStorage
localStorage.setItem('name','why');
sessionStorage.setItem('age',23);
获取localStorage和sessionStorage
localStorage.setItem('name','why');
console.log(localStorage.getItem('name')); // 打印why
sessionStorage.setItem('age',23);
console.log(sessionStorage.getItem('age'));// 打印23
service worker
Service Worker
是运行在浏览器背后的独立进程,一般可以用来实现缓存功能,实现Service Worker
的话,必须使用https传输协议,一个实现Service Worker
缓存js
文件可以如下写
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('./1.js').then(success => {
console.log('注册成功');
}).catch(error => {
console.log('注册失败');
})
}
缓存机制
缓存位置
提示
缓存在不同的位置,它的优先级是不同的,缓存按优先级可以划分为:
- Service Worker可以让我们自由控制应该缓存哪些文件(PWA实现的重要手段)
- Memory Cache(内存缓存)内存缓存读取效率高,但一旦我们关闭了浏览器,内存缓存也就没有了。
- Disk Cache(硬盘缓存)与内存缓存相比,硬盘缓存具有量大以及时效的两大优点。
- Push Cache当前三者缓存都没有命中时,才会读取Push Cache中的缓存信息,但此种方式的缓存信息时间较短,只在会话
Session
中存在,一旦会话结束也就释放了。 - 当以上缓存都没有命中时,才会发起请求。
缓存策略
提示
通常来说,浏览器缓存策略分为两种:强缓存和协商缓存,缓存策略可通过HTTP Header来实现。
强缓存: 强缓存可以通过设置Expires
和Cache-Control
来实现,强缓存表示在缓存期间,不需要请求,State Code
为200,Cache-Control
可以组合使用多个指令,常见指令如下所示:
协商缓存: 协商缓存表示如果缓存过期了,那么就需要重新发起请求验证资源是否有更新,可通过设置HTTP Header的Last-Modified
和ETag
来实现,如果资源没有改变,State Code
为304
渲染原理
DOM树
浏览器在接受到服务器传递回来的字节流数据后,会经过转换,把0
和1
的字节流数据转换成DOM树结构,会经历如下图所示的过程:
最终可能回渲染成如下的DOM树结构:
CSSDOM
与DOM树渲染过程类似,CSSOM树渲染过程会经历如下图所示的过程:
DOM和CSSOM树合并
当DOM树和CSSOM树渲染完毕后,就会合并在一起形成一个渲染树,渲染树并不是简单的将DOM树和CSSOM树简单的合并在一起,渲染树只包含需要显示的DOM节点。渲染树合并完毕后,然后会根据渲染树进行布局,随后调用GPU进行绘制,显示在屏幕上。