精选文章 全栈面试题总结-十四道大厂

全栈面试题总结-十四道大厂

作者:编程鹿子 时间: 2020-07-27 03:01:09
编程鹿子 2020-07-27 03:01:09

防抖,顾名思义,防止抖动,以免把一次事件误认为多次,敲键盘就是一个每天都会接触到的防抖操作。

想要了解一个概念,必先了解概念所应用的场景。在 JS 这个世界中,有哪些防抖的场景呢

  1. 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
  2. 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
  3. 文本编辑器实时保存,当无任何更改操作一秒后进行保存

代码如下,可以看出来 防抖重在清零 clearTimeout(timer)

function debounce (f, wait) {
  let timer
  return (...args) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      f(...args)
    }, wait)
  }
}
复制代码

节流 (throttle)

节流,顾名思义,控制水的流量。控制事件发生的频率,如控制为1s发生一次,甚至1分钟发生一次。与服务端(server)及网关(gateway)控制的限流 (Rate Limit) 类似。

scroll

代码如下,可以看出来 节流重在加锁 timer=timeout

function throttle (f, wait) {
  let timer
  return (...args) => {
    if (timer) { return }
    timer = setTimeout(() => {
      f(...args)
      timer = null
    }, wait)
  }
}
复制代码

总结 (简要答案)

  • 防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。 代码实现重在清零 clearTimeout 。防抖可以比作等电梯,只要有一个人进来,就需要再等一会儿。业务场景有避免登录按钮多次点击的重复提交。
  • 节流:控制流量,单位时间内事件只能触发一次,与服务器端的限流 (Rate Limit) 类似。 代码实现重在开锁关锁 timer=timeout; timer=null 。节流可以比作过红绿灯,每等一个红灯时间就可以过一批。

02 在前端开发中,如何获取浏览器的唯一标识

更多描述: 如何获取浏览器的唯一标识,原理是什么

在 Issue 中交流与讨论: 02 在前端开发中,如何获取浏览器的唯一标识

由于不同的系统显卡绘制 canvas 时渲染参数、抗锯齿等算法不同,因此绘制成图片数据的 CRC 校验也不一样。

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 可以获取浏览器指纹信息。

  1. 绘制 canvas ,获取 base64 的 dataurl
  2. 对 dataurl 这个字符串进行 md5 摘要计算,得到指纹信息

但是对于常见的需求就有成熟的解决方案,若在生产环境使用,可以使用以下库

它依据以下信息,获取到浏览器指纹信息, 而这些信息,则成为 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 也有分类

  • browser independent component :有些 component 同一设备跨浏览器也可以得到相同的值,有些独立浏览器,得到不同的值
  • stable component : 有些 component 刷新后值就会发生变化,称为不稳定组件

在实际业务中,可根据业务选择合适的组件

const options = {
  excludes: {userAgent: true, language: true}
}
复制代码

简答

根据 canvas 可以获取浏览器指纹信息

  1. 绘制 canvas ,获取 base64 的 dataurl
  2. 对 dataurl 这个字符串进行 md5 摘要计算,得到指纹信息

若在生产环境使用,可以使用 fingerprintjs2 ,根据业务需求,如单设备是否可跨浏览器,以此选择合适的 component

03 在服务端应用中如何获得客户端 IP

在 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: , , 
复制代码

以下是 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];
  },
复制代码

参见源码: github.com/koajs/koa/b…

04 js 如何全部替代一个子串为另一个子串

更多描述: 假设有一个字符串 `hello. hello. hello. ` 需要替换为 `AAA`,即把 `hello. ` 替换为 `A`

在 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 中,目前支持性不好

05 如何获取一个进程的内存并监控

更多描述: 在编写脚本时,有时会出现内存过大发生 OOM 的事情,那我们如何得知某个进程的内存?另外又如何监控它

在 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
复制代码

全栈面试题总结-十四道大厂1

总结

简而言之,有以下三个命令

pidstat -r
htop/top -p
ps -O rss -p

关于更多指标的监控可以参考我的文章: linux 各项监控指标小记

06 CORS 如果需要指定多个域名怎么办

在 Issue 中交流与讨论: 06 CORS 如果需要指定多个域名怎么办

CORS 通过控制 Access-Control-Allow-Origin 控制哪些域名可以共享资源,取值如下

Access-Control-Allow-Origin:  | *
复制代码

其中 * 代表所有域名, origin 代表指定特定域名,那如何设置多个域名了?

此时需要通过代码实现, 根据请求头中的 Origin 来设置响应头 Access-Control-Allow-Origin ,那 Origin 又是什么东西?

请求头: Origin

并不是所有请求都会自动带上 Origin ,在浏览器中带 Origin 的逻辑如下

Origin
Origin

逻辑理清楚后,关于服务器中对于 Access-Control-Allow-Origin 设置多域名的逻辑也很清晰了

  1. 如果请求头不带有 Origin ,证明未跨域,则不作任何处理
  2. 如果请求头带有 Origin ,证明跨域,根据 Origin 设置相应的 Access-Control-Allow-Origin:

使用伪代码实现如下:

// 获取 Origin 请求头
const requestOrigin = ctx.get('Origin');

// 如果没有,则跳过
if (!requestOrigin) {
  return await next();
}

// 设置响应头
ctx.set('Access-Control-Allow-Origin', requestOrigin)
复制代码

Vary: Origin

此时可以给多个域名控制 CORS,但此时假设有两个域名访问 static.shanyue.tech 的跨域资源

  1. foo.shanyue.tech ,响应头中返回 Access-Control-Allow-Origin: foo.shanyue.tech
  2. bar.shanyue.tech ,响应头中返回 Access-Control-Allow-Origin: bar.shanyue.tech

看起来一切正常,但如果中间有缓存怎么办?

  1. foo.shanyue.tech ,响应头中返回 Access-Control-Allow-Origin: foo.shanyue.tech ,被 CDN 缓存
  2. bar.shanyue.tech ,因由缓存,响应头中返回 Access-Control-Allow-Origin: foo.shanyue.tech ,跨域出现问题

此时, Vary: Origin 就上场了,代表为不同的 Origin 缓存不同的资源

总结 (简要答案)

CORS 如何指定多个域名?

根据请求头中的 Origin 来设置响应头 Access-Control-Allow-Origin ,思路如下

  1. 总是设置 Vary: Origin ,避免 CDN 缓存破坏 CORS 配置
  2. 如果请求头不带有 Origin ,证明未跨域,则不作任何处理
  3. 如果请求头带有 Origin ,证明浏览器访问跨域,根据 Origin 设置相应的 Access-Control-Allow-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 端缓存移动端页面

07 既然 cors 配置可以做跨域控制,那可以防止 CSRF 攻击吗

在 Issue 中交流与讨论: 07 既然 cors 配置可以做跨域控制,那可以防止 CSRF 攻击吗

对 CORS 一点用也没有

  1. form 提交不通过 CORS 检测 ,你可以在本地进行测试
  2. 即使通过 xhr 及 fetch 进行提交被 CORS 拦住, 但是对于简单请求而言,请求仍被发送 ,已造成了攻击

08 如何避免 CDN 为 PC 端缓存移动端页面

在 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

09 如何实现表格单双行条纹样式

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

10 简述下 css specificity

在 Issue 中交流与讨论: 10 简述下 css specificity

css specificity 即 css 中关于选择器的权重,以下三种类型的选择器依次下降

  1. id 选择器,如 #app
  2. class 、 attribute 与 pseudo-classes 选择器,如 .header 、 [type="radio"] 与 :hover
  3. type 标签选择器和伪元素选择器,如 h1 、 p 和 ::before

其中通配符选择器 * ,组合选择器 + ~ > ,否定伪类选择器 :not() 对优先级无影响

另有内联样式 

 及 !important (最高) 具有更高的权重

:not 的优先级影响 - codepen 可以看出 :not 对选择器的优先级无任何影响

11 node 中 module.exports 与 exports 有什么区别

在 Issue 中交流与讨论: 11 node 中 module.exports 与 exports 有什么区别

一句话: exports 是 module.exports 的引用,如果 exports 没有重赋值,则二者没有任何区别

类似如下所示

const exports = module.exports
复制代码

那如下结果会如何导出?

module.exports = 100
exports = 3
复制代码

很显然会导出 100,毕竟 exports 进行了重赋值。

那在 node 源码中如何实现的呢?从源码里可以看出 exports 的实质

全栈面试题总结-十四道大厂2

详见源码: github.com/nodejs/node… ,可以看出符合猜想

众所周知,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);
}
复制代码

12 如何获取当前系统中的在线用户数 (并发用户数)

更多描述: 一些 SaaS 系统基于 Pricing 的考虑,会限制团队人数及同时在线数,如何实现

在 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())
复制代码

总结

  1. 每当用户访问服务时,把该用户的 ID 写入优先级队列,权重为当前时间
  2. 根据权重(即时间)计算一分钟内该机构的用户数
  3. 删掉一分钟以上过期的用户

13 如何把 json 数据转化为 demo.json 并下载文件

在 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')
复制代码

总结

  1. 模拟下载,可以通过新建一个  标签并设置 url 及 download 属性来下载
  2. 可以通过把 json 转化为 dataurl 来构造 URL
  3. 可以通过把 json 转换为 Blob 再转化为 ObjectURL 来构造 URL

14 在浏览器中如何监听剪切板中内容

在 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 中不生效

如果你现在也想学习前端开发技术,在入门学习前端的过程当中有遇见任何关于学习方法,学习路线,学习效率等方面的问题,
你都可以申请加入我的前端qq学习交流裙:1142648440

里面聚集了一些正在自学前端的初学者,裙文件里面也有我做前端技术这段时间整理的一些前端学习手册,
前端面试题,前端开发工具,PDF文档书籍教程,需要的话都可以自行来获取下载。
如果这篇文章对你有帮助,请记得给我来个评论+转发噢

勿删,copyright占位
分享文章到微博
分享文章到朋友圈

上一篇:最强大的专业PDF阅读器PDF Reader Pro功能

下一篇:最强大的专业PDF阅读器PDF Reader Pro功能

您可能感兴趣

  • 历时两周,将我司的Hadoop2升级到Hadoop3,踩了几个大坑...

    文末有赠书福利 继一次超万亿规模的Hadoop NameNode性能故障排查过程之后,虽然解决了Hadoop2.6.0版本在项目中的问题,但客户依然比较担心,一是担心版本过老,还存在其他未发现的问题;二是按目前每天近千亿条的数据增长,终究会遇到NameNode的第二次瓶颈。 基于上述原因,我们决定将当前集群由Hadoop2.6.0版本升级到Hadoop3.2.1版本,且启用联邦模式。历时2周...

  • 适合新手练习的Python项目有哪些?

    适合新手练习的Python项目有哪些?简单易上手的Python项目汇总:Web 项目设计:内容聚合器、正则表达式查询工具、网址缩短、便利贴、功能、测验、GUI 项目设计:MP3 播放器、闹铃提醒工具、文件管理器、记账功能、命令行项目设计:通讯录、网站连接检查、批量文件重命名工具、目录树生成器。   一、Web 项目设计:内容聚合器(Content Aggregator)   该项目设计的主要...

  • 芯片破壁者(十.上):风起樱花之地

    在不断升级的中美科技战中,每个人都很容易发现,在芯片上受制于人似乎是一个最难解的谜题。面对这种情况,很多国人可能都在思考:我们到底有没有可能打破“芯片枷锁”? 而从历史里寻找答案是文明的天性,在审视国家间的半导体博弈时,有一个无法绕开的话题,就是上世纪60年到到90年代,横跨数十年、关系错综复杂的美日半导体纠葛。这段历史中最为人津津乐道的有两点。一是日本在80年代一跃超过美国成为全球半导体产...

  • 2020起重机司机(限桥式起重机)模拟考试及起重机司机(限桥式起重机)证考试

    题库来源:安全生产模拟考试一点通公众号小程序 2020起重机司机(限桥式起重机)模拟考试及起重机司机(限桥式起重机)证考试,包含起重机司机(限桥式起重机)模拟考试答案解析及起重机司机(限桥式起重机)证考试练习。由安全生产模拟考试一点通公众号结合国家起重机司机(限桥式起重机)考试最新大纲及起重机司机(限桥式起重机)考试真题出具,有助于起重机司机(限桥式起重机)模拟考试系统考前练习。 1、【判断...

  • 华为、苹果、三星的手机终结之战

    过去10年中,智能手机的快速普及,改变了全世界大多数人的生活和工作方式,而在这场变革背后,苹果、三星、华为这些科技巨头的积极推动作用不容忽视。 其中三星起到的作用尤其不容小觑,2011年之后,全球智能手机市场中三星份额整体超过苹果,以每年2、3亿部出货量,在全球数十亿用户群体中不断扩散影响力。单从智能手机出货量这个方面考虑,过去9年,三星是全球市场中无可争议的霸主。 数据来自IDC 自201...

  • 对云桌面、桌面云、私有云的一些看法

    最近整理了一下自2017年以来自己对私有云服务平台构建的一些认识,并以刚刚为某后勤保障部队单位提供的小规模私有云服务平台为例阐述。 私有云平台、云计算服务与虚拟化: 我个人认为这三个名称分别指涉了三个不同的事物: 私有云平台:在实践中,私有云平台时包含了服务端软硬件集成和客户端软硬件集成的一整套特定业务需求场景的、已经处于落地实施阶段的解决方案项目。 比如某后勤保障部队为简化征兵流程、节省相...

  • 学计算机必看:程序员部落酋长,给计算机专业学生的 6 个建议!

    如果你喜欢编程,那么你真是受到了上天的眷顾。你是非常幸运的少数人之一,能够以自己喜欢的事谋生。大多数人没有这么幸运。你认为理所当然的观念“热爱你的工作”,其实是一个很现代的概念。通常的看法是,工作是一种让人很不开心的事,你为了拿工资才不得不去上班。 你工作的目的是为了攒下钱去干那些自己真正喜欢干的事,但是前提是你得等到65岁退休之后才行,而且还有不少条件。条件一,你的积蓄必须足够多;条件二,...

  • 210道面试官必问经典面试题总结分享|Java+JVM+数据库+算法+Spring+中间件+设计模式超详细整理

    “金九银十”的秋招热潮已经来了,由于疫情影响、今年的秋招来的格外早。小编从今天六月份开始备战,不负众望的成功拿下了一些大厂的offer。经过这么多次的面试,凭借自己的面试成功经验和这两个月的准备,小编整理了一份面试清单分享给大家,希望能给和我一样为了秋招奋战多时的码农亲人们一点帮助(java方向),觉得有帮助的同学可以转发点个赞哦~~祝大家面试顺利,一举拿下大厂offer! 一、JAVA基础...

华为云40多款云服务产品0元试用活动

免费套餐,马上领取!
CSDN

CSDN

中国开发者社区CSDN (Chinese Software Developer Network) 创立于1999年,致力为中国开发者提供知识传播、在线学习、职业发展等全生命周期服务。