农学类专业转行做【前端】的秋招历程
本硕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 富途二面
- 智力题
- 编程题
- 代码填空题