浏览器
1.1 cookie sessionStorage localStorage 区别
共同点:
都是保存在浏览器端、且同源的
区别:
cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递,而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下
存储大小限制也不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭之前有效;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭
作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
web Storage支持事件通知机制,可以将数据更新的通知发送给监听者
Storage的api接口使用更方便
1.2 如何写一个会过期的localStorage,说说想法
惰性删除 和 定时删除
惰性删除
惰性删除是指,某个键值过期后,该键值不会被马上删除,而是等到下次被使用的时候,才会被检查到过期,此时才能得到删除。
var lsc = (function (self) {
var prefix = 'lsc_'
/**
* 增加一个键值对数据
* @param key 键
* @param val 值
* @param expires 过期时间,单位为秒
*/
self.set = function(key, val, expires) {
key = prefix + key;
val = JSON.stringify({'val': val, 'expires': new Date().getTime() + expires * 1000});
localStorage.setItem(key, val);
};
/**
* 读取对应键的值数据
* @param key 键
* @returns {null|*} 对应键的值
*/
self.get = function(key) {
key = prefix + key;
var val = localStorage.getItem(key);
if (!val) {
return null;
}
val = JSON.parse(val);
if (val.expires < new Date().getTime()) {
localStorage.removeItem(key);
return null;
}
return val.val;
};
return self;
}(lsc || {}));
定时删除
定时删除是指,每隔一段时间执行一次删除操作
- 随机测试20个设置了过期时间的key。
- 删除所有发现的已过期的key。
- 若删除的key超过5个则重复步骤****1,直至重复500次。
var lsc = (function (self) {
var prefix = 'lsc_'
var list = [];
//初始化
self.init = function () {
var keys = Object.keys(localStorage);
var reg = new RegExp('^' + prefix);
var temp = [];
//遍历所有localStorage中的所有key
for (var i = 0; i < keys.length; i++) {
//找出可过期缓存的key
if (reg.test(keys[i])) {
temp.push(keys[i]);
}
}
list = temp;
};
self.init();
self.check = function () {
if (!list || list.length == 0) {
return;
}
var checkCount = 0;
while (checkCount < 500) {
var expireCount = 0;
// 随机测试20个设置了过期时间的key
for (var i = 0; i < 20; i++) {
if (list.length == 0) {
break;
}
var index = Math.floor(Math.random() * list.length);
var key = list[index];
var val = localStorage.getItem(list[index]);
// 从list中删除被惰性删除的key
if (!val) {
list.splice(index, 1);
expireCount++;
continue;
}
val = JSON.parse(val);
// 删除所有发现的已过期的key
if (val.expires < new Date().getTime()) {
list.splice(index, 1);
localStorage.removeItem(key);
expireCount++;
}
}
// 若删除的key不超过5个则跳出循环
if (expireCount <= 5 || list.length == 0) {
break;
}
checkCount++;
}
}
//每隔一秒执行一次定时删除
window.setInterval(self.check, 1000);
return self;
}(lsc || {}));
1.3 localStorage 能跨域吗
不能
解决办法
- 通过postMessage来实现跨源通信
- 可以实现一个公共的iframe部署在某个域名中,作为共享域
- 将需要实现localStorage跨域通信的页面嵌入这个iframe
1.4 memory cache 如何开启
memory cache 如何开启是一种比较特殊的缓存,他不受max-age、no-cache等配置的影响,即使我们不设置缓存,如果当前的内存空间比较充裕的话,一些资源还是会被缓存下来。但这种缓存是暂时的,一旦关闭了浏览器,这一部分用于缓存的内存空间就会被释放掉。如果真的不想使用缓存,可以设置no-store,这样,即便是内存缓存,也不会生效
1.5 localstorage的注意哪些问题
- 兼容性问题
- localStorage在浏览器的隐私模式下面是不可读取的
- localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡
- localStorage不能被爬虫抓取到
1.6 浏览器输入URL发生了什么
- URL 解析
- DNS 查询
- TCP 连接
- 处理请求
- 接受响应
- 渲染页面
1.7 浏览器是如何渲染页面的?
不同浏览器内核渲染机制有所区别
- HTML 被 HTML 解析器解析成 DOM 树;
- CSS 被 CSS 解析器解析成 CSSOM 树;
- 结合 DOM 树和 CSSOM 树,生成一棵渲染树(Render Tree),这一过程称为 Attachment;
- 生成布局(flow),浏览器在屏幕上“画”出渲染树中的所有节点;
- 将布局绘制(paint)在屏幕上,显示出整个页面。
webkit
Gecko
1.8 重绘、重排
概念
- 重排(Reflow):当渲染树的一部分必须更新并且节点的尺寸发生了变化,浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树
- 重绘(Repaint):是在一个元素的外观被改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。比如改变某个元素的背景色、文字颜色、边框颜色等等
区别:
重绘不一定需要重排(比如颜色的改变),重排必然导致重绘(比如改变网页位置)
引发重排
- 添加、删除可见的dom
- 元素的位置改变
- 元素的尺寸改变(外边距、内边距、边框厚度、宽高、等几何属性)
- 页面渲染初始化
- 浏览器窗口尺寸改变
- 获取某些属性。当获取一些属性时,浏览器为取得正确的值也会触发重排,它会导致队列刷新,这些属性包括:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle() (currentStyle in IE)。所以,在多次使用这些值时应进行缓存
优化方案
浏览器会维护1个队列,把所有会引起重排,重绘的操作放入这个队列,等队列中的操作到一定数量或者到了一定时间间隔,浏览器就会flush队列,进行一批处理,这样多次重排,重绘变成一次重排重绘
减少 reflow/repaint:
不要一条一条地修改 DOM 的样式。可以先定义好 css 的 class,然后修改 DOM 的className。
不要把 DOM 结点的属性值放在一个循环里当成循环里的变量。
为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会reflow 的。
千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。(table及其内部元素除外,它可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。这也是为什么我们要避免使用table做布局的一个原因。)
不要在布局信息改变的时候做查询(会导致渲染队列强制刷新)
1.9 事件循环(Event loop)
主线程从"任务队列"中读取执行事件,这个过程是循环不断的,这个机制被称为事件循环
JavaScript 的事件分两种
- 宏任务:包括整体代码 script,setTimeout,setInterval
- 微任务:Promise.then(非 new Promise),process.nextTick(node 中)
具体执行:
事件的执行顺序——先执行宏任务,然后执行微任务,任务有同步的任务和异步的任务,同步的进入主线程,异步的进入 Event Table 并注册函数,异步事件完成后,会将回调函数放在队列中,如果还有异步的宏任务,那么就会进行循环执行上述的操作
主 线程会不断从任务队列中按顺序取任务执行,每执行完一个任务都会检查microtask队列是否为空(执行完一个 任务的具体标志是函数执行栈为空),如果不为空则会一次性执行完所有microtask。然后再进入下一个循环去 任务队列中取下一个任务执行
详细步骤:
选择当前要执行的宏任务队列,选择一个最先进入任务队列的宏任务,如果没有宏任务可以选择,则会 跳转至microtask的执行步骤。
将事件循环的当前运行宏任务设置为已选择的宏任务。
运行宏任务。
将事件循环的当前运行任务设置为null。
将运行完的宏任务从宏任务队列中移除。
microtasks步骤:进入microtask检查点。
更新界面渲染。
返回第一步。
执行进入microtask检查的的具体步骤如下:
- 设置进入microtask检查点的标志为true。
- 当事件循环的微任务队列不为空时:选择一个最先进入microtask队列的microtask;设置事
件循环的当 前运行任务为已选择的microtask;运行microtask;设置事件循环的当前运行任务
为null;将运行结束 的microtask从microtask队列中移除。
- 对于相应事件循环的每个环境设置对象(environment settings object),通知它们哪些
promise为 rejected。
- 清理indexedDB的事务。
- 设置进入microtask检查点的标志为false。
注意
当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行
1.10 let a = 1 挂载在哪里?
var a 挂载在window下。而let是挂载在 全局函数下面