阅读 2371

不知名本科菜鸡の春招前端心路历程(阿里,腾讯,美团) | 掘金技术征文

啰嗦的话

本人现在大三,就读于北京师范大学珠海分校的前端小菜鸡。受人蛊惑,1月份开始寻找暑期实习,经历十分坎坷。因为也拿到自己心爱的offer啦,有的公司也已经稳了,等HR面或者等offer啦,所以特来分享一下俺的经历哈哈哈,希望对大家有所帮助!!

心里准备

没有名校光环,没有大厂实习经历,没有ACM金牌,一无所有,唯有坚持、努力、思考可以帮助我们

常见QA

这些俺就没有研究太多了哈哈哈,就简单扯一点点噗噗

前端技术学习路径

我个人的话,是这样子学的噗噗

graph LR
css-->js
js-->dom

React-->Webpack
Webpack-->Taro
复制代码

其实说实话,对于技术栈这些东东我倒也没有太仔细的去研究过噗噗,大概就是以下这些吧:

  • React/Vue
  • Webpack
  • Babel
  • Html5
  • Css3
  • Typescript
  • Sass/Less
  • Git

基本都是平时可能做项目会碰到,比较常见的东东

前端框架学习

因为我是大一时候接触React的,所以也是情有独钟,Vue也只是了解一些基本的原理还有使用方法,没有深入研究。俺觉得如果要研究研究框架的话。就这样子吧!

  1. 了解框架出现的原因
  2. 了解框架解决了那些问题/难点
  3. 了解框架基本语法、使用方式
  4. 了解框架的编码规范
  5. 了解框架的坑、以及出现的原因
  6. 了解框架实现的原理
  7. 了解框架的源码
  8. 自己写一个框架(划掉

大厂算法/笔试

这个东东其实我倒也没有感受的太深,就是我的感受就是,字节跳动的算法要求会比别的大厂会高(来自一面挂的小菜鸡)

笔试题

因为今年阿里内推没有笔试,TX也还没到常规批的笔试,就只经历过美团、快手的笔试。大家可以去看一下他们的笔试题,反正俺自闭了~

基本就几个方向吧,大家可以去leetcode疯狂刷题打卡!

  1. 动态规划
  2. 图论
  3. DFS/BFS
  4. 贪心算法
  5. 排序
  6. 数组/字符串

代码评测题/面试中的笔试

大家可以看看阿里跟TX的代码评测题,其实他们出题的感觉挺像的。基本流程都差不多

一般分为3 - 4题吧

  1. 第一题一般是一道leetcode难度easy/medium难度的算法题,都是比较常见的吧,一般是链表/数组/字符串
  2. 第二、三题一般是结合实际情况的算法题,今年常见的是DFS的题型,例如模拟css选择器来找节点,或者求加载依赖的顺序。
  3. 最后一题一般是一个代码综合题,一般做不出来的,时间太短了,像什么模拟贪吃蛇啊,或者写一个React组件啊之类的

或者说可能是纯实践的操作题,这些基本都是我面试中遇到过的东东,例如:

  • 实现深拷贝
  • 实现观察者模式/订阅发布
  • 实现下划线形式转换大驼峰(a_bb_ccc -> ABbCcc)
  • 实现排序
  • 实现函数柯里化
  • 实现函数扁平化

大厂前端的要求

这个的话,其实我感受不是很明显,就我而言吧,我觉得他们想要的是,对原理的东西一定要了解/熟悉,有做一定的实践。例如`:

面试官:webpack,babel有使用过吗?
面试官:那你自己写过吗?

类似这种,不仅要我们了解,会使用,还需要知道一些原理,并且自己会加以改进。

无论什么都是这样的,再比如像是React。他们或许不会问你怎么使用,他们喜欢问:

面试官:keys的作用,如果列表元素重新排序,是否会重新渲染
面试官:setState异步or同步,平时有遇到过吗
面试官:React的阻止冒泡怎么样的,跟原生有什么区别

等等之类的东东,就是一些我们平时可能不会在意的小坑,或者需要注意的东西。需要我们不仅会用也要知道为什么这样用。

前端复习重点

当然还是基础优先啦~

  • 网络
  • 浏览器
  • css
  • js
  • 性能
  • 框架

等等这些,就我而言,其实我也不知道什么是重点,但是我觉得前端性能优化方面很多面试官都喜欢问

  • 常见的性能优化方式
  • 你如何发现性能不好
  • 如何发现内存泄漏
  • 怎么提高首屏渲染速度
  • 如何优化接口访问
  • 如何实现高帧率动画
  • 性能优化的指标

等等这些,关于性能方面的东西,感觉会比较常问

个人建议

前期准备

  • 面试最重要还是得基础扎实hhhh,例如前端相关的知识点,我们可以面经或者书籍来恶补!,基础一定要扎实,做不到脱口而出也要思考一下吧大致的东东讲出来。
  • 最重要还是得有自己项目,因为大厂更看重你的实践能力,以及在实践中的思考,难点的解决,所以我觉得有一个自己的项目,无论是在简历筛选或者是面试之中都很有用
  • 做好平时的积累、沉淀。例如可以自己总结一些难点,或者易错的东东,发表一些博客文章,在社区中与大家分享一下,这都是不错的!
  • 一定要坚持,万事开头难,可能觉得知识点基础都那么多了,面试怎么过嘛?但是付出的努力与回报是成正比的,你现在多努力,以后就会有多感谢自己当初的那份执着
  • 梳好头发,换好衣服(一位被leader吐槽粉色睡衣的小菜鸡路过~)

不要紧张

面试大家都会紧张,这是肯定的,但是千万别过于紧张。我这人本来就是容易紧张的,所以导致面试的时候,经常紧张得说不出话哈哈哈,特别是刚刚开始,前几次的面试,哇,还是视频面试,紧张的说不出话hhh直抖腿

所以俺建议吼~可以起来走一走,喝口水,面试前10分钟可以看看B站,冷静冷静,就当做聊天一样就可以啦!

体现思考

我们很多人面试的时候,我们或许都会选择去看一些面经,很多基础的知识点,的确看面经会更加方便、快捷。但是这也有一个弊端...

那就是不能体现我们的 ”思考过程与能力

举个例子

image

TCP三次握手

面试官:你知道TCP三次握手吗,你讲一下吧~
小菜鸡:客户端发送syn,服务端接收然后发送syn + ack包给客户端,客户端接收后发一个ack,连接成功

emmm,很好,但是这个简单的答案,但是会不会有所死板,更像是照着书本,标准答案念出来的

面试官:你知道TCP三次握手吗,你讲一下吧~

小菜鸡:客户端发送syn,跟服务器说“我要连接啦”,进入syn_send状态。
服务端接收然后发送syn + ack包给客户端,跟客户端说“我知道了,我这边ok了”,进入syn_recv状态。
客户端接收后发一个ack,跟服务端说”我要连接咯!“,进入establish状态,连接成功。

emmm,⑧错⑧错,带有了一点点自己的理解在里面,但是其实还可以加一点东东,可以再详细解释一下为什么是三次握手,而不是两次。

面试官:(给你个眼神)

小菜鸡:因为三次是为了确定双方的收发能力:第一次,确定了客户端的发送能力。第二次,服务端确定了自己的接受能力。第三次,确定了客户端的接受能力与服务端的发送能力。
再比如说,如果我们第一次握手,因为卡住了或者别的原因,导致连接请求没有发送出去,那么客户端会发起第二次连接请求,然后连接成功,之后断开。这时候!!这时候!!如果第一次握手的请求不卡了,现在发起了连接,而如果只需要两次握手,那么现在客户端与服务端就连接起来了,但是这时候客户端并没有东西要发送,所以就导致服务端资源被占用啦。

俺就是举个例子,hhhh说的对不对俺也不知道噗噗。就大概是这种思路嘛,面试官往往只是为了推敲出候选人是否有足够的思考能力,与技术潜力来承担起工作的重任。这样子可以很好的体现出我们的思考能力哈啊哈。

XSS和CSRF攻防

俺们再举个例子哈哈哈,比如说现在问xss跟csrf,大噶会怎么回答呢?

面试官:(开始你的表演)

小菜鸡:csrf就是跨站页面请求来进行攻击,一般我们添加token来防御,xss就是跨站脚本攻击,一般我们过滤一下script标签啊这些就ok

我们不妨说多一点,发挥口才!!!

面试官:(开始你的表演)

小菜鸡:csrf就是跨站页面请求来进行攻击,就比如我们现在登录了一个银行的网页,然后我们打开新的标签页,点击了一些攻击的网页,这时候因为我们刚刚登录,cookie还没有过期,我们刚刚点击的网页就会进行csrf攻击,利用cookie进行csrf攻击,钱就没了啦......

css就是跨站脚本攻击,分为XXX类,常见的比如现在有一个评论的组件,用户输入评论存储进数据库,然后别的用户访问的时候可以看到。如果这时候用户植入恶意的js代码评论,我们存储到数据库中,那样别的用户访问就会被攻击啦!我们可以通过过滤尖角号,或者把他转换成html实体来防御......

举举例子~更加生动!面试官更能理解你的意思!

所以俺建议吼~我们看面经之后,或许可以总结一些例子,或者一些深入的东东,例如“为什么”,“怎么来的”这些东东,面试的时候可以发挥口才疯狂吹!

适当引导

面试过几轮之后,有了一定的经验,你就会发现,面试官一般会跟着你说的东西,或者根据你的简历来进行提问。所以,其实我们可以适当的引导一下面试官问你熟悉的东东。比如......

面试官:优化性能?

小菜鸡:balabala ... 重绘、回流 ... balbala

面试官:那你说一下重回回流吧~

小菜鸡:(舒服了)

嘿嘿,可以通过一些关键词来引导hhhh

面试官:内存泄漏?

小菜鸡:内存泄漏有xxxx情况,我之前用React做一个项目,里面有一个监听滚动条,结果发现切换了页面还在监听,结果发现是没有取消时间监听,发生了内存泄漏,之后我通过xxx方法解决了

其实我们这里可以引导面试官往几个方面问

  1. 你的项目,难点等
  2. 使用hook的useEffect的return方法来取消事件监听
  3. 还有哪些内存泄漏的情况,如何解决
  4. 如何发现内存泄漏

所以俺的建议吼,这里只是举一个简单的例子,我们其实可以通过很多种方法,来引导面试官来问我们熟悉的东西,而不是被面试官带进他自己的节奏,把你问倒hhhh

项目难点

如果有自己的项目,那么有一个特殊的难点会显得尤为重要(俺因为没有,被怼的好惨hhhh)最好呢,是一个网上不能轻易找到的功能或者设计。这样会凸显自己~嗯!很强!就决定是你了。

或者是一个系统整体的架构之类的,体现出自己对于产品的理解很深哈哈哈。

再或者,可以针对自己现在的项目,总结出一个功能点计划,现在没有不代表未来没有,也可以表现的出自己也了解过相关的知识,知道大体的方向,只不过现在还没有实施,还在计划之中,体现自己很有热情!!

热情热情

热情很重要嗷,这个部分科班非科班,本科还是研究生。无论是谁,热情总能让面试官对你有好的印象。无关时间长短,只是自己的心态热情。对一个功能难点深入探究,对技术原理反复推敲,为自己的项目作出多大多大的努力。俺觉得都是十分好的东东!

稍加思索

面试的时候,问到不会的东东很正常,但是我个人觉得不要立刻说不会,其实可以稍加思索,然后想一想问题前后的联系,然后说一下自己的理解。向面试官请求引导,看看能不能答出来。如果实在不行,也可以跟面试官说,这方面还需要加强,面试结束之后自己再查查资料补一补。学习的精神很重要。

虚心请教

很多人觉得面试官很凶,很有压力,但其实我们应该是抱着学习的心态去面试的。面试只是查漏补缺的过程。我们面试结束之后,其实可以询问面试官可否加一下好友之类的。然后可以询问他一些人生的建议、技术的指导等等。厚着脸皮问,不要担心被嫌弃就好啦哈哈哈。我就是这样问一个面试官,才学到了很多东西,项目的难点,或者技术的思考之类的东东。

部分面经

阿里の盒马X数字农业事业部

初面

初面是聊得最久的一次了,一个多小时了吧,不过初面的面试官真的很让人感动一直在鼓励我,“好啊好啊”,“没关系没关系”,啊太棒了,给俺这个小菜鸡很多信心hhhh

  1. 输入url到页面展示
  2. 浏览器存储
  3. 如何实现继承
  4. 跨域,常用哪个,解释一下
  5. 缓存
  6. 重绘回流
  7. 性能优化
  8. React优势
  9. React生命周期
  10. React最佳实践
  11. React新特性
  12. 如果列表组件要新增一些内容,例如标题,简介等,你会怎么对代码进行修改(容器组件 -> 展示组件)
  13. csrf 和 xss
  14. flex
  15. 判断是否为数组
  16. typeof arr === 'object'
  17. 浏览器事件循环,node事件循环
  18. 事件委托
  19. webpack流程,插件
  20. koa源码
  21. koa洋葱模型
  22. mobx原理
  23. 首屏优化
  24. async/await Promise
  25. 盒模型
  26. babel原理
  27. Taro原理

一面

一面俺就放放笔试题还有俺自己做的情况吧哈哈哈哈,一面的面试官跟俺说拓扑排序,俺才知道原来还有这种东西(流泪...

笔试题目

  1. 给定一个链表,判断链表中是否有环,比如下图这种即为有环链表。
    image
    加分项:使用空间复杂度 O(1) 实现

  1. 分析一个项目的依赖结构,并按依赖优先级排序。 已知一个项目的依赖结构,期望在前端通过 loader 的方式异步加载相关的组件,而我们期望依赖在加载的过程中:
  • 每一个依赖被加载后都会被立刻执行,那么如果要争取加载一个依赖,则其子依赖都应该优先被加载
  • 每一个依赖不希望在钱多出现冗余的情况,若依赖出现多版本的情况,则默认使用更新的版本,比如已知项目依赖结构为(其中 @ 后面的为依赖版本号):
ProjectA
- a@0.1.0
    - d@0.2.0
    - c@0.1.0
- b@0.1.1
    - e@0.1.2
    - c@0.1.2
- c@0.2.0
复制代码

则其中一种输出的依赖优先级排序为:
['d@0.2.0', 'c@0.2.0', 'a@0.1.0', 'e@0.1.2', 'b@0.1.1']

输出分析: 为了让 a 加载后可以争取执行,则必须先加载 d 和 c,b 的加载同理,又因为在整个依赖关系下,c 的最新版本为 0.2.0 于是有了如上的输出结果。


  1. 请用 React 实现一个搜索框组件,功能包括:
  • 输入文本字数限制
  • 可配置输入文本约束,比如仅限输入数字
  • 用户输入时可支持关键字搜索,并出现下拉框展示相关项
    image

俺的答案

  1. 第一题leetcode原题来的,环形链表好像是,可以用快慢指针或者简单的集合
const cycle1 = function (node) {
  let set = new Set()
  while (node) {
    if (set.has(node))
      return true
    else
      set.add(node)
    node = node.next
  }
  return false
};



const cycle2 = function (node) {
  let start = node
  let end = node.next
  while (start !== end) {
    // 没有环就null
    if (end === null || end.next === null) return false
    start = start.next
    end = end.next.next
  }
  return true
}
复制代码
  1. 第二题的话我拿到题目第一个想到的就是DFS来寻找那些依赖,然后最后再对依赖这些进行版本比较(其实应该用集合、还有拓扑排序来优化)

这题有大佬在评论区指出错误啦!之前那个版本是比较字符串,但是忽略了多位数字版本的情况,例如b@0.2.22b@0.2.3,结果应该是前者,但之前版本是输出后者,现在修改了一下!好嘞!

function update(npmList) {
  let versions = {}
  let res = []

  // 比较版本号
  function cmp(a, b) {
    const versionListA = getVersion(a).split('.')
    const versionListB = getVersion(b).split('.')
    for (let index = 0; index < 3; index++) {
      const versionA = parseInt(versionListA[index])
      const versionB = parseInt(versionListB[index])
      if (versionA > versionB) return a
      else if (versionA === versionB) continue
      else return b
    }
    return a
  }

  // 获得版本号
  function getVersion(str) {
    return str.substr(str.indexOf('@') + 1)
  }

  function dfs(npmList) {
    if (npmList.length === 0) return

    npmList.forEach((npm) => {
      const { name, deps = [] } = npm
      // 先遍历他们的依赖
      dfs(deps)
      let key = name.substr(0, name.indexOf('@'))
      // 如果依赖不存在则添加,若已存在,则取最新版
      if (!versions[key]) {
        versions[key] = name
      } else {
        versions[key] = cmp(versions[key], name)
      }
      // 添加进最后的加载列表
      res.push(key)
    })
    return
  }
  dfs(npmList)
  // 去除重复项,然后将包名转换为依赖名,eg: a -> a@0.1.0
  return [...new Set(res)].map(key => versions[key])
}
复制代码
  1. 第三题的话,我粗略写了一下噗,写的也不是很好,用React整的

// 第三题React部分第三题React部分第三题React部分第三题React部分第三题React部分

import React, { Component } from 'react';
import './input.css'

function debounce(fn, delay = 500) {
  let timeout = null
  return function (e, ...args) {
    e.persist && e.persist()
    timeout && clearTimeout(timeout)
    timeout = setTimeout(() => {
      fn.call(this, e, ...args)
    }, delay)
  }
}

class Tips extends Component {
  render() {
    const { tipsList } = this.props
    return tipsList && tipsList.length !== 0 ? (
      <div className="tips__container">
        {tipsList.map((item, index) => {
          return (
            <a href="#" key={index} className="link">{item}</a>
          )
        })}
      </div>
    ) : <div></div>
  }
}

export default class Input extends Component {
  constructor(props) {
    super(props);
    this.state = {
      keyWords: [
        '前端工程师1', '前端高级开发1', '后端工程师1', '测试开发1', '项目主管1', 'dress', 'Recent', '123456', 'awdad1'
      ],
      inputValue: '',
      inputType: 'text',
      inputMaxLen: 20,
      wordsList: []
    }
    this.handleInput = debounce(this.handleInput, 200)
    this.handleMaxLenChange = debounce(this.handleMaxLenChange, 400)
  }

  handleInput = (e) => {
    const { target: { value } } = e
    const { keyWords } = this.state
    const tipsList = !value
      ? []
      : keyWords.filter(item => {
        const res = item.search(new RegExp(value, 'i'))
        return res !== -1
      })
    this.setState({
      inputValue: value,
      tipsList
    })
  }

  handleTypeClick = (e) => {
    const { target: { name } } = e
    this.setState({ inputType: name })
  }

  handleMaxLenChange = (e) => {
    const { target: { value } } = e
    const { inputValue } = this.state
    const newInputValue = inputValue.substr(0, +value)
    // 如果设置最大长度小于现在关键词的长度,则截取一下
    this.input.value = newInputValue
    this.setState({ inputMaxLen: value, inputValue: newInputValue })
  }

  render() {
    const { tipsList, inputType, inputMaxLen } = this.state
    return (
      <div className="container">
        <div className="control__container" onClick={this.handleTypeClick}>
          <button name="text">文本</button>
          <button name="number">数字</button>
          <span>最大长度: </span>
          <input type="number" placeholder="默认: 20" onInput={this.handleMaxLenChange} />
        </div>
        <div className="input__container">
          <div className="input__wrap">
            <input
              ref={input => this.input = input}
              placeholder="请输入关键词"
              type={inputType}
              maxLength={inputMaxLen}
              onInput={this.handleInput} />
            <button>搜索</button>
          </div>
          <Tips tipsList={tipsList} />
        </div>
      </div>
    )
  }
}
复制代码
// 第三题CSS部分第三题CSS部分第三题CSS部分第三题CSS部分第三题CSS部分第三题CSS部分


.container {
  width: 600px;
  height: 400px;
  margin: 0 auto;
  padding: 30px;
  background: #fff;
}

.input__container {
  margin-top: 30px;
}

.input__wrap {
  display: flex;
  align-items: center;
}

.input__wrap input {
  box-sizing: border-box;
  width: 85%;
  height: 50px;
  padding: 0 10px;
  border: #666 1px solid;
  border-right: 0;
  outline: none;
}

.input__wrap button {
  cursor: pointer;
  box-sizing: border-box;
  width: 15%;
  height: 50px;
  color: #fff;
  font-size: 20px;
  border: none;
  border: #666 1px solid;
  outline: none;
  background: #1890ff;
}

.control__container {
  display: flex;
  align-items: center;
}

.control__container button {
  cursor: pointer;
  width: 50px;
  height: 30px;
  margin-right: 10px;
  color: #fff;
  outline: none;
  border: #333 1px solid;
  border-radius: 8px;
  background: #1890ff;
}

.control__container span {
  margin-left: auto;
  margin-right: 10px;
  color: #666;
  font-size: 14px;
}

.tips__container {
  overflow-y: scroll;
  max-height: 200px;
  border: #333 1px solid;
  border-top: 0;
}

.tips__container .link {
  display: block;
  height: 30px;
  padding: 5px 10px;
  color: #666;
  line-height: 30px;
  text-decoration: none;
}

.tips__container .link:hover {
  color: #fff;
  background: #666;
}

input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
  display: none;
}
复制代码

二面

二面主要是结合项目来问的,抓住一个功能发散开来,例如我项目的聊天室的功能,吓得我好慌好慌hhh,不过面试官人很好一直引导我,特别是那些场景题,引导我去思考,啊,太感动了!!!

  1. 项目の各种东东
  2. 聊天私聊怎么做
  3. 聊天记录未读消息怎么做
  4. 聊天离线信息处理
  5. session会话管理如何实现(用户登录后,打开新标签页输入url访问资源)
  6. 访问资源权限控制
  7. 事件队列题
  8. websocket如何连接
  9. 做题系统组件的设计与拓展(拓展更多类型的题目,如何设计组件)
  10. 用户鉴权系统设计
  11. 为什么密码表跟用户信息表分开放
  12. 数据库表的设计

三面

这一面也是结合项目来问的。啊,三面的面试官真的太好了,期初我的小项目没什么难点(我都以为凉透透了呜呜呜),然后面试官给了提了几个建议,让我多去深入思考思考,面试完之后去找他请教问题,也一直很有耐心给我指导指导,啊,太棒了吧🤭

  1. 项目の各种东东
  2. 飞猪为什么挂了
  3. 项目难点
  4. 小程序登录怎么做的
  5. 封装了什么组件
  6. 建议:在线阅卷、批注、修改错别字等(canvas绘制)
  7. 建议:实时监控每个同学的进度(选择答案后之后教师端更新/摄像头监控)
  8. a与b聊天,将他们的记录,多选,然后合并发给c,如何设计
  9. 除了编译成小程序,有试过app吗

四面

四面应该是总监面,还是得感谢三面的面试官给我提的建议,之后做出来之后,发现这个东东可以作为项目的一个难点来吹哈哈哈!四面主要还是围绕项目来问,我个人觉得关注的更是自己的思维、对技术的认识、对自己未来的发展规划等宏观的内容

就是这个小功能,canvas实现作业批注等功能

  1. 项目难点(canvas绘制公式
  2. websocket实现,聊天功能的实现(心跳检测,断线重连
  3. 项目の细节
  4. 聊天信息的一致性,时序性
  5. 目标、具体想通过实习学到什么
  6. 技术规划,之后学些什么

五面

五面是交叉面,心惊胆跳呜呜呜,希望不要挂我...五面问的大多是基础方面的内容,不过也是结合项目来问的,这一面面试官主要关注的是性能方面的内容,例如数据埋点啊、页面加载时间、接口响应时间等一系列关于性能方面的问题,他希望的是数据量化的一个东东,具体的实现,达成了什么目标等

  1. 项目做了那些事情
  2. 小程序运行池
  3. 小程序和H5的区别
  4. 缓存存在哪(强缓存、协商缓存分别通过什么字段保存
  5. React与Vue的区别
  6. React的优势
  7. Taro编译的机制
  8. Taro支持的端有哪些
  9. node的机制、优势
  10. 项目的难点,如何解决,遇到的问题
  11. 如何监控性能
  12. 尝试做了哪些性能上的优化
  13. 收集用户信息?数据埋点?性能指标?量化指标?
  14. 资源大小,加载速度,页面渲染时间,接口访问时间
  15. 如何优化node后台的接口(sql优化,表结构重写
  16. 使用什么服务器,部署在什么操作系统上
  17. 多人同时访问接口测试过吗?最高承载多少
  18. webpack如何减小资源打包大小
  19. 擅长什么
  20. 你的优势是什么
  21. 目标?规划?

腾讯のWXG开放平台小程序基础部

一面

一面是笔试 + 面试,俺也放放题目跟俺的答案吧!

笔试题目

  1. 实现⼀个函数 reverse(a, n) ,反转⼀个含有 n 个整数的数组 a(直接在数组a上操作,元素交换次数 尽可能少,不能使⽤js Array 类内置属性和⽅法)。

  1. 实现⼀个函数 countLongest(tree) ,输⼊⼀棵⼆叉树,返回⼆叉树中距离最⻓的两个叶⼦节点之间 的距离。
var x = [0, 1, 2, 3]
reverse(x, 4) // x = [3, 2, 1, 0]
var y = [1, 2, 3, 4, 1]
reverse(y, 5) // y = [1, 4, 3, 2, 1]
var tree1 = {
  value: 1,
  left: {
    value: 2
  },
  right: {
    value: 3
  }
}
countLongest(tree1) // 2
var tree2 = {
  value: 1,
  left: {
    value: 2,
    left: {
      value: 3,
      left: {
        value: 6
      }
    },
    right: {
      value: 4
    }
  },
  right: {
    value: 5
  }
}
countLongest(tree2) // 4
复制代码

  1. 在前端开发中,通常会把多个js⽂件合并成⼀个⽂件,以减少⽹络请求次数,达到优化加载速度的⽬ 的,但是当⽂件之间存在依赖关系时,对js合并的顺序,会有⼀定的要求,⽐如 A.js 依赖了 B.js,那打 包后的⽂件,B.js 需要排在 A.js 的前⾯。 实现⼀个函数 resolve(tree) ,根据js的依赖关系树 tree,输出合理的打包顺序的数组(结果可能不 唯⼀,输出其中⼀种即可)。

样例

var tree1 = {
  name: 'main.js',
  require: [{
    name: 'A.js'
  }, {
    name: 'B.js'
  }]
}
resolve(tree1) // ['A.js', 'B.js', 'main.js']
var tree2 = {
  name: 'page.js',
  require: [{
    name: 'A.js',
    require: [{
      name: 'B.js',
      require: [{
        name: 'C.js'
      }]
    }]
  }, {
    name: 'D.js',
    require: [{
      name: 'C.js'
    }, {
      name: 'E.js'
    }]
  }]
}
resolve(tree2) // ['C.js', 'E.js', 'D.js', 'B.js', 'A.js', 'page.js']
复制代码

  1. 给定⼀个整数数组 a,实现⼀个函数 countMax(a) ,计算出从 a 中选择出多个不相邻元素组成最⼤的 和是多少。

样例

var x = [1, 4, 5, 3]
countMax(x) // 7
var y = [3, 12, 6, 2, 4]
countMax(y) // 16
复制代码

俺的答案

  1. 就是简单的倒置hhh
function reverse(arr) {
  let len = arr.length
  for (let start = 0; start < Math.floor(len / 2); start++) {
    let end = len - start - 1;
    [arr[start], arr[end]] = [arr[end], arr[start]]
  }
  return arr
}
复制代码
  1. 这题是leetcode原题好像,就算算深度
function countLongest(tree) {
  if (!tree) return 0
  let res = 0

  function dfs(node) {
    if (!node) return 0
    const leftMax = dfs(node.left)
    const rightMax = dfs(node.right)
    res = Math.max(leftMax + rightMax, res)
    return Math.max(leftMax, rightMax) + 1
  }
  dfs(tree)
  return res
}

console.log(countLongest({
  value: 1,
  left: {
    value: 2
  },
  right: {
    value: 3
  }
}))
console.log(countLongest({
  value: 1,
  left: {
    value: 2,
    left: {
      value: 3,
      left: {
        value: 6
      }
    },
    right: {
      value: 4
    }
  },
  right: {
    value: 5
  }
}))
复制代码
  1. 第三题是不是很眼熟哈哈哈,跟盒马一面的笔试题好像(其实我发现很多面试笔试题都有这相关的影子)还是DFS来找
function resolve(npmList) {
  const res = []

  function dfs(npmList) {
    if (npmList.length === 0) return

    npmList.forEach((npm) => {
      const { name, require = [] } = npm
      dfs(require)
      !res.includes(name) && res.push(name)
    })
    return
  }
  dfs(npmList)
  return res
}


console.log(resolve([{
  name: 'page.js',
  require: [{
    name: 'A.js',
    require: [{
      name: 'B.js',
      require: [{
        name: 'C.js'
      }]
    }]
  }, {
    name: 'D.js',
    require: [{
      name: 'C.js'
    }, {
      name: 'E.js'
    }]
  }]
}]))
复制代码
  1. 用动态规划来找
function countMax(arr) {
  const len = arr.length
  const dp = new Array(len).fill(0);
  dp[0] = arr[0]
  dp[1] = arr[1]
  dp[2] = arr[0] + arr[2]
  for (let i = 3; i < len; i++) {
    dp[i] = arr[i] + Math.max(dp[i - 2], dp[i - 3])
  }
  return Math.max(dp[len - 1], dp[len - 2])
}

console.log(countMax2([1, 4, 5, 3]))
console.log(countMax2([3, 12, 6, 2, 4]))
复制代码

面试内容

  1. 项目
  2. 为什么用token不用cookie
  3. 跨域(前端跟前端的跨域,iframe之间)
  4. xss
  5. React与Vue的对比
  6. Taro与其他多端框架
  7. 主要还是项目发散
  8. 如何实现轮播图

二面

二面基本都是一些基础吧,但是就有些地方会深入去挖这样子

  1. https原理,握手过程(何时对称/非对称,谁先谁后,为什么这样)
  2. 常见的优化
  3. webp格式优化了多少
  4. 缓存以键值形式存在浏览器,键是什么,值是什么
  5. 设计一个缓存策略,(hash值)
  6. React的key
  7. React与Vue的区别
  8. Taro与小程序官方框架的区别
  9. 小程序运行池
  10. React列表key固定,顺序调换会渲染吗(不会)
  11. 如何判断性能瓶颈
  12. 项目の各种东东

三面

三面感觉还不够二面难,问的比较常见吧应该说,然后也是问问项目这样子

  1. 算法:判断数组中是否存在两个数相加等于目标值,给出多种思路与时间空间复杂度(暴力循环,排序后循环剪枝,动态规划)
  2. es6的class如何实现私有变量(symbol + 闭包)
  3. 如何进行性能监控
  4. 常见的性能优化方法
  5. 内存泄露如何发现,如何解决
  6. 垃圾回收机制
  7. 跨域(cors + jsonp + 其他不常见的跨域方法)
  8. 浏览器缓存
  9. 实现深拷贝,深拷贝的用途
  10. xss、csrf
  11. cookie与token的工作原理,区别,如何设计
  12. http1.1、http2.0
  13. http无状态
  14. websocket是什么协议,如何连接
  15. websocket有什么优势,对比轮训呢
  16. 事件循环
  17. setTimeout是否准时,如果不是则应该提前还是延迟
  18. webpack流程
  19. 常见的http状态码
  20. babel原理、taro原理
  21. map中的键值会不会被回收(weakMap,weakSet等)
  22. 项目....难点、设计、收获
  23. 平时如何学习

美团到店事业部

一面

  1. 项目
  2. 原型链
  3. 继承
  4. instanceof
  5. 事件循环
  6. 异步任务有哪些,底层如何实现(不会
  7. websocket
  8. 轮询的弊端
  9. websocket的弊端与优势
  10. TCP三次握手
  11. 为什么需要三次握手
  12. 如果两次握手会发生什么,保持不必要的链接主要是浏览器端还是服务端收到影响大
  13. TCP的拥塞问题(不会
  14. xss和crsf
  15. 跨域(种类,如何使用
  16. 同源策略
  17. 跨域为了解决什么问题
  18. cookie和token
  19. 如何使用token,设计
  20. 意向城市
  21. 为什么选择前端
  22. 前端学习的规划
  23. koa中间件模型,洋葱模型
  24. 三道算法
1. 合并两个有序链表
2. 找出数组第K大的数
3. 晨晨是个爱跑步的孩子,这一天,他准备跑正好k米。
他所在的城市的道路可以看做n个点,m条无向边组成的图,
每条边有一个固定的长度。  晨晨有强迫症,

他跑步前往一个目的地一定要走最短路(当然有多条最短路就可以随意选择了)。  
晨晨希望知道,他正好跑k米能走到的目的地的个数。


注意,目的地可能在图中的点和边上,且该目的地距离晨晨的起点的最短路正好k米。 
若k大于所有路径之和自然不存在这样的目的地,输出结果自然为0。

复制代码

二面

  1. function和箭头函数
  2. 箭头函数有什么特殊的地方
  3. React的生命周期哪些是不安全的,为什么
  4. 为什么会出现React Hooks
  5. Memo跟useCallback的区别与联系
  6. hooks模拟生命周期
  7. 内存泄漏
  8. 如何监控内存泄漏,如何发现(全局变量,闭包,监听事件,定时器
  9. SPA的优缺点
  10. 首屏加载如何优化
  11. webpack的plugin跟loader的区别
  12. css垂直居中
  13. margin相对于谁
  14. 移动端用什么布局
  15. 如何还原设计稿
  16. em、rem
  17. margin-top百分比相对于什么
  18. 图片格式之间的区别
  19. 如何检测浏览器不支持webp(img onerror
  20. Promise的reject跟catch,场景,你如何使用
  21. class 与 function的区别
  22. class实现静态变量
  23. for in 和 Object.keys的区别
  24. 类继承的特点
  25. ES6的Map与对象有什么区别
  26. git rebase/stash/commit
  27. 不用div如何包裹多个元素(Fragment,<><>).
  28. 原型链,作用,如何实现,原理
  29. 盒模型
  30. 笔试题:数组扁平化排序去重
  31. 笔试题:实现柯里化函数

三面

  1. 项目成就,经历,心得(疯狂追问
  2. css移动方块(css3,setInterval,requestAnimationFrame)
  3. 为什么requestAnimationFrame能够做到60帧
  4. javascript引擎,v8引擎,垃圾回收
  5. v8引擎跟别的有什么区别
  6. 代码编译大致流程
  7. babel
  8. http跟https区别,具体
  9. 如果https发来证书,但是我没有CA的公钥怎么办(用久的请求CA新的
  10. 堆跟栈的区别
  11. 还有别的什么内存块
  12. 垃圾回收
  13. 如何手动垃圾回收(定时器,监听事件,对象等
  14. 两个this指向的问题
  15. 笔试部分
实现自定义数组下的排序方法(挂载到数组原型)
实现下划线字符串转换大驼峰(asf_ad_ada_adwa -> AsfAdAdaAdawa)
实现发布订阅(on,off,emit,once)
复制代码