前言
我目前四年开发经验,目前正在准备考虑换一个工作环境,因此最近经历的面试比较多,大厂(滴滴,美团,平安,招行等)小厂都有,最好的结果是滴滴,目前已收到offer。本文从这期间遇到的一个简单的问题“请阐述sessionStorage、localStorage以及cookie三者的区别?”的分析,进而来谈谈一些学习心得。那么,从现在起,我们开始探索里面的技术细节,来尝试回答这个问题。
我们从一下几个点来做比较:存储大小,浏览器兼容性,是否随HTTP发送,存储时间,存储形式等几个方面来分别比较。
0、浏览器的同源策略
同源的定义:如果两个 URL 的 protocol、port(如果有指定的话)和 host 都相同的话,则这两个 URL 是同源。 示例:
| URL | 结果 | 原因 |
|---|---|---|
| store.company.com/dir2/other.… | 同源 | 只有路径不同 |
| store.company.com/dir/inner/a… | 同源 | 只有路径不同 |
| store.company.com/secure.html | 失败 | 协议不同 |
| store.company.com:81/dir/etc.htm… | 失败 | 端口不同 ( http:// 默认端口是80) |
| news.company.com/dir/other.h… | 失败 | 主机不同 |
访问存储在浏览器中的数据,如 localStorage 和 IndexedDB,是以源进行分割。每个源都拥有自己单独的存储空间,一个源中的 JavaScript 脚本不能对属于其它源的数据进行读写操作。
只有我们理解清楚了什么是同源策略,我们才能对三种客户端存储技术展开分析。
1、cookie
由于http是无状态的协议,当我们需要保持用户的登录状态等操作的时候,服务器无法识别两次的请求是否来源于同一个浏览器,因此产生了cookie,它是服务器发送到用户浏览器并保存在本地的一小块数据。 服务器收到 HTTP 请求时,服务器可以在响应头里面添加一个 Set-Cookie 选项。浏览器收到响应后通常会保存下 Cookie,之后对该服务器每一次请求中都通过 Cookie 请求头部将 Cookie 信息发送给服务器。
会话期 Cookie 是最简单的 Cookie,浏览器关闭之后它会被自动删除,它仅在会话期内有效。会话期Cookie不需要指定过期时间(Expires)或者有效期(Max-Age)。如:
document.cookie = 'name=yangxu'
有些浏览器提供了会话恢复功能,这种情况下即使关闭了浏览器,会话期Cookie 也会被保留下来。
持久性 Cookie 的生命周期取决于过期时间(Expires)或有效期(Max-Age)指定的一段时间。如:
document.cookie = 'name=yangxu;expires=Thu, 22 Apr 2021 07:33:10 GMT'
Domain属性: 指定了哪些主机可以访问到 Cookie。如果不指定,默认为 当前origin,不包含子域名。如果指定了domain,则一般包含子域名
document.cookie = 'name=yangxu;domain=baidu.com;expires=Thu, 22 Apr 2021 07:33:10 GMT'
则可以在cookie的有效期内,所有百度及百度的子域名都可以访问到。
当前大多数浏览器遵循 RFC 6265,设置 Domain 时 不需要加前导点。浏览器不遵循该规范,则需要加前导点,例如:Domain=.mozilla.org
Path属性: 标识指定了主机下的哪些路径可以访问到 Cookie,如:
document.cookie = 'name=yangxu;domain=.baidu.com;path=/item/2008年北京奥运会/329733;expires=Thu, 22 Apr 2021 07:33:10 GMT'
仅仅在页面baike.baidu.com/item/2008%E… 才可以访问到刚才设置的cookie,在百度百科的其余页面则不能访问到这个cookie。
SameSite属性: Cookie 允许服务器要求某个 cookie 在跨站请求时不会被发送。
js通过 document.cookie 是无法访问带有 HttpOnly 属性的cookie的,而保存session的cookie都有HttpOnly这个属性;
在JS中设置cookie,通过document.cookie = '一个新的cookie'设置,但是这并不会覆盖之前的cookie,直接通过document.cookie可以获取到当前的所有cookie。
关于cookie的存储大小,我翻阅《JavaScript高级程序设计》(第四版),作者是这样阐述的:
通常,只要遵守以下大致的限制,就不会遇到任何问题:
1、不超过300个cookie;
2、每个cookie;
3、每个域不超过20个cookie;
4、每个域不超过81920字节;
如果cookie超过了单个域的上限,浏览器就会删除之前的cookie,以便为新的cookie腾出空间。最好保证cookie的大小不超过4096字节,上下可能有一个字节的误差,这个大小限制适用于一个域下面的所有cookie,而不是单个cookie。
cookie几乎不存在兼容性问题。但是浏览器SameSite属性的支持情况如下:
2、localStorage
Storage对象提供了访问特定域下的会话存储或本地存储的功能。我个人的理解是,localStorage和sessionStorage是Storage对象的不同实现。localStorage 中被存储的键值对总是以UTF-16 DOMString 的格式所存储,其使用两个字节来表示一个字符。 (需要注意, 和js对象相比, 键值对总是以字符串的形式存储意味着数值类型会自动转化为字符串类型)。所以接下来我们可以来一个骚操作:
const a = Symbol("a");
localStorage.setItem(a, '123');
//Uncaught TypeError: Cannot convert a Symbol value to a string
const b = {};
localStorage.setItem(b, '456')
// 因为会自动调用Symbol.toPrimitive方法,所以实际上我们设置了一个key为"[object Object]"的键。
由于ES6提供了我们可以控制toPrimitive的行为的方式,因此:
const obj = { [Symbol.toPrimitive]() { return "hello world"; } }
localStorage.setItem(obj, 'Hello');
//实际上我们设置了一个key为"hello world"的键。
localStorage可以通过localStorage.setItem方法设置键值,也可以直接通过localStorage.xxx = '需要存储的内容'设置,但是我个人的经验不太推荐使用第二种方式,如果你不小心覆盖了localStorage对象上本身的方法,你就是改变了浏览器的行为了,这对于程序来说是有一定的潜在隐患的。 localStorage是永久存储的,只要你不手动清除。localStorage存储的内容是不会随http请求发送的。localStorage在每个域下面的存储大小是5M(不同的浏览器可能支持情况不一致)。 Storage的浏览器支持情况如下:
3、sessionStorage
sessionStorage的特性基本上都与localStorage一致,此处不再赘述,而本节重点研究sessionStorage的失效特征。 1、页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来的页面会话。
sessionStorage.setItem(1, 1)
当我们刷新页面,其实sessionStorage还在的哦,但是我们如果关闭当前页面,则sessionStorage将会失效。
2、在新标签或窗口打开一个页面时会复制顶级浏览会话的上下文作为新会话的上下文,这点和 session cookies 的运行方式不同。 这句话不是特别好理解,我们以实际的例子来解释这个规则。
window.open("https://www.baidu.com")
window.location.href = "https://www.baidu.com"
//可以通过这两种方式拿到复制的sessionStorage
使用此方式会复制sessionStorage。
//假设我们现在是在a.html页面
<a target="_blank" href="b.html">打开新页面</a>
但是,正常情况这样也是会复制sessionStorage的,根据我的测试,假设我现在有A.html,B.html,我现在A.html中设置sessionStorage,然后再打开B.html,然后再到A.html操作sessionStorage,然后再从B.html打开到C.html,此时C.html是无法复制A.HTML里面的内容的。因此,我觉得把“顶级”理解为“上级”更为恰当(这是我在后面在Chrome80上的测试结果)。 但当时撰写此文的时候,恰好我用的是Chrome89,一度让我怀疑人生,后面查阅资料得知Chrome89停止了支持这样的特性。传送门,但是通过JS的window.open是可以复制的,大家可以拿自己的小本本记一下。
3、打开多个相同的URL的Tabs页面,会创建各自的sessionStorage,关闭对应浏览器窗口(Window)/ tab,会清除对应的sessionStorage。
4、总结
根据上面的一些验证,我们得出了一下结论:
| 项目 | 存储大小 | 浏览器兼容性 | 是否随HTTP发送 | 存储时间 | 是否跨域 |
|---|---|---|---|---|---|
| cookie | 4K | 几乎所有浏览器都支持,SameSite属性支持较差 | 是 | 临时cookie浏览器关闭清除,永久cookie取决于Expires设置 | Domain可以控制其在当前域及其子域共享 |
| localStorage | 每个域下面5M(不同浏览器的实现可能有差别) | IE8+ | 否 | 不过期 | 否 |
| sessionStorage | 每个域下面5M(不同浏览器的实现可能有差别) | IE8+ | 否 | 在页面关闭清除 | 否 |
我之所以会写这篇文章,是因为我在之前的面试中折戟于平安科技的面试官提出的这个问题,以我当时所掌握的程度,显然是给不出面试官想要的答案的。我个人觉得作为一个前端开发人员,还是应该追求技术的深度,“好读书,不求甚解”不适用于一个技术人的追求。可能就是某一个不起眼的小特性你没有认真品味揣摩,会给团队或项目带来一些不好的影响。
从我最近面试大公司的结果来看的话,大厂除了对于一些基础算法有考察以外,更多的是注重于对于一些框架的底层实现、设计模式、浏览器渲染原理、计算机网络原理等基础知识的考察,所以各位读者平时可以注重于源码学习和基础能力培养。一方面要注重于技术广度(尽可能的涉猎自己所需要的一切技术知识点),另一方面对于一些常用的框架或工具研究一下底层的实现,能达到一定的技术深度,同时可以帮助我们快速定位实际开发中所遇到的问题。不要迷恋框架,这是一个很重要的点。如果说离开了框架就不会高效的完成开发任务的话,那么其实你离失业并不遥远。我个人的观点是掌握好主流的A-V-R三剑客中其中一个框架,对另外两个框架有一些了解即可。
还有一个很重要的点就是要避免浮躁,不要觉得这个知识点很简单,就静不下心去看。“温故而知新,可以为师矣”,老祖宗传下来的好东西那真的不是浪得虚名。
最后祝愿各位奋斗在大厂之路上的同行们早日上岸:best wishes!
由于笔者水平有限,写作过程中难免出现错误,若有纰漏,请各位读者指正,请联系作者本人,邮箱404189928@qq.com,你们的意见将会帮助我更好的进步。
5、参考资料
1、developer.mozilla.org/zh-CN/docs/…
2、developer.mozilla.org/zh-CN/docs/…
3、developer.mozilla.org/zh-CN/docs/…
4、developer.mozilla.org/zh-CN/docs/…
5、developer.mozilla.org/zh-CN/docs/…
6、《JavaScript高级程序设计》(第四版)