本文收录于 GitHub 日问: DailyQuestion,内含大厂内推机会、面经大全及若干面试题,天天学习五分钟,一年进入大厂中。javascript
在 Issue 中交流与讨论: 01 什么是防抖和节流,他们的应用场景有哪些
防抖,顾名思义,防止抖动,以避免把一次事件误认为屡次,敲键盘就是一个天天都会接触到的防抖操做。css
想要了解一个概念,必先了解概念所应用的场景。在 JS 这个世界中,有哪些防抖的场景呢html
代码以下,能够看出来防抖重在清零 clearTimeout(timer)
前端
function debounce (f, wait) { let timer return (...args) => { clearTimeout(timer) timer = setTimeout(() => { f(...args) }, wait) } }
节流,顾名思义,控制水的流量。控制事件发生的频率,如控制为1s发生一次,甚至1分钟发生一次。与服务端(server)及网关(gateway)控制的限流 (Rate Limit) 相似。java
scroll
事件,每隔一秒计算一次位置信息等代码以下,能够看出来节流重在加锁 timer=timeout
node
function throttle (f, wait) { let timer return (...args) => { if (timer) { return } timer = setTimeout(() => { f(...args) timer = null }, wait) } }
clearTimeout
。防抖能够比做等电梯,只要有一我的进来,就须要再等一下子。业务场景有避免登陆按钮屡次点击的重复提交。timer=timeout; timer=null
。节流能够比做过红绿灯,每等一个红灯时间就能够过一批。<blockquote> 更多描述: 如何获取浏览器的惟一标识,原理是什么 </blockquote>linux
在 Issue 中交流与讨论: 02 在前端开发中,如何获取浏览器的惟一标识
因为不一样的系统显卡绘制 canvas
时渲染参数、抗锯齿等算法不一样,所以绘制成图片数据的 CRC
校验也不同。css3
function getCanvasFp () { const canvas = document.getElementById('canvas') const ctx = canvas.getContext('2d') ctx.font = '14px Arial' ctx.fillStyle = '#ccc' ctx.fillText('hello, shanyue', 2, 2) return canvas.toDataURL('image/jpeg') }
所以根据 canvas
能够获取浏览器指纹信息。nginx
canvas
,获取 base64
的 dataurlmd5
摘要计算,获得指纹信息可是对于常见的需求就有成熟的解决方案,若在生产环境使用,可使用如下库git
它依据如下信息,获取到浏览器指纹信息,而这些信息,则成为 component
canvas
webgl
UserAgent
AudioContext
requestIdleCallback(function () { Fingerprint2.get((components) => { const values = components.map((component) => component.value) const fp = Fingerprint2.x64hash128(values.join(''), 31) }) })
在 fingerprintjs2
中,对于 component
也有分类
component
同一设备跨浏览器也能够获得相同的值,有些独立浏览器,获得不一样的值component
刷新后值就会发生变化,称为不稳定组件在实际业务中,可根据业务选择合适的组件
const options = { excludes: {userAgent: true, language: true} }
根据 canvas
能够获取浏览器指纹信息
canvas
,获取 base64
的 dataurlmd5
摘要计算,获得指纹信息若在生产环境使用,可使用 fingerprintjs2,根据业务需求,如单设备是否可跨浏览器,以此选择合适的 component
在 Issue 中交流与讨论: 03 在服务端应用中如何得到客户端 IP
若是有 x-forwarded-for
的请求头,则取其中的第一个 IP,不然取创建链接 socket 的 remoteAddr。
而 x-forwarded-for
基本已成为了基于 proxy 的标准HTTP头,格式以下,可见第一个 IP 表明其真实的 IP,能够参考 MDN X-Forwarded-For
X-Forwarded-For: 203.0.113.195, 70.41.3.18, 150.172.238.178 X-Forwarded-For: <client>, <proxy1>, <proxy2>
如下是 koa
获取 IP 的方法
get ips() { const proxy = this.app.proxy; const val = this.get(this.app.proxyIpHeader); let ips = proxy && val ? val.split(/\s*,\s*/) : []; if (this.app.maxIpsCount > 0) { ips = ips.slice(-this.app.maxIpsCount); } return ips; }, get ip() { if (!this[IP]) { this[IP] = this.ips[0] || this.socket.remoteAddress || ''; } return this[IP]; },
参见源码: https://github.com/koajs/koa/...
<blockquote> 更多描述: 假设有一个字符串 hello. hello. hello.
须要替换为 AAA
,即把 hello.
替换为 A
</blockquote>
在 Issue 中交流与讨论: 04 js 如何所有替代一个子串为另外一个子串
若是须要全量替换字符串,可使用 String.prototype.replace(re, replacer)
,其中正则表达式须要开启 global
flag
const s = 'foo foo foo' s.replce(/foo/g, 'bar')
那如题中,是否可使用正则表达式来替代子串
答:不能够,由于使用子串构建正则时,有可能有特殊字符,就有可能出现问题,以下
// 期待结果: 'AhelloX hello3 ' > 'hello. helloX hello3 '.replace(new RegExp('hello. ', 'g'), 'A') < "AAA"
而在 javascript
中替换子串只能使用一种巧妙的办法:str.split('foo').join('bar')
> 'hello. hello. hello. '.split('hello. ').join('A') < "AAA"
真是一个巧(笨)妙(拙)的办法啊!!!!!大概 TC39 也意识到了一个问题,因而出了一个新的 API,在 ESNext
中
String.prototype.replaceAll() 'aabbcc'.replaceAll('b', '.'); // 'aa..cc'
详细文档在 String.prototype.replaceAll
两种办法
str.split('foo').join('bar')
str.replaceAll('foo', 'bar')
,在 ESNext
中,目前支持性很差<blockquote> 更多描述: 在编写脚本时,有时会出现内存过大发生 OOM 的事情,那咱们如何得知某个进程的内存?另外又如何监控它 </blockquote>
在 Issue 中交流与讨论: 05 如何获取一个进程的内存并监控
经过 ps
能够获知一个进程所占用的内存
$ ps -O rss -p 3506 PID RSS S TTY TIME COMMAND 3506 6984 S pts/1 00:00:00 vim
若是要监控内存,确定使用对进程万能的命令 pidstat
(PS: 这名字一听就知道是干吗的)
## -r 显示内存信息 ## -p 指定 pid ## 1: 每一个一秒打印一次 $ pidstat -r -p 3506 1 Linux 3.10.0-957.21.3.el7.x86_64 (shanyue) 11/04/19 _x86_64_ (2 CPU) 20:47:35 UID PID minflt/s majflt/s VSZ RSS %MEM Command 20:47:36 0 3506 0.00 0.00 139940 6984 0.18 vim 20:47:37 0 3506 0.00 0.00 139940 6984 0.18 vim 20:47:38 0 3506 0.00 0.00 139940 6984 0.18 vim 20:47:39 0 3506 0.00 0.00 139940 6984 0.18 vim 20:47:40 0 3506 0.00 0.00 139940 6984 0.18 vim 20:47:41 0 3506 0.00 0.00 139940 6984 0.18 vim
pidstat
是属于 sysstat
下的 linux 性能工具,但在 mac 中,如何定位内存的变化?此时可使用万能的 top/htop
$ htop -p 31796
简而言之,有如下三个命令
pidstat -r
htop/top -p
ps -O rss -p
关于更多指标的监控能够参考个人文章: linux 各项监控指标小记
在 Issue 中交流与讨论: 06 CORS 若是须要指定多个域名怎么办
CORS
经过控制 Access-Control-Allow-Origin
控制哪些域名能够共享资源,取值以下
Access-Control-Allow-Origin: <origin> | *
其中 *
表明全部域名,origin
表明指定特定域名,那如何设置多个域名了?
此时须要经过代码实现,根据请求头中的 Origin
来设置响应头 Access-Control-Allow-Origin
,那 Origin 又是什么东西?
并非全部请求都会自动带上 Origin
,在浏览器中带 Origin
的逻辑以下
Origin
,值为当前域名Origin
逻辑理清楚后,关于服务器中对于 Access-Control-Allow-Origin
设置多域名的逻辑也很清晰了
Origin
,证实未跨域,则不做任何处理Origin
,证实跨域,根据 Origin
设置相应的 Access-Control-Allow-Origin: <Origin>
使用伪代码实现以下:
// 获取 Origin 请求头 const requestOrigin = ctx.get('Origin'); // 若是没有,则跳过 if (!requestOrigin) { return await next(); } // 设置响应头 ctx.set('Access-Control-Allow-Origin', requestOrigin)
此时能够给多个域名控制 CORS,但此时假设有两个域名访问 static.shanyue.tech
的跨域资源
foo.shanyue.tech
,响应头中返回 Access-Control-Allow-Origin: foo.shanyue.tech
bar.shanyue.tech
,响应头中返回 Access-Control-Allow-Origin: bar.shanyue.tech
看起来一切正常,但若是中间有缓存怎么办?
foo.shanyue.tech
,响应头中返回 Access-Control-Allow-Origin: foo.shanyue.tech
,被 CDN 缓存bar.shanyue.tech
,因由缓存,响应头中返回 Access-Control-Allow-Origin: foo.shanyue.tech
,跨域出现问题此时,Vary: Origin
就上场了,表明为不一样的 Origin
缓存不一样的资源
CORS 如何指定多个域名?
根据请求头中的 Origin
来设置响应头 Access-Control-Allow-Origin
,思路以下
Vary: Origin
,避免 CDN 缓存破坏 CORS 配置Origin
,证实未跨域,则不做任何处理Origin
,证实浏览器访问跨域,根据 Origin
设置相应的 Access-Control-Allow-Origin: <Origin>
使用伪代码实现以下
// 获取 Origin 请求头 const requestOrigin = ctx.get('Origin'); ctx.set('Vary', 'Origin') // 若是没有,则跳过 if (!requestOrigin) { return await next(); } // 设置响应头 ctx.set('Access-Control-Allow-Origin', requestOrigin)
相关问题: 如何避免 CDN 为 PC 端缓存移动端页面
在 Issue 中交流与讨论: 07 既然 cors 配置能够作跨域控制,那能够防止 CSRF 攻击吗
对 CORS 一点用也没有
form
提交不经过 CORS
检测,你能够在本地进行测试xhr
及 fetch
进行提交被 CORS 拦住,可是对于简单请求而言,请求仍被发送,已形成了攻击在 Issue 中交流与讨论: 08 如何避免 CDN 为 PC 端缓存移动端页面
若是 PC 端和移动端是一套代码则不会出现这个问题。这个问题出如今 PC 端和移动端是两套代码,却共用一个域名。
使用 nginx
配置以下,根据 UA 判断是否移动端,而走不一样的逻辑 (判断UA是否移动端容易出问题)
location / { // 默认 PC 端 root /usr/local/website/web; # 判断 UA,访问移动端 if ( $http_user_agent ~* "(Android|webOS|iPhone|iPad|BlackBerry)" ){ root /usr/local/website/mobile; } index index.html index.htm; }
解决方案一般使用 Vary
响应头,来控制 CDN 对不一样请求头的缓存。
此处可使用 Vary: User-Agent
,表明若是 User-Agent 不同,则从新发起请求,而非从缓存中读取页面
Vary: User-Agent
固然,User-Agent
实在过多,此时缓存失效就会过多。
使用 Vary: User-Agent
,根据 UA 进行缓存。
Vary: User-Agent
但最好不要出现这种状况,PC 端和移动端若是是两套代码,建议用两个域名,理由以下
nginx
判断是否移动端容易出错在 Issue 中交流与讨论: 09 如何实现表格单双行条纹样式
经过 css3
中伪类 :nth-child
来实现。其中 :nth-child(an+b)
匹配下标 { an + b; n = 0, 1, 2, ...}
且结果为整数的子元素
nth-child(2n)
/nth-child(even)
: 双行样式nth-child(2n+1)
/nth-child(odd)
: 单行样式其中 tr
在表格中表明行,实现表格中单双行样式就很简单了:
tr:nth-child(2n) { background-color: red; } tr:nth-child(2n+1) { background-color: blue; }
同理:
:nth-child(-n+3)
:nth-last-child(-n+3)
在 Issue 中交流与讨论: 10 简述下 css specificity
css specificity
即 css 中关于选择器的权重,如下三种类型的选择器依次降低
id
选择器,如 #app
class
、attribute
与 pseudo-classes
选择器,如 .header
、[type="radio"]
与 :hover
type
标签选择器和伪元素选择器,如 h1
、p
和 ::before
其中通配符选择器 *
,组合选择器 + ~ >
,否认伪类选择器 :not()
对优先级无影响
另有内联样式 <div class="foo" style="color: red;"></div>
及 !important
(最高) 具备更高的权重
:not
的优先级影响 - codepen 能够看出:not
对选择器的优先级无任何影响
在 Issue 中交流与讨论: 11 node 中 module.exports 与 exports 有什么区别
一句话:exports
是 module.exports
的引用,若是 exports
没有重赋值,则两者没有任何区别
相似以下所示
const exports = module.exports
那以下结果会如何导出?
module.exports = 100 exports = 3
很显然会导出 100,毕竟 exports
进行了重赋值。
那在 node 源码中如何实现的呢? 从源码里能够看出 exports 的实质
详见源码: https://github.com/nodejs/nod...,能够看出符合猜测
众所周知,node 中全部的模块代码都被包裹在这个函数中
(function(exports, require, module, __filename, __dirname) { exports.a = 3 });
而如下源码指出,exports
是如何得来
const dirname = path.dirname(filename); const require = makeRequireFunction(this, redirects); let result; // 从这里能够看出来 exports 的实质 const exports = this.exports; const thisValue = exports; const module = this; if (requireDepth === 0) statCache = new Map(); if (inspectorWrapper) { result = inspectorWrapper(compiledWrapper, thisValue, exports, require, module, filename, dirname); } else { // 这里是模块包装函数 result = compiledWrapper.call(thisValue, exports, require, module, filename, dirname); }
<blockquote> 更多描述: 一些 SaaS 系统基于 Pricing 的考虑,会限制团队人数及同时在线数,如何实现 </blockquote>
在 Issue 中交流与讨论: 12 如何获取当前系统中的在线用户数 (并发用户数)一些 SaaS 系统基于订价策略的考虑,会限制团队人数及同时在线数,如何实现?
经过 redis
的 zset
可实现并发用户数。
当一个用户请求任何接口时,实现一个 middleware,处理如下逻辑
// 当一个用户访问任何接口时,对该用户Id,写入 zset await redis.zadd(`Organization:${organizationId}:concurrent`, Date.now(), `User:${userId}`) // 查询当前机构的并发数 // 经过查询一分钟内的活跃用户来确认并发数,若是超过则抛出特定异常 const activeUsers = await redis.zrangebyscore(`Organization:${organizationId}:concurrent`, Date.now() - 1000 * 60, Date.now()) // 查出并发数 const count = activeUsers.length // 删掉过时的用户 await redis.zrembyscore(`Organization:${organizationId}:concurrent`, Date.now() - 1000 * 60, Date.now())
在 Issue 中交流与讨论: 13 如何把 json 数据转化为 demo.json 并下载文件
json 视为字符串,能够利用 DataURL
进行下载
Text -> DataURL
除了使用 DataURL,还能够转化为 Object URL 进行下载
Text -> Blob -> Object URL
能够把如下代码直接粘贴到控制台下载文件
function download (url, name) { const a = document.createElement('a') a.download = name a.rel = 'noopener' a.href = url // 触发模拟点击 a.dispatchEvent(new MouseEvent('click')) // 或者 a.click() } const json = { a: 3, b: 4, c: 5 } const str = JSON.stringify(json, null, 2) // 方案一:Text -> DataURL const dataUrl = `data:,${str}` download(dataUrl, 'demo.json') // 方案二:Text -> Blob -> ObjectURL const url = URL.createObjectURL(new Blob(str.split(''))) download(url, 'demo1.json')
<a href="url" download><a>
标签并设置 url
及 download
属性来下载json
转化为 dataurl
来构造 URLjson
转换为 Blob
再转化为 ObjectURL
来构造 URL在 Issue 中交流与讨论: 14 在浏览器中如何监听剪切板中内容
经过 Clipboard API
能够获取剪切板中内容,但须要获取到 clipboard-read
的权限,如下是关于读取剪贴板内容的代码:
// 是否可以有读取剪贴板的权限 // result.state == "granted" || result.state == "prompt" const result = await navigator.permissions.query({ name: "clipboard-read" }) // 获取剪贴板内容 const text = await navigator.clipboard.readText()
注: 该方法在
devtools
中不生效
相关问题: 【Q019】如何实现选中复制的功能
我是山月,正致力于天天用五分钟可以看完的简短答案回答一个大厂高频面试题。
欢迎关注公众号【互联网大厂招聘】,定时推送大厂内推信息及面试题简答,天天学习五分钟,半年进入大厂中