阅读 9267

阿里盒马&数农、腾讯WXG小程序团队 —— 小菜鸡自闭の面经😿(等HRing...)

啰嗦の前言

俺这只小菜鸡🐔终于也快迎来曙光了!😀

俺现在是一名大三学生,渴望能获得一份大厂的实习机会呜呜呜,所以不自量力的投了一下阿里跟腾讯。阿里是提前批就开始面了的。很多部门都面了一下下,面了啥也忘了哈哈哈哈。也多亏了提前批,让俺知道哪些地方还需要再补补。

阿里这边最终选择了盒马,盒马的面试官都好好,好和蔼,给了好多建议,没有因为我菜就嫌弃我呜呜呜,太感动了。而且!看到这张“屁股脸”难道不想加入换一套限量公仔/手办吗!

个人の想法

  • 我觉得面试更像是一次学习的机会,可以查漏补缺,之后自己深入学习更多的东西
  • 我还是觉得大厂面试,还是得有一个自己熟悉的、参与度高的项目来展开,因为还是很多面试官会选择问问项目经历来了解我们。
  • 不会就是不会哈哈哈哈,老实承认就好,之后自己下去再找找资料补一补就好!不必沉浸在悲伤之中
  • 面试完之后,如果有问题,尽量积极主动的去寻找面试官请教,不要觉得不好意思或者说不敢这样子,我觉得就算即便是挂了也好,也可以请教一下学到更多的东东,不至于一场面试下来颗粒无收

阿里の盒马

初面

初面是聊得最久的一次了,一个多小时了吧,不过初面的面试官真的很让人感动一直在鼓励我,“好啊好啊”,“没关系没关系”,啊太棒了,给俺这个小菜鸡很多信心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. 目标?规划?

腾讯の小程序

一面

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

笔试题目

  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. 平时如何学习

菜鸡の总结

自己还是太菜了呜呜呜😭

希望HR面的时候放俺一条生路可好...

还是觉得面试不要慌,慌也没有用,不如让自己冷静一下,然后每次回答问题的时候想一下应该怎么表达会更清晰(我就是表达不行,面试官都理解不了俺在说啥hhhhh)。问的问题除了结合项目来发散,还喜欢出一些情景题,现场设计一个组件啊、一个缓存策略啊、一套权限控制啊之类的内容。到时候就随机应变,想一下再说出来就好啦!

最后还是祝愿大家春招早日结束战斗!!拿到心爱的offer!!