复盘一下最近的面试

179 阅读7分钟

背景

7、8 月份时,面试了两个月,时间跨度比较长,但实际面试次数不多。
那会比较佛系,面得过就过,面不过就下一家,面试前也不准备,面完也不复盘一下。
不出所料,没什么合适的offer,一家给的高但是996,另一家给的没到预期,就都放弃了
9 月有事情,就没面试,最近过完国庆,重新开始面试,这次要好好面一下了~

所以写下此篇记录
外包面试,仅作为个人记录,或许对大家没什么用,可以不看!


第一家 (一面)

上来就要共享桌面写代码,我都愣了,这么直接的吗哈哈?
不过比较好的是,出了 4 道题,让我自己选择其中 2 道去做,
不像我之前的面试,出一道做一道,自己没什么可选择的余地。

// 写一个 EventEmitter / Promise all / 快排 / 深拷贝 四选一
// EventEmitter 需实现:on、emit、off、once 四个方法
// Promise all 列表里可能传入不是 Promise 的类型,需要支持其它类型,原样返回即可

这四道代码题还是比较简单,正巧都会,就写了前两个。

接下来是一道 this 指向的问题,(浏览器环境下)

var obj = {
  a: 1,
  fire: () => {
    console.log(this.a)
  },
}

var a = 2
var fireInGlobal = obj.fire
fireInGlobal()

这道题我今天再看,觉得会打印 2,并且很确定。
fire 是箭头函数(它的this是词法作用域的),这里的 this,会指向 window,所以是 2
结果那天,忘了面试官说了个啥,被忽悠瘸了,一开始说 2,后来回答 1。。。

再然后就是一道事件循环的题目,也比较简单

setTimeout(function () {
  console.log('2')
  new Promise(function (resolve) {
    console.log('3')
    resolve()
  }).then(function () {
    console.log('4')
  })
})

new Promise(function (resolve) {
  console.log('5')
  resolve()
}).then(function () {
  console.log('6')
}).then(() => {
  console.log('6+')
})

事件循环几乎是面试必考的题目,学完后再看这些会发现挺简单的
以下是几个关键点:

  1. 异步代码分两类,
  • 宏任务(setTimeout等添加的),
  • 微任务(Promise.then|MutationObserver等添加的)
  1. new Promise 内的函数代码,会立即执行
  2. promise 链上的 then 和 catch 里的代码,会在上个 promise 状态改变后,才去添加微任务
  3. 执行顺序是:全局 js 代码 -> 全部微任务 -> 一个宏任务 -> 全部微任务 -> 一个宏任务 -> 全部微任务... 这样循环执行下去
  4. 需要注意的是,在执行微任务过程中产生了新的微任务,也会把它执行完之后,再去执行宏任务,换句话说,执行宏任务的时候,一定是微任务队列里一个都没有的时候

值得写的就以上这三个。

以下三个是我当时没答好的,都是知道一点但不深入:

// * 函数
// http 缓存
// setState 的流程,落下了 commit 阶段

事后自己看了浏览器缓存,这里把笔记贴上来

#### 协商缓存 
Cache-Control 
- public:可以被任何缓存系统缓存 
- no-store:禁止任何缓存 
- no-cache:可以缓存,但下次使用缓存内容前,须先发请求验证它是否有效 
- 有两种方式验证: 
1. Last-Modified 和 If-Modified-Since // 验证文件最后修改时间 
2. Etag 和 If-None-Match // 验证文件摘要 

> 步骤是: 
1. 浏览器第一次请求资源,服务端返回的资源响应头有"Etag"或"Last-Modified" 
2. 浏览器下次再请求该资源时,带上"If-Modified-Since" 或者 "If-None-Match" 请求头 
3. 服务器收到后,检查该资源的信息,
 - 如果没有改变,则返回 304,代表使用缓存 
 - 如果改变,则返回新的资源,与新的"Etag"或"Last-Modified"

这个面试,自我打分:70~75


第二家 (一面)

这家上来就问 React Filber 架构,我又懵了,问一个小外包这问题,有点难了吧
所以心里做好了被 pass 掉的准备,但万万没想到,后续问的很多问题,都不深入
回答个大概,面试官感觉我是知道的,就接着问下一题了~
ps: 我个人感觉这家比较符合我心里招外包的问题,就是找个干活儿的,不需要你太深入,来了能写业务代码就行
他们的问题比较实际一些,都是开发中可能碰到的,我答得挺好,面试官直接表示了通过

题目问太多,我答得比较快,所以记不起来了,这里就不写了。

这个面试,自我打分: 90


第一家 (二面)

二面考察的就有点全面了
以下两个都是 React 组件闭包的问题,问代码运行后是怎么样的,如何修改让它符合预期

我一时大意,看了下,就说会从 0 往上增长,面试官说你再仔细看下,
哈哈哈,现在想起来还挺感谢这句话,因为仔细看下,就会发现闭包了

(面试场景下,心态不够稳。。。有的东西本来是会的,但急于回答亦或是紧张,导致不够细心回答错误,做面试官的不要急于否定求职者,给他个提示看看~)

这两个最终都回答正确了

// 问题一
function Counter () {
  const [count, setCount] = useState(0)

  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1)
    }, 1000)
    return () => clearInterval(id)
  }, [])

  return <h1>{count}</h1>
}

// 问题二
function Counter () {
  const [count, setCount] = useState(0)
  const [step, setStep] = useState(1)

  useEffect(() => {
    const id = setInterval(() => {
      setCount(c => c + step)
    }, 1000)
    return () => clearInterval(id)
  }, [])

  return (
    <>
      <h1>{count}</h1>
      <input value={step} onChange={e => setStep(Number(e.target.value))} />
    </>
  )
}

代码题

// www.baidu.com  -> com.baidu.www
第一层:实现上面的反转
第二层:不使用 Array.prototype.reverse
第三层:不多占用新的内存空间

前端安全(xss、csrf)
回答的不好,2018 年刚入行那会记得,现在忘差不多了。。
以下是 ChatGPT 的回答

XSS(Cross-Site Scripting)
XSS 是一种代码注入攻击,攻击者通过在目标网站的输入或输出中注入恶意脚本,通常是 JavaScript,当其他用户访问该网页时,这些恶意脚本会在用户的浏览器中执行。XSS 攻击可以导致以下后果:
窃取用户的信息,如 cookies、会话 tokens。
执行用户的操作。
传播蠕虫。

防御措施:
严格过滤和转义用户输入。
使用 Content Security Policy (CSP) 来限制脚本执行。
对输出进行 HTML 转义。

---

CSRF(Cross-Site Request Forgery)
CSRF 是一种利用用户已认证状态对受信任网站执行未授权操作的攻击方式。攻击者通过在用户访问的恶意网站上嵌入对目标站点的请求,使得用户在不知情的情况下执行某些操作。这类攻击通常依赖于用户已经登录,并且浏览器保存着该网站的认证信息(如 cookies)。

防御措施:
使用并验证 CSRF 令牌。
验证 Referer  Origin 头。
避免对敏感操作使用 GET 请求。

ts 类型体操
这个完全不会。。
以下是 ChatGPT 的回答

// 定义一个 Pick 类型,从 T 中挑选出 K 属性并组成新的类型 
type Pick<T, K extends keyof T> = { 
  [P in K]: T[P]; 
};

正则
挺简单的一个正则,匹配数字就行,但我没写对
因为现在需要写正则的场景少,遇到的话,直接丢给 ChatGPT 了
只记得 \d 是数字,\w是字母这些基础,惭愧惭愧。。。

这个面试,自我打分:60


第三家 (一面)

这家问的比较传统,先是介绍项目,然后做一些代码题

第一题,考察一下引用传值

var a = []

var b = a

a.push(1)

a = [2]

console.log(b)

第二题, 这道题有两个坑点,我只想到是要考察 setTimeout 执行时机,所以信心满满地说 4,
结果,面试官让我再看一下~ 感谢每一位面试官的耐心~

for(var i = 0; i < 5; i++) {

  setTimeout(() => {

    console.log(i)

  }, 10)

}

第三题,上一道题目,如何让它打印 0~4

通过闭包传入参数,很简单,代码略

第四题,promise 链的执行顺序
感觉我就像个 AI,一开始给出自信满满地答案,
面试官让我解释一下原因,在解释的过程中,自己就发现了问题。。。
然后重新才回答正确~

const first = () => (new Promise((resolve, reject) => {

  console.log(3);

  let p = new Promise((resolve, reject) => {

    console.log(7);

    setTimeout(() => {

      console.log(5);

      resolve(6);

    }, 0)

    resolve(1);

  });

  resolve(2);

  p.then((arg) => {

    console.log(arg);

  });

}));

first().then((arg) => {

  console.log(arg);

});

第五题,写个 ToDoList 组件,看看思路

这个比较简单,就不赘述了

后续有时间了再补充吧,最近在学 NextJs,可能没时间写这个了