农学类专业转行做【前端】的秋招历程

本硕211,农学类专业,硕士期间方向是生物信息学(算是计算机相关吧),自学前端断断续续有一年了。
经历了两个月的秋招过程,最终勉强收到了58和shopee的意向书(虽然感觉应该都是白菜),还有几家在池子里等着开奖。

下面分享一下我的面试记录吧,希望能给其他小伙伴,尤其是我这种转行的一点参考~

10.01 更新:收到58、shopee、贝壳、好未来的意向书了

08.05 ~ 09.07

这一个月经历各种一面挂、二面挂、hr面后排序挂。最后只拿到了一个需要实习的offer,但是导师不让实习,这就只能继续面了。最***均每两天收到一封感谢信,麻木了,菜是原罪。再坚持一个月吧。

这个阶段没有把问到的问题记录下来,只能把大致能想到问得比较多的写下来。

  • js 有哪些数据类型(基本上一上来就会问这个)

  • 手写一个 bind 函数 (被问超过3次)

    Function.prototype.myBind = function (...args) {
        let t = args.shift()
        return (...newArgs) => {
            this.call(t, args.concat(...newArgs))
        }
    }
    // 面试官会问:为什么return 箭头函数,this指向什么,为什么用call

    手写 call、apply 和 bind 可以参考我写的这篇博客:https://blog.csdn.net/qq_18369669/article/details/107727344

  • 实现 Promise.all (被问2次)

    Promise.myAll = function (promises) {
        return new Promise((resolve, reject) => {
            let count = 0
            const res = []
            const len = promises.length
            for (let i = 0; i < len; i++) {
                Promise.resolve(promises[i])
                    .then((val) => {
                        res[i] = val
                        count++
                        if (count === len) resolve(res)
                    }, (err) => {
                        reject(err)
                    })
            }
        })
    }

    实现Promise.all 和 Promise.race,可以参考这篇博客:https://blog.csdn.net/qq_18369669/article/details/108279773

  • 判断代码输出

    • this 指向 (问完后,继续问改变this指向的方法)
    • 异步(问完后让你回答宏任务、微任务)
  • 各种排序算法原理(快排问得多一点)

  • http 缓存策略(问到超过3次)、http2.0(问到超过2次)

  • vue 中双向绑定原理、路由原理 (超过3次)

  • 跨域相关、cookie相关,CORS如何带cookie

  • 项目中用到了哪些优化的措施

09.07 字节一面

估计凉的,比较疲惫,一上来就整懵了

  • 判断代码输出

    window.data = 5;
    var foo = {
      data: 6,
      click: () {
        console.log(this.data);
      }
    };
    div.addEventListener('click', foo.click); // undefined

    事件绑定的回调函数中 this 指向 DOM 元素

    var bar = foo.click;
    bar() // 5

    普通函数调用,this指向window

    export default {
      data () {
        return {
          data: 5
        }
      },
      methods: {
        windowClick () {
          console.log(this.data)
        }
      },
      mounted () {
        window.data = 3
        window.addEventListener('click', this.windowClick) // data
      }
    }

    vue 组件的方法中的 this 指向该 vue 实例,而vue实例可以直接访问data数据。然后面试官问了我,data 数据是怎么挂到 vue 实例上面的,我说,new Vue() 的时候有一个 mergeOptions 的过程。。也不知道怎么说了。

  • 异步相关

    • 实现一个 JS 的sleep
    async function sleep (time) {
        return new Promise(resolve => {
            setTimeOut(() => {
                resolve()
            }, time)
        })
    }

    没写出来,就写了async 和 setTimeout。

    async 函数应该返回一个Promise

    • 判断代码输出
    var number = 5;
    var scores = [98, 50, 70, 66];
    scores.forEach(async a => {
      number++;
      await sleep(1000);
      console.log(number);
    });

    输出4个9,答对了

    继续问,如何做才能输出6、7、8、9

    我答的是,用一个自调用函数,把number传进去,创建函数作用域

    其实还有一种方法,用一个let 变量(块级作用域)存number

  • 常用DOM查询函数、getElementsByClassName 和 querySelectorAll 的区别

    • getElementsByClassName 返回的是 HTMLCollection

    • querySelectorAll 返回的是 NodeList

    • getElementsByClassName 的结果是实时变动的

    • querySelectorAll 结果不是实时变动的

    区别没答出来

  • 手写 getElementById 函数

    function getElementById (node, id) {
      if (node.getAttribute('id') === id) return node
      let children = node.children
      for (let child of children) {
            let res = getElementById(child, id)
            if (res) return res
      }
    }

    本质上就是树的遍历,可以用广度优先或者深度优先。

    状态不好,第一遍没写对,面试官提醒下,硬着头皮写,居然还写对了。

  • 数组乱序

    比较简单的算法,写的时候取随机index,忘记取整了,不知道面试官注意到没

    以前总结过3种方法:https://blog.csdn.net/qq_18369669/article/details/106086809

  • 实现一个组件

    实现一个插入表格的工具框,工具框的内容主要为一个9 x 9的九宫格,通过鼠标悬浮选中,在鼠标点击的时候输出选中的行数和列数

    要求写出模版、js、样式

    按照 vue 的写法实现的,但是自己写的话,肯定是写一点看一下,这里相当于盲写,还没有代码补全,还是很不适应的。最后基本框架搭好了吧,细节肯定有很多问题。

  • 反问。然后就没有了

总结:async await 还不熟

09.08 多益网络 一面

基本是是问答形式,问题比较基础

印象深刻一点的是实现一个 js 的单例模式,但是qq视频,没有写代码

09.08 快手一面

  • 项目的创新点,如何构建项目的,了解哪些构建工具,webpack的打包过程,项目优化点

  • 实现节流、防抖,并测试

  • 实现继承

    function Parent () {  
    }
    function Child () {
       Parent.call(this)
    }
    Child.prototype = new Parent()

    面试官继续问,Child.prototype = new Parent()能不能不要调用 Perent 这个构造函数,我想不到,他提醒我说Object.create,当时还是有点蒙,在面试官的提示下,一步步写出来的。这里不熟练,估计很影响打分

    Child.prototype = Object.create(Parent.prototype)
    // Child.prototype 是要指向一个 Parent 的实例对象,但是不调用构造函数,如何创建一个实例对象呢,Object.create(Parent.prototype)可以创建一个对象,这个对象的__proto__ 指向 Parent.prototype,就是 Parent 的实例对象了

    然后让用 instanceof 验证,最后实现一个 isInstanceof

    function isInstanceof(child, Parent) {
        let p = child.__proto__
        while (p) {
            if (p === Parent.prototype) return true
            p = p.__proto__
        }
        return false
    }
  • 智力题 (感觉就是小学二年级的智力思考题,因为我在我弟弟的课本上见过类似的)

    求A, B, C的值
       CAB
    -  BAC
    _________
       BCA

    折腾了很久,说了一些思路,最后没有解答出来。(很气,很快就建立方程组了,但是有一个地方写错了,导致解出来是非整数,然后就被面试官的思路带乱了)

    转换成加法的算式:
       BCA
    +  BAC
    _________
       CBA
    发现 A + C 得 A、C + A 得 B,所以 A + C 不可能小于10,因为小于10的话应该得相等的值。(如果A和B相等,C就是0,C很明显不是0,因为B + B 得 C)
    A + C 就是一个 10 到 20 之间的值,那么,(A + C) % 10 = A,(C + A + 1) % 10 = B
    A + C 不可能大于20,也就是 A + C - 10 = A、A + C + 1 - 10 = B
    两式相减得:A = B + 1
    还有 A + C 进了一位,所以百位的 B + B + 1 = C
    联立以上各式,解得 A、B、C 分别为 5,4,9
  • 用两个栈实现队列

    比较基础的算法,思路是push的话就往stack1里面push就行了,shift的话就从stack2里面pop出来,如果stack2为空,就把stack1的全部pop出来,并依次放到 stack2 里面,之后再从stack2里面pop出来。

    写代码的时候,思路不清晰,忘了判断stack是否为空了。面试官也没说什么了

  • 反问

总结,准备过的东西就还好,没准备过的,在面试的时候还是状态很难调整好

09.10 字节二面

  • 双向绑定的原理

    题目:实现一个双向绑定,要求修改修改input,自动更新到p上面

    <input id="input" />
    <p id="value" />
    const data = {
        msg: ''
    }
    // 更新视图,即修改p标签的value
    function updateView (key, val) {
        if (key === 'msg') {
            document.getElementById('value').innerHTML = val
        }
    }
    // 为 input 标签绑定事件
    let input = document.getElementById('input')
    input.addEventListener('change', function () {
        data.msg = this.value
    })
    function defineReactive (obj, key, value) {
        Object.defineProperty(obj, key, {
                get () {
                    return value
                },
                set (newValue) {
                    updateView(key, newValue)
                    value = newValue
                }
            })
        }
    }
    function observer (obj) {
        if (typeof obj !== 'object' || obj == null) return
        for (let key in obj) {
            reactive(obj, key, obj[key])
        }
    }
    observer(data)

    刚好看过,直接写了

    然后聊到事件处理流程,事件委托

    event.target、event.currentTarget 没说清楚

    • event.target返回触发事件的元素
    • event.currentTarget返回绑定事件的元素
  • instanceOf 原理,手动实现 function isInstanceOf (child, Parent)

    链表的遍历,判断是否和 Parent.prototype 相等

  • 如何保持cpu利用率50%

    不懂

  • 进程、线程之间的关系,死锁是什么

    答了线程之间可以资源共享,进程之间能传递消息

  • tcp/ip协议栈、网络模型

  • content-type 有哪些,301、302、307状态码

    303和307是对302的细划分

    • 303: 对于 POST 请求,表示请求已经被处理,客户端应该使用GET方法去请求Location 里的URL
    • 307:对于 POST 请求,表示请求还没有被处理,客户端仍然要使用POST方法去请求Location 里的URL
  • node.js 中 require('xxx') 是从哪里导入的

    • 路径形式
    • 核心模块,如'path',已经编译成二进制文件 (没答出来)
    • 第三方模块,从node_modules中找,找不到就找上层目录,在 package.json 的 main 字段确定入口,没有 main 字段,就默认index.js (没答清楚)
  • 算法题,反转单链表

    思路一下子就能找到,但是没有背过答案,写的过程中临时指针变量,还是思考了好久,最后还是写出来了

  • 判断输出,多重bind,this指向,箭头函数能否当构造函数等

    多重bind,第一次就固定了this指向,后面不会在改变了

总结:一上来就写observer,刚刚好上午看了,进程线程那一部分基本上不怎么了解,可以抽时间学习一下

09.11 虾皮一面

  • 面向对象的三个特征,分别说一下什么意思

    封装、继承、多态,什么特征就不知道了,大致瞎讲了一下

  • 计算机网络相关问题问了很多

  • 前端基础:

    • js:异步,循环i,setTimeout 中输出什么,如何解决(块级作用域,函数作用域)

      ​ 节流和防抖区别、重排和重绘的区别

    • css:inline-block 的 div 之间的空隙,原因及解决

  • 判断输出

    console.log(0 == [])
    console.log([1] == [1])

    涉及到类型转换,可以以参考:https://blog.csdn.net/qq_18369669/article/details/106607233

  • 算法题目,非递归实现二叉树的先序遍历

    function preOrder (node) {
        let stack = [node]
        while (stack.length > 0) {
            let cur = stack.pop()
            stack.push(cur.right)
            stack.push(cur.left)
            console.log(cur.val)
        }
    }
    // 注意要先 push right 和 left

    非递归的先序遍历是比较简单的,很久没写,也是楞了半天才写出来(还好写出来了)

    不过中序、后序遍历的非递归实现,就很难了,应该不会问到吧。。

  • 算法题目,验证有效的括号

    用栈实现,在面试官的指引下,完成了一些边界情况的判断

总结:这次面试还算可以,基本上答出来了。不过面试写代码还是有点害怕的。前端相关的计算机基础需要再多了解一下。

09.12 拼多多 一面

  • 两栏自适应

    以前总结过:https://blog.csdn.net/qq_18369669/article/details/106427531sh

    但是面试官给的DOM结构是

    <div class="container">
        <div class="right"></div>
            <div class="left"></div>
    </div>        

    right 在前,left 在后,

    可以用flex:

      .container {
        display: flex;
      }
      .left {
        width: 200px;
        order: -1;
      }
      .right {
        flex: 1;
      }

    也可以,left 把 margin-top 设为负,移上去:

      .left {
        width: 200px;
        height: 100px;
        margin-top: -100px;
    
      }
      .right {
        height: 100px;
        margin-left: 200px;
      }

    其他的没有想到了,答得不好

  • 算法:3数之和

    只写了个大概,没有去重复也可以

  • 跨域,CORS 预检请求的状态码和使用的 http method

    200、OPTIONS

  • 手写防抖, 处理参数

09.12 拼多多二面

  • 编程:找出一个DOM节点下,所有的与这个DOM节点层级相差4以上的img标签,并为它们添加一个class

    就是一个dfs,

    写出来后面试官问,假如有一个分支层级很深,并且里面没有img标签,岂不是要递归到底,有没有什么办法解决这个问题

    if (child.innerHTML.indexOf('<img') !== -1) dfs(child)

    面试官说我的方法有点hack,还有没有其他办法,我想不出来了

  • 异步相关的判断输出,手写代码

    写一个函数,传入一个数组,数组里面的promise的状态都改变后,处理所有的结果。

    (类似于Promise.all,但是不触发.catch)

  • vue 相关问题

    • computed、match、watch 有什么应该场景
    • vue 优化(答了异步加载组件,keep-alive)
    • webpack 介绍一下
    • 项目中的问题

09.12 百度一面

  • 移动端 1px 问题

  • 尾调用优化

  • localStorage 能跨域吗

  • ES6 module 和 CommonJS module 的区别

  • 判断输出

    var a = {n: 1}
    var b = a;
    a.x = a = {m: 2}
    [1, 2, 3].map(parseInt)
  • vue 中 $nextTick 原理 (之前被美团面试官也问到过这个,没理解清楚)

  • pwa、serverless了解过吗(不了解)

  • 问了一堆小程序,混合开发的问题(完全不懂,没接触过)

09.13 贝壳 一、二、hr面

  • vuex 原理
  • 代码:
    • 去掉字符串前后的空格
    • function reset (arr) {},使得 [1,2,3,4,5,6] => [1,3,5,2,4,6]
  • null 和 undefined 的区别,如何让一个属性变为null(不懂什么意思)
  • 504 如何排查
  • map 和 forEach 的区别
  • 如何触发上拉刷新

09.14 字节三面

  • 代码题(完全不会,思考了一晚上也不会,网上搜到原题,才思考清楚)

    实现一个 Scheduler 类,完成对Promise的并发处理,最多同时执行2个任务

    补全代码:

    class Scheduler {
      constructor () {
      }
      add () {
      }
    }
    
    const timeout = (time) => new Promise(resolve => {
      setTimeout(resolve, time)
    })
    
    const scheduler = new Scheduler()
    
    const addTask = (time, order) => {
      // scheduler.add 传入一个函数,这个函数返回一个Promise
      scheduler.add(() => timeout(time))
        .then(() => console.log(order))
    
    }
    
    addTask(1000, '1')
    addTask(500, '2')
    addTask(300, '3')
    addTask(400, '4')

    使得以上代码的输出顺序是:2、3、1、4

    关键点是这个add方法要返回一个Promise对象,同时还要完成‘并发’控制,看了网上的结题,关键是超过限制的并发添加一个阻塞,而在任务执行完后按顺序解除阻塞。如何实现呢?把阻塞的Promise的resolve,放到一个队列里面,依次执行就可以了

    • async/await 版本

      class Scheduler {
        constructor () {
            this.max = 2
            this.queue = []
            this.count = 0
      
        }
        async add (promiseCreator) {
          // 如果 当前并发数量 超出限制,就要等到有空闲了,才调用 promiseCreator
          if (this.count >= this.max) {
            await new Promise (resolve => {
              this.queue.push(resolve) // 一直要等到 resolve 执行
            })
          }
          this.count++
          let result = await promiseCreator()
          this.count--
          if (this.queue.length > 0) {
            this.queue.shift()() // resolve 在这里才会执行!! 那这里是哪里呢,是前面的任务执行完一个就 resolve 一个
          }
          return result
        }
      }
    • Promise 版本

      class Scheduler {
        constructor () {
            this.max = 2
            this.queue = []
            this.count = 0
      
        }
      
        add(promiseCreator) {
          // 第一个 promise 作用是 增加一个阻塞
          return new Promise (resolve => {
            if (this.count < this.max) {
              // 注意 count ++ 一定要写在这里(同步执行),不能写在异步(.then)里面,表示当前并发数量增加
              this.count++
              resolve()
            } else {
              this.queue.push(resolve)
            }
          })
          .then(() => {
            // 每个任务都要执行,现在开始执行
            return promiseCreator().then(() => {
              // 执行完就 count--,表示当前并发数量减少一个
              this.count-- // 虽然这里不写也没问题,但是为了通过 this.count 能随时获取到当前并发数量,最好还是写上
              // 执行完就要触发新的任务执行,而这里可以把新任务的阻塞取消,也就是阻塞Promise的 resolve 执行掉
              if (this.queue.length > 0) {
                  this.queue.shift()()
                  this.count ++ // 虽然这里不写也没问题,但是为了通过 this.count 能随时获取到当前并发数量,最好还是写上
              }
            })
          })
        }
      }
  • 贪吃蛇是怎么完成碰撞触发的(忘了)

  • 扫描二维码登录的原理

  • 一个官网怎么优化,有哪些性能指标,如何量化

  • tcp 是如何确保有效传输的,拥塞机制

  • 死锁是什么,如何预防

(卒。。)

09.15 京东一面

  • audio 标签的api(项目中用到了,没答好)

  • 各种前端基础

  • 业务场景:秒杀倒计时如何确保准确性

09.16 海康二面

主要是 js、css基础、vue 的使用

  • vue 请求数据放在 created 好还是 mounted 里好

  • vue组件间传值, listeners 了解过吗(不了解)

09.16 快手二面 (挂)

  • 如何实现 pwa
  • 浏览器本地存储,缓存
  • webpack 抽离 chunk 的原理
  • vue keep-alive 的原理
  • 算法:连续最大乘积(没刷过这个题真的不会,当成最大连续和了,改了一下也没改对,用暴力写了)

09.17 富途一面

  • 常规 css、js、http
  • 涉及到 tcp,再次问了拥塞机制(我说不记得了,用的少,面试官说没事)
  • 代码题:深拷贝(有自己引用自己的情况)、跳台阶、验证括号

09.18 虾皮二面

  • js 垃圾回收机制

    标记清除、引用计数

  • tooken 能放在cookie中吗

  • 编码和字符集的区别

  • http2 哪些时候会比http1 慢 (没搜到)

  • 32位系统和64位体统的区别(额外问的问题)

  • 1000 万 个ip,如何做到O(1) 查找

09.18 深信服一面

  • css 继承关系

    有一些属性能继承(如font-size、color),有一些则不能继承(如border)

    font-size 如果 为百分数,继承的是计算后的值

  • 页面资源预加载

  • 调试工具

09.18 字节四面

  • CDN

  • ES6 module、CommonJS module 的区别,循环引用的问题

  • ES5 继承 和 ES6 继承的区别

  • 代码题(难度一般):

    实现 Array.prototype.removeWhere

    跳楼梯

    实现 Eventemitter 类,有on、emit、off 方法

09.20 顺丰一面

  • flex属性
  • for of 可以遍历哪些对象
    • 可遍历map(遍历结果每一项都是键值对数组),object(必须有Symbol.iterator属性),array,set, string等
  • map object 的区别
    • object 一般有原型(除非Object.create(null))
    • object 只能把 String 和 Symbol 作为 key
    • map 有 size
  • no-store 和 no-cache 的区别
    • no-store 完全不允许缓存
    • no-cache 允许代理服务器缓存

09.20 字节五面 (挂)

  • webpack 热修复原理
  • 既然函数式引用类型,为什么 vue 的 data 还是可以用函数
  • node.js 中 事件循环 和 浏览器事件循环的区别
  • node.js 中 多进程如何创建子进程,某个进程被 kill 了,如何重启
  • 静态代码扫描,如何设计
  • 代码题
    • 第K大的数(写了部分排序,讲了小顶堆、大顶堆、类快速排序)

09.22 金山云一面

  • 画 0.5px 的直线
  • xhr 的 readyState
  • CORS 的 Expose-Headers
  • window.open
  • 如何不受到http缓存的限制,每次都更新资源

09.22 京东二面

问贪吃蛇,没问其他的,如果让你的蛇随着鼠标移动你会怎么做(bfs)

09.23 深信服二面

  • css 切换主题
  • 前端切换中英文
  • 长列表优化

09.23 顺丰二面

  • 数组去重,set 和 array、map 和 object 的区别
  • 哪些类型能被扩展操作符...扩展
  • 扫描二维码原理
  • memory cache 如何开启

09.24 好未来正式批 一面、二面、hr面

没问前端基础

  • 进程线程的区别
  • tcp、udp区别、tcp如何有效连接
  • webpack 有哪些阶段
  • vue 原理

09.24 富途二面

  • 智力题
  • 编程题
  • 代码填空题
#面经##校招##前端工程师#
全部评论
牛逼👍🏻
2 回复 分享
发布于 2020-09-28 12:07
感觉楼主的面试题目难度好高,是因为转行的缘故吗。。。
1 回复 分享
发布于 2020-09-28 14:31
太强了老哥 有实习经历吗
点赞 回复 分享
发布于 2020-09-30 03:38
字节五面挂..... 第一次看到字节居然5次技术面..
点赞 回复 分享
发布于 2020-09-30 16:51
楼主是做的什么项目啊?
点赞 回复 分享
发布于 2020-10-08 22:21
楼主,同农学😭蹲一个联系方式
点赞 回复 分享
发布于 2021-12-06 13:41

相关推荐

点赞 评论 收藏
分享
评论
9
70
分享
牛客网
牛客企业服务