自从换了公司后这几年经常在忙着招人,面试的时候时不时地挖点坑看看别人会不会往里面跳...
很久没写文章了,就顺便总结一些经验吧...
我不确定能不能写完,写多少是多少吧.
当然文章仅代表个人观点,觉得不对怼我就好.
1.GET 和 POST 请求有什么区别
常规问题了,有些文章说 POST 比 GET 安全啥的,说实话确实是无稽之谈, GET有长度上限啥也是浏览器本身给限制的,
还有一个说法是GET 不能传 body,
于是我就用 express 跑了个小服务
const express = require('express')
const app = express()
app.use(require('express-body'))
/* 测试入口 */
app.use('/test', (req, res) => {
res.send('test page here')
})
/* 接口 */
app.use('*', (req, res) => {
res.send(req.body)
})
app.listen('3000', () => {
console.log('express start')
})
然后我在chrome里用 fetch
试了下
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({"hello":"world"});
var requestOptions = {
method: 'GET',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("localhost:3000", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
果不其然,
浏览器给我抛了个异常说 GET 或者 HEAD 的请求不能有body.
然后我用 xhr
试了下
var data = JSON.stringify({"hello":"world"});
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("GET", "http://localhost:3000/");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(data);
没想到请求发出去了,但结果返回的是一个空的json
看了下请求内容,确实没有任何body的影子
貌似在 chrome 上确实验证了这个说法,但,真的是这么一回事吗?
然后我用postman
给服务发了个带body的GET请求
结果请求成功地返回了我传过去的body信息
于是我又用 curl 试了下
curl --location --request GET 'localhost:3000' \
--header 'Content-Type: application/json' \
--data-raw '{
"hello":"world"
}'
也成功了
那这又是怎么一回事?
难不成跟同源策略一样又是浏览器搞的方言?
于是我去搜了下,大概找到了如下资料 whenToUseGet
看到了如下说明, 我英语不大好大家自行翻译吧
By convention, when GET method is used, all information required to identify the resource is encoded in the URI. There is no convention in HTTP/1.1 for a safe interaction (e.g., retrieval) where the client supplies data to the server in an HTTP entity body rather than in the query part of a URI. This means that for safe operations, URIs may be long. The case of large parameters to a safe operation is not directly addressed by HTTP as it is presently deployed. A QUERY or "safe POST" or "GET with BODY" method has been discussed (e.g., at the December 1996 IETF meeting) but no consensus has emerged.
然后StackOverflow有这么一个问题 HTTP GET with request body大家可以看下里面的回答
协议中有这么一句话
A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.
The response to a GET request is cacheable; a cache MAY use it to
satisfy subsequent GET and HEAD requests unless otherwise indicated
by the Cache-Control header field ([Section 5.2 of [RFC7234]](https://tools.ietf.org/html/r...
也就是说, 浏览器这么做其实也是遵守了HTTP的规范,只是其他的客户端也可以不搭理这个规范,就是传也行不传也行,当然协议是推荐你不传的....如果传了的话就不能保证幂等了,不能幂等就意味着缓存就无意义了.
2.HTTP 缓存
各类中文资料常说的协商缓存强缓存啥的我就不多说了,这俩名词的出处已经无法考证了,可能就是为了方便大家理解缓存特性吧,协议内是没有这个说法的,给一个复杂特征定一个容易记的名词是一种常见的做法,不过传来传去传多了大家都以为真有这么一回事了
然后来看下 no-cache
和 no-store
这个是 no-cache
的说明
The "no-cache" response directive indicates that the response MUST
NOT be used to satisfy a subsequent request without successful
validation on the origin server. This allows an origin server to
prevent a cache from using it to satisfy a request without contacting
it, even by caches that have been configured to send stale responses.
就是必须通过服务器验证来确定当前资源能否使用
而 no-store
的说明
The "no-store" response directive indicates that a cache MUST NOT
store any part of either the immediate request or response. This
directive applies to both private and shared caches. "MUST NOT
store" in this context means that the cache MUST NOT intentionally
store the information in non-volatile storage, and MUST make a
best-effort attempt to remove the information from volatile storage
as promptly as possible after forwarding it.
大致就是说不想走缓存就走这个
所以一个误区就是,不想缓存你应该在 Cache-Control
内走 no-store
3.在 <Meta /> 标签内挂 no-cache 能不能阻止缓存页面?
大家应该经常在各类文章里看到,如何让当前网页不缓存,最简单粗暴的方法就是在header里加上
<meta http-equiv="cache-control" content="no-store" />
<meta http-equiv="Pragma" content="no-store" />
<meta http-equiv="expires" content="0" />
于是我按照上述说明在本地挂了个页面
在第二次访问的时候
直接快乐地304回来了,完全没效果嘛.仔细想下也是嘛,http协议走不走缓存是通过请求头来判定的,还没到body那层就给你判定了要不要走缓存了,再说了,服务器还要知道你发的是html文本,然后解析里面是不是有这么一个meta标签,里面还得有对应的字段啥的,如果是jsp的动态页面你觉得这层拦截操作是在apache里做呢还是在nginx上做?
所以可以证明这个东西无效咯?
作为一名严谨的程序员,我肯定不能这么快下这个结论的嘛,网上虽然各种文章你抄我我抄你,但很多东西并非空穴来风.
于是我去w3翻了一大圈资料,给我找到了一些东西: meta
HTML3.2里就有了,然后我继续往后翻
众所周知的,w3c在19年放弃发布html和dom的标准,whatwg一家独大了
于是我转头在在whatwg里继续翻
根本没有嘛.
然后我跑去翻了下
好家伙,唯一和缓存相关的东西根本不能实现嘛.
所以我们大抵是有一些结论了.这东西不靠谱,挂上去也没效果,该http干的事还是http干.
4.localStorage 和 sessionStorage
大家一般聊到 localStorage
和 sessionStorage
都会说到这个体积的问题,一般是说最多只能用 5MB
,
但实际情况真的是这样的吗?
因为比较懒,所以我去找了个小网站测试了下storage
在chrome下确实是5兆左右,
然后我换了个苹果自带的浏览器 safari
结果localStorage
只有5兆,sessionStorage
超过10兆
浏览器的差异一下子就出来了,这和前面说的5兆没啥关系了呀,但这5兆的说法肯定不是空穴来风,于是我又跑去w3看了下协议说明
W3: WebStorage
User agents should limit the total amount of space allowed for storage areas, because hostile authors could otherwise use this feature to exhaust the user's available disk space.
User agents should guard against sites storing data under their origin's other affiliated sites, e.g. storing up to the limit in a1.example.com, a2.example.com, a3.example.com, etc, circumventing the main example.com storage limit.
User agents may prompt the user when quotas are reached, allowing the user to grant a site more space. This enables sites to store many user-created documents on the user's computer, for instance.
User agents should allow users to see how much space each domain is using.
A mostly arbitrary limit of five megabytes per origin is suggested. Implementation feedback is welcome and will be used to update this suggestion in the future.
For predictability, quotas should be based on the uncompressed size of data stored.
上面大致写了如下的意思,为了避免某些别有用心的开发人员耗尽你电脑上的磁盘空间,所以对本地存储的资源体积进行了限制,但是依旧可以被通过改变域名的方式继续存更多的数据,建议是每个源5兆左右.
又是建议... 也就是说,搞大搞小也就是客户端自己看着办,只要你觉得合理,你搞5个G也没问题.
Set 和 Map
一般常见的 Set
能干啥大家应该都会说用来给数组去重,但如果这个数组是[{name: 1}, {name: 2}, {name: 3}, {name: 1}]
这种情况呢,这时候我们就会给面试者挖出两个坑.
用Map
的鸭式辨型解题
const data = [{name:1}, {name:2}, {name:3}, {name:1}]
const maps = new Map()
data.forEach((item) => {
maps.set(item.name, item)
})
const result = [...maps].map((item) =>item[1])
表面上确实是解决了字面量相关的问题
但是一旦这个对象里面有额外的值的情况,那前面说的方式必然出问题
而第二个坑是 Set 本身就是个指针问题
const a = {name: 1}
const b = {name: 2}
const c = Array.from(new Set([a, a, b, a]))
我们执行这个操作以后只会出现 [{name:1}, {name:2}]
的结果,
所以并非对象导致的,而是对象的指针导致的.
所以如果我们能解决指针的问题,或许就能解决这个问题,
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。