参考文章

电话预约

注:电面前,请记得用xxx的拨号功能来拨打,会自动隐藏真实号码。避免用自己手机直接拨打。

• 您好,我是xxxx前端技术部的前端工程师:xxx,我这边收到了您的一封简历,现在想和您做一个电话沟通,不知道您现在方便吗?
(如果不方便)
• 好的,那我们再约个时间,你什么时候方便?

面试流程

  • 自我介绍
  • 确认对方意向(公司、职位、地点)
  • 应聘者自我介绍
  • 面试相关知识及考察点
  • 面试结束,告知后续的节奏点,比如有人会通知结果

面试技术点

HTML

  • 行内元素和块级元素有什么区别?各自有哪些代表性的标签?
    • 是否独占一行
    • 是否可以互相包含
  • 怎么理解 HTML 的语义化?
    • html 标签的使用
    • css className 的使用
    • 无障碍
  • 纯 html 怎么实现点击表单项的标签文本可以触发右边的单选框勾上?
  • 纯 html 怎么实现点击表单里面的一个按钮触发表单提交?

CSS

  • css position 有哪些值?默认值是什么?分别是怎么样的行为?
    • absolute 相对谁?
    • 答出新的 sticky 加分
  • css overflow 有哪些值?默认值是什么?分别是怎样的行为?
  • css box-sizing 有哪些值?默认值是什么?分别是怎样的行为?
  • 水平居中
    • 行内元素容器怎么让子元素水平居中?text-align:center;
    • 块级元素容器怎么让子元素水平居中?margin: 0 auto;
    • flex 容器怎么让子元素水平居中? justify-content: center;
    • 如果子元素是绝对定位的,怎么水平居中?left:50%; transform:translate(-50%,0);
  • 用 top left 定位和 transform 的区别
    • 是否影响父元素大小和兄弟元素位置
    • 性能
  • display: none;与visibility: hidden;的区别
  • css 动画一般会有哪些实现?
    • transition
    • animation
    • js
  • Less
    • 怎么定义变量?
    • mixin 的作用是什么?定义的时候加不加括号有什么区别?
    • 父选择器的几种用法?
      • 普通
      • 中划线
  • rem 这个单位了解过吗?

JavaScript

  • js 中有哪些数据结构类型?
    • primitive 的数据类型,可以用 typeof 判断的: undefined、Boolean、Number、String、BigInt、Symbol
    • 复合类型:
      • Object:衍生出 Array、Map、Set、WeakMap、WeakSet、Date, typeof 的结果全是 ‘object’
      • Function
  • 特殊类型:null,typeof 的结果是 ‘object’, 但是本身是 primitive 的
  • 如何判断数据结构类型?
    • 最好的方式是 bject.prototype.toString.call,但是自定义的 class 会返回 object,可以用 instanceof 做进一步筛选
  • 数组里 forEach 和 map 的使用场景是什么?
    • 如果 map 循环里面想要执行异步的逻辑,并且要拿到返回值该怎么办?
  • var let const 定义一个变量的差别是啥?
    • let 可以在块级作用域
  • 一个函数执行时候的 this 取决于什么因素?
    • 怎么绑定一个函数执行时候的 this?
      • call 和 apply、bind 的作用是什么,有什么区别?
  • 箭头函数的准确定义是啥?
  • JS 函数调用是按值传递还是按引用传递?
  • 能说说你对闭包的理解吗?是什么意思?一般有什么应用场景?
  • addEventListener 的第三个参数 capture true false 的差异是啥?
  • 怎么阻止事件冒泡?
  • Promise.all() 和 Promise.race() 的作用分别是啥?
  • 正则表达式后面的 g、i 这些修饰字符分别是什么意思?
  • 怎么获取一个 dom 节点的坐标位置?

ES6

  • 箭头函数会被 bable 或 ts 编译成啥知道吗?
  • class 会被 bable 或 ts 编译成啥知道吗?
    • class 属性,函数
    • constructor 代码和属性初始值代码的顺序
    • class extends 会编译成啥
    • static 属性和 static 方法会编译成啥?
    • constructor 返回值的意义是啥?
  • async/await 被 babel 或 ts 往下编译的时候主要思路是啥?
  • lodash 的 debounce 和 thottle 分别是干嘛的?

    TypeScript

  • 有哪些基础概念?

    • interface、type、enum
  • 泛型的使用场景有哪些?
  • declare merge 了解过吗?

    React

  • react 的 jsx 会被编译成什么样?

  • setState 是同步的还是异步的?为什么会设计成这样?
  • class 组件有哪些生命周期?如果是 function 组件,怎么用 hooks 来模拟这些生命周期?
  • react 父组件怎么拿到子组件的变量和方法?
  • react 子组件怎么拿到父组件的变量和方法?
  • react 组件怎么获取到自己渲染的 dom 节点?
  • HOC 高阶组件的原理和应用场景是啥?
  • render props 了解吗?解决什么问题?
  • context 了解吗?解决什么问题?有哪些不同的新老 api?
  • react 的 PureComponent 和 Component 两个基类有啥差异?
  • Portal 了解过吗?作用是啥?
  • react router 的大致原理是啥?
  • virtual dom 存在的意义是什么?
  • react 里面阻止事件冒泡之后,其它地方就一定监听不到了吗?
  • redux 是解决什么问题的?有哪些概念?
  • react-redux 是解决什么问题的?有哪些概念?
  • react 里面 dangerouslySetInnerHtml 是干啥的?
  • mobx 了解吗?主要有哪些概念?

antd

  • Input 的 value 和 defaultValue 分别是干啥的?
  • Table 组件的 rowKey 是干嘛的?可以设置为随机数吗?可以设置为 1,2,3,4 吗?
  • 如果组件的 state 上有一个变量 dataSource,是给到 antd table 组件的 props,当你想要修改第二行的某一列的字段的时候,该怎么 setState?原因是啥?(immutable)
  • FormItem 的 fildName 熟悉是干嘛的
  • Menu 的 activeKey 和 MenuItem 的 key 有啥关系?在 MenuItem 里面 console.log(this.props.key) 会输出什么?
  • FormItem 的 filedName 是干嘛的

    Vue

  • 响应式是什么意思?实现原理是什么?

    Node.js

  • 浏览器端的 js 执行和 node.js 端的 js 执行最大的差异是啥?

    • 浏览器端怎么实现密集计算不阻塞页面渲染?
  • nodejs 的模块系统跟前端的模块系统分别是啥?
  • 了解过 express/koa/egg 吗?

网络

  • 跨域问题是指什么?
    • 浏览器的安全策略,对除了静态资源之外的 xhr 请求有同源限制
  • 跨域问题一般有哪些解决方案?原理是啥?
    • jsonp:客户端用 script 标签伪装成静态资源请求,避开 xhr 的同源限制,服务端返回一段JavaScript 代码,其中会执行一个客户端 js 作用域内存在的 callback 函数,函数名是客户端指定的,从而拿到返回值
    • cors:客户端发起 fetch 请求的时候指定 cors 模式,服务端返回对应的 cors 头
  • cookie 和 session 的主要区别是什么?
  • 主流网站的登录是怎么实现的?
  • HEAD 请求是干嘛的?
  • OPTION 请求是干嘛的?
  • cookie 的 httponly 属性是干嘛的?
  • cookie 的 secure 属性是干嘛的?

数据库

  • join
  • left join 原理是什么?
  • join 的 on 和 select 的 where 差别是什么?
  • limit 5 和 limit 5, 10 分别是什么意思?
  • 一般应该对哪些字段加索引?
  • 唯一所有和普通索引的差别是什么?

    工程

  • webpack

    • 解决什么问题的?用过哪些插件?
    • 怎么优化构建速度和减少构建体积?
    • CommonChunkPlugin 了解吗
  • babel
    • 解决什么问题的?用过哪些插件?
  • 多人合作时如何保证项目整体的代码质量?
    • lint
    • cr
    • test/ci
      • jest
      • snapshot
  • git
    • 常用命令有哪些?分别作用是啥?
    • rebase 和 merge 有什么差异?分别在什么场景下用?
  • 说说你了解的有哪些景点的设计模式?以及他们分别可以对代码组织起到什么作用?

算法

  • js 里面如果你要实现一个链表,一般会采取怎样的数据结构?大致思路是怎样的?

    综合技巧

  • 如果一个网页访问有性能问题,你会怎么办?

    • chrome performance 火焰图
    • js 加载体积很大怎么办?
      • webpack dynamic import
    • 执行卡顿
  • 如果用户反馈访问你们公司的一个网页看到一个 404 界面,你会怎么办?
    • 确认是后端 404 还是前端路由 404,前端路由展开到路由系统

个人的发展潜力

说说你平常关注的一些技术相关的网站,个人等信息来源?
你觉得写 js 和你平常写 c++ java 有没有什么感受上的差异吗?可以随便谈谈感受

面试结束语

  • 我的问题问完了,请问你有什么问题要问的么?
  • 今天的沟通就到这里,沟通的结果我们会尽快反馈给你。谢谢,再见!

笔试题

写一段代码使得点击页面的任意div标签时都能弹出当前时间戳

参考答案:

  1. document.body.addEventListener('click', function(ev){
  2. var target = ev.target || ev.srcElement;
  3. if(target.tagName.toLowerCase() === ''){
  4. alert(new Date().getTime())
  5. }
  6. });

请实现一个 EventBus 模块,可以实现自定义事件的订阅、触发、移除功能,功能如下所示

  1. const eventBus = new EventBus();
  2. function handleSleep1(){
  3. console.log('sleep1');
  4. }
  5. function handleSleep2(){
  6. console.log('sleep2');
  7. }
  8. function handleSleep3(){
  9. console.log('sleep3');
  10. }
  11. // 一堆监听
  12. eventBus.on('sleep', handleSleep1);
  13. eventBus.on('sleep', handleSleep2);
  14. eventBus.on('sleep', handleSleep1);
  15. eventBus.on('sleep', handleSleep3);
  16. // 取消一个
  17. eventBus.off('sleep', handleSleep3);
  18. // 触发
  19. eventBus.emit('sleep');
  20. // 预期正确输出是(重复监听不生效、按监听顺序执行、取消的不生效)
  21. // sleep1
  22. // sleep2
  23. // 全部取消
  24. eventBus.off('sleep');
  25. // 触发
  26. eventBus.emit('sleep');
  27. // 预期的正确输出是:没有输出

参考答案:

  1. class EventBus {
  2. eventPool = {};
  3. on(eventName, handler) {
  4. if (!this.eventPool[eventName]) {
  5. this.eventPool[eventName] = [];
  6. }
  7. if (this.eventPool[eventName].includes(handler)) {
  8. return;
  9. }
  10. this.eventPool[eventName].push(handler);
  11. }
  12. emit(eventName) {
  13. const pool = this.eventPool[eventName];
  14. if (pool) {
  15. pool.forEach(handler => handler());
  16. }
  17. }
  18. off(eventName, handler) {
  19. if (!this.eventPool[eventName]) {
  20. return;
  21. }
  22. if (handler) {
  23. const targetIndex = this.eventPool[eventName].findIndex(item => item === handler);
  24. if (targetIndex !== -1) {
  25. this.eventPool[eventName].splice(targetIndex, 1);
  26. }
  27. } else {
  28. this.eventPool[eventName] = [];
  29. }
  30. }
  31. }

将输入的数组组装成一颗树状的数据结构,注意边界异常值处理,功能如下所示:

  1. // 输入 1
  2. transform([
  3. { id: 1, name: 'i1' },
  4. { id: 2, name: 'i2', parentId: 1 },
  5. { id: 4, name: 'i4', parentId: 3 },
  6. { id: 3, name: 'i3', parentId: 2 },
  7. ]);
  8. // 输出 1
  9. /**
  10. {
  11. "id": 1,
  12. "name": "i1",
  13. "children": [
  14. {
  15. "id": 2,
  16. "name": "i2",
  17. "parentId": 1,
  18. "children": [
  19. {
  20. "id": 3,
  21. "name": "i3",
  22. "parentId": 2,
  23. "children": [
  24. {
  25. "id": 4,
  26. "name": "i4",
  27. "parentId": 3,
  28. "children": []
  29. }
  30. ]
  31. }
  32. ]
  33. }
  34. ]
  35. }
  36. */
  37. // 输入 2
  38. transform([
  39. { id: 1, name: 'i1' },
  40. { id: 2, name: 'i2', parentId: 1 },
  41. { id: 4, name: 'i4', parentId: 3 },
  42. { id: 3, name: 'i3', parentId: 2 },
  43. { id: 11, name: 'i11', parentId: 'UFO' },
  44. ]);
  45. // 输出 2
  46. /**
  47. {
  48. "id": 1,
  49. "name": "i1",
  50. "children": [
  51. {
  52. "id": 2,
  53. "name": "i2",
  54. "parentId": 1,
  55. "children": [
  56. {
  57. "id": 3,
  58. "name": "i3",
  59. "parentId": 2,
  60. "children": [
  61. {
  62. "id": 4,
  63. "name": "i4",
  64. "parentId": 3,
  65. "children": []
  66. }
  67. ]
  68. }
  69. ]
  70. }
  71. ]
  72. }
  73. */
  74. // 输入 3
  75. transform([
  76. { id: 1, name: 'i1', parentId: 4 },
  77. { id: 2, name: 'i2', parentId: 1 },
  78. { id: 3, name: 'i3', parentId: 2 },
  79. { id: 4, name: 'i4', parentId: 3 },
  80. ]);
  81. // 输出 3
  82. /**
  83. {
  84. "id": 1,
  85. "name": "i1",
  86. "children": [
  87. {
  88. "id": 2,
  89. "name": "i2",
  90. "parentId": 1,
  91. "children": [
  92. {
  93. "id": 3,
  94. "name": "i3",
  95. "parentId": 2,
  96. "children": [
  97. {
  98. "id": 4,
  99. "name": "i4",
  100. "parentId": 3,
  101. "children": []
  102. }
  103. ]
  104. }
  105. ]
  106. }
  107. ]
  108. }
  109. */

参考答案:

  1. function findTopParent(list) {
  2. return list.filter((item) => !item.parentId);
  3. }
  4. function findChildren(list, targetItem) {
  5. return list.filter((item) => item.parentId === targetItem.id);
  6. }
  7. function getChildren(list, targetItem) {
  8. return findChildren(list, targetItem).map((item) => ({
  9. ...item,
  10. children: getChildren(list, item),
  11. }));
  12. }
  13. function transform(list) {
  14. if (Object.prototype.toString.call(list) !== '[object Array]') {
  15. return {};
  16. }
  17. // 找到顶级父结构
  18. const top = findTopParent(list);
  19. if (top.length !== 1) {
  20. return {};
  21. }
  22. const topItem = top[0];
  23. // 找到当前父结构的所有子结点
  24. return {
  25. ...topItem,
  26. children: getChildren(list, topItem),
  27. };
  28. }