来聊聊面试中的几个不大不小的坑

2021/2/4 14:40:37

本文主要是介绍来聊聊面试中的几个不大不小的坑,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

自从换了公司后这几年经常在忙着招人,面试的时候时不时地挖点坑看看别人会不会往里面跳...
很久没写文章了,就顺便总结一些经验吧...
我不确定能不能写完,写多少是多少吧.

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));

果不其然,
image.png
浏览器给我抛了个异常说 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
image.png
看了下请求内容,确实没有任何body的影子
image.png
貌似在 chrome 上确实验证了这个说法,但,真的是这么一回事吗?

然后我用postman给服务发了个带body的GET请求
image.png

结果请求成功地返回了我传过去的body信息
image.png

于是我又用 curl 试了下

curl --location --request GET 'localhost:3000' \
--header 'Content-Type: application/json' \
--data-raw '{
    "hello":"world"
}'

image.png
也成功了

那这又是怎么一回事?
难不成跟同源策略一样又是浏览器搞的方言?
于是我去搜了下,大概找到了如下资料 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-cacheno-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" />

于是我按照上述说明在本地挂了个页面
img.png
在第二次访问的时候
直接快乐地304回来了,完全没效果嘛.仔细想下也是嘛,http协议走不走缓存是通过请求头来判定的,还没到
所以可以证明这个东西无效咯?
作为一名严谨的程序员,我肯定不能这么快下这个结论的嘛,网上虽然各种文章你抄我我抄你,但很多东西并非空穴来风.
于是我去w3翻了一大圈资料,给我找到了一些东西: meta
image.png
HTML3.2里就有了,然后我继续往后翻
众所周知的,w3c在19年放弃发布html和dom的标准,whatwg一家独大了
于是我转头在在whatwg里继续翻

  • WHATWG-The Meta Element

image.png
根本没有嘛.

然后我跑去翻了下

  • WHATWG-Meta Extensions

image.png
好家伙,唯一和缓存相关的东西根本不能实现嘛.
所以我们大抵是有一些结论了.这东西不靠谱,挂上去也没效果,该http干的事还是http干.

4.localStorage 和 sessionStorage

大家一般聊到 localStoragesessionStorage 都会说到这个体积的问题,一般是说最多只能用 5MB,
但实际情况真的是这样的吗?

因为比较懒,所以我去找了个小网站测试了下storage
image.png
在chrome下确实是5兆左右,
然后我换了个苹果自带的浏览器 safari
image.png
结果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}]的结果,
所以并非对象导致的,而是对象的指针导致的.
所以如果我们能解决指针的问题,或许就能解决这个问题,



这篇关于来聊聊面试中的几个不大不小的坑的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程