本人19年年末来到百度实习(已离职),作为第一份工作,带给我的收获蛮大的。几个月的实习生活让我了解到了公司的开发流程、代码规范、团队沟通的能力等等,在这里先感谢老东家。下面是春招总结:
面试情况
- 阿里巴巴(淘系技术部) - 意向书
- 字节跳动(懂车帝) - offer
- 美团(到店餐饮) - HR面结束
- 网易雷火 - 未开始流程
- 快手 - 未开始流程
面试记录
阿里巴巴
阿里巴巴是通过师兄内推投递的,在系统开之前提前面了一轮,一共四轮技术面(含一轮交叉面)+ 一轮HR面。还算快,一个月左右走完流程。内推的好处就是如果流程卡住,内推人可以帮忙催一催面试官尽早安排面试。
一面、二面(50min)
因为最开始没有做面试记录,凭靠记忆想到了下面几个题
promise的原理,catch之后能调用then方法吗?- 略,可以。
let promise = new Promise((resolve, reject) => {
reject(1);
});
// catch与then方法相同,都会返回一个promise对象,存在显式返回和隐式返回两种情况
// 1.隐式返回:返回一个resolve值为undefined的promise
promise.catch(err => {
console.log(err); // 1
}).then(resolve => {
console.log(resolve); // undefined
});
// 2.显式返回一个promise
promise.catch(err => {
return new Promise(resolve => {
console.log(err); // 1
resolve(2);
});
}).then(resolve => {
console.log(resolve); // 2
});
- cookie有哪些属性?知道SameSite吗?
Domain、Path、Max-Age、Secure、HttpOnlySameSite:把三个值Strict、Lax、None的作用讲一下,chrome80版本把默认值None改为Lax造成什么影响。推荐阮一峰老师的文章:Cookie 的 SameSite 属性
- 浏览器事件循环和Node事件循环的区别
- node11以下版本与浏览器
事件循环的区别
- node11以下版本与浏览器
- 讲一下React新生命周期(16.3之后版本)
- 旧版生命周期

- 新版生命周期

- 废弃了
componentWillMount、componentWillReceiveProps、componentWillUpdate。React17将推出新的异步渲染方式,dom挂载之前将可以被打断,因此上述三个生命周期不能保证在需要执行的时候只执行一次 - 新增
getDerivedStateFromProps、getSnapshotBeforeUpdat
- 旧版生命周期
- 优化首屏加载,讲一下代码分割
- 使用
suspense配合lazy封装动态引入模块组件
- 使用
const myComponent = lazy(
() => import(
/* webpackChunkName: "index" */
'./index'
);
);
const myAsyncComponent = props => (
<Suspense fallback={<Loading />}>
<myComponent {...props} />
</Suspense>
);
- 讲一下React错误边界
- 错误边界是一种 React 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,并且,它会渲染出备用 UI,而不是渲染那些崩溃了的子组件树。错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。 ——react16开发文档
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
// 如果捕捉到子组件错误,渲染回退页ErrorComponent
if (this.state.hasError) {
return <ErrorComponent />;
}
// 如果未捕捉到子组件错误,渲染子组件
return this.props.children;
}
}
// 用法
<ErrorBoundary>
<Child1 />
<Child2 />
...
</ErrorBoundary>
- 项目中有一个场景如何实现的(点击地图上一个县,找到所在的省、市)
// 数据结构如下,点击一个地址获取到该地址的id,通过广度遍历去找到它所在的路径
const data = [{
id: '1',
name: '四川省',
children: [
{
id: '2',
name: '阿坝藏族自治州',
children: [
{
id: '3',
name: '汶川县'
}
]
}
]
}];
// 当时考虑到提高点击一级、二级地点(省、市)的遍历速度。实际在这里两种遍历差别不大。
function bfs(target, id) {
const stack = [...target];
while (stack.length) {
const current = stack.shift(); // 深度遍历的话换成pop
if (!current.path) {
current.path = new Array(current.id);
}
if (current.children) {
stack.push(...current.children.map(
item => {
return {
...item,
path: current.path.concat(new Array(item.id))
}
}
));
}
if (current.id === id) {
return current.path;
}
}
return undefined;
}
console.log(bfs(data, '3')); // [ '1', '2', '3' ]
三面、四面(50min)
三面四面都是围绕着个人项目和实习项目去问的,参考意义不大。到三面其实就是boss面了,主要看参加过的比赛和项目,如果参加过含金量比较高的比赛或者做过几个完整的项目(即使是一个完整的项目也比多个零散的拼凑项目强)会比较占优势。
HR面(30min)
HR在一个晚上突然打电话过来,直接进行了面试。问了专业相关的知识、如何学习前端、微信小程序设计大赛的收获、自己的优点、手里的offer(当时手里有个字节offer,就问了优先选择哪个,为什么)。面下来很快,大概半小时就结束了,没有感觉到HR很凶(网传阿里HR比较凶)。
字节跳动
周日上午一面,结束后面试官打电话约下午二面,晚上又接到电话约了HR面,整体流程非常快。
一面、二面 (1h)
- 左侧200px,右侧自适应,两列等高(高不写死)
<div class="container">
<div class="left">
111
</div>
<div class="right">
11111<br/>
11111
</div>
</div>
<!--1. flex布局,比较常用-->
.container {
display: flex;
}
.left {
width: 200px;
background-color: blue;
}
.right {
flex: 1;
background-color: red;
}
<!--2. 定位加calc-->
.container {
position: relative;
width: 100%;
}
.left {
position: absolute;
width: 200px;
height: 100%;
background-color: blue;
}
.right {
width: calc(100% - 200px);
margin-left: 200px;
background-color: red;
}
<!--3. table布局加calc-->
.container {
display: table;
width: 100%;
}
.left {
display:table-cell;
width: 200px;
background-color: blue;
}
.right {
display:table-cell;
width: calc(100% - 200px);
background-color: red;
}
- 实现一个秒针动画
- 可以用
animation去做
- 可以用
- span {padding: 12px 24px;},span标签上下左右内边距是多少
- 上下:0、左右:24px
- IFC相关
- 怎么获取元素距离页面左上角距离top、left
offsetTop、offsetLeft
- [] == false、{} == false
- ==隐式转化
- ([]).toString() = ""(空字符串)
- ({}).toString() = "[object Object]"
Object.prototype.toString.call(),判断引用数据类型
- 页面渲染流程,重绘、重排
- [1, '2', [3, 4]]转化为[1, '2', 3, 4]
// 如果数组里全是字符串,可以用下面这个方法,在这里显然不行
arr.toString().split(',');
// 1 遍历
function bfs(arr) {
let arr2 = [];
let stack = [...arr];
while(stack.length) {
let item = stack.shift();
item instanceof Array ? stack.push(...item) : arr2.push(item);
}
return [...new Set(arr2)];
}
console.log(bfs(arr));
// 2 递归
function foo(arr, result = []) {
if (!(arr instanceof Array)) {
result.push(arr);
return;
}
arr.forEach(item => {
foo(item, result);
});
return result;
}
console.log(foo(arr));
- 自定义Hooks,useInView(页面隐藏再显示执行方法)
const useInView = function(fn) {
useEffect(() => {
const innerFn = function() {
!document.hidden && fn();
}
// 绑定事件
document.addEventListener('visibilitychange', innerFn);
return (() => {
// 卸载时注销事件
document.removeEventListener('visibilitychange', innerFn);
});
});
}
function MyComponent() {
useInView(() => {
console.log(1111);
});
useInView(() => {
console.log(2222);
});
return (
<div></div>
);
}
- 找到第一个不重复字符,如 'abadc' -> 'b','stEDeSS' -> 't',无不重复时返回空字符
- 实现promise.all
- react实现一个modal组件,支持打开和关闭功能,且modal内部的内容可以自定义
- 使用hooks配合
props.children
- 使用hooks配合
- 给定有序数组array和数字n,找出n在array中起始位置的下标和终止位置的下标。如 array = [1, 1, 2, 2, 3], n = 2; 返回[2, 3]
- 可以把n出现的下标放到数组里,返回第一项和最后一项,显然不是面试官想要的答案,面试官想让用LRU实现
- 浏览器跨域的解决方法,cookie、localstorage、sessionstorage能否跨域,cookie如果超过存储上限会怎样
- 下面代码输出
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function () {
console.log('setTimeout');
}, 0);
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
console.log('script end');
/**
* script start
* async1 start
* async2
* promise1
* script end
* promise2
* async1 end
* setTimeout
*/
- 下面代码在Node环境下和浏览器环境下输出
console.log('start');
setTimeout(() => {
console.log('children2');
Promise.resolve().then(() => {console.log('children2-1')});
}, 0);
setTimeout(() => {
console.log('children3')
Promise.resolve().then(() => {console.log('children3-1')});
}, 0);
Promise.resolve().then(() => {console.log('children1')});
console.log('end');
// node11以下
/**
* start
* end
* children1
* children2
* children3
* children2-1
* children3-1
*/
// 浏览器及node11以上
/**
* start
* end
* children1
* children2
* children2-1
* children3
* children3-1
*/
- 实现一个new方法
function _new(obj, ...args) {
let newObj = {};
newObj.__proto__ = obj.prototype;
let ret = obj.call(newObj, ...args);
return typeof ret === 'object' ? ret : newObj;
}
HR面(15min)
纯聊天,结束后没有口头offer,上午面完的,下午HR加微信说面试通过,次日中午收到offer邮件。字节跳动的面试效率可以说是非常高了。
美团
美团基本上隔两天约一轮面试,三轮技术+一轮HR。
一面 (70min)
- 数据库中的死锁
- 数据库中的范式
- 操作系统进程的状态
- 执行、阻塞、就绪
- 自定义hooks实现componentDidUpdate(初始化时不执行)
const useMyDidUpdate = function(fn) {
const status = useRef(false);
useEffect(() => {
// 初始化时不执行
if (!status.current) {
status.current = true;
return;
}
fn();
});
}
- https的传输过程
- 通过非对称加密传送对称加密的密钥
- 拿到密钥后通过对称加密进行数据交换
- CA证书防止中间人攻击。
- 推荐微信开放社区的一篇文章一次安全可靠的通信——HTTPS原理
- 实现垂直居中
<div class="container">
<div class="item"></div>
</div>
<!--1.flex布局-->
.container {
display: flex;
justify-content: center;
align-items: center;
}
.item {
}
.container {
display: flex;
}
.item {
margin:auto;
}
<!--2.absolute定位-->
.container {
position: relative;
}
.item {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
<!--3.grid布局-->
.container {
display: grid;
}
.item {
justify-self: center;
align-self: center;
}
<!--4.table布局-->
.container {
display: table;
}
.item {
display: table-cell;
vertical-align: middle;
text-align: center;
}
二面(50min)
- git相关
- git rebase 和 git merge的区别
- git回滚到任意版本
- map和weakMap的区别
- 从能否遍历和成员是否弱引用的角度去答
- 实现一个抽奖功能
- 参与者放到数组里,生成一个随机的索引(概率相等),获取对应值
- Math.random()左闭右开集合,概率不相等
- Math.floor(Math.random()*(max-min+1)+min)在[min, max]概率相等
- 实现发布订阅模式
let event = {
// 存放订阅事件
childrenList: {},
// 订阅函数
listen(type, fn) {
//如果chilidrenlist里这个缓存不存在,就先将它创建为空,为后续做准备
!this.childrenList[type] && (this.childrenList[type] = []);
// 判断传进来的是否是一个函数,若是就加到childrenList[type]下的数组中等待执行
typeof fn == 'function' && this.childrenList[type].push(fn);
// console.log(this.childrenList)
},
// 发布函数
touch(type) {
let fns = this.childrenList[type];
if (!fns && fns === 0) {
return false
}
fns.forEach(fn => {
fn.apply(this, [arguments]);
});
}
}
/**
* 创建一个订阅小红
*/
event.listen('小红', arguments => {
console.log(`${arguments[0]},${arguments[1]}`)
});
event.listen('小红', arguments => {
console.log(`大家注意,我们班的${arguments[0]}。${arguments[1]}`)
});
/**
* 创建一个订阅小明
*/
event.listen('小明', arguments => {
console.log(`${arguments[0]},${arguments[1]}`)
});
/**
* 向小红订阅事件发布消息
*/
event.touch('小红', '这次考试英语成绩年纪第一');
/**
* 向小明订阅事件发布消息
*/
event.touch('小明', '小明不出你的意料,你数学成绩还是倒数第一');
- 实现快排
let arrA = [10, 11, 12, 13, 9, 8, 7,14];
function quickSort(arr) {
// 递归的终止条件
if (arr.length <= 1) {
return arr;
}
let begin = arr[0];
// 左指针
let i = 1;
// 右指针
let j = arr.length - 1;
while (i < j) {
while (arr[i] < begin && i < j) {
i ++;
}
while (arr[j] > begin && i < j) {
j --;
}
// 交换两个数的位置
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
let left, right;
if(arr[i] < begin){
left = arr.slice(1, i+1) ;
right = arr.slice(i+1, arr.length);
}
if(arr[i] >= begin){
left = arr.slice(1, i) ;
right = arr.slice(i, arr.length);
}
return [...quickSort(left), begin, ...quickSort(right)];
}
console.log(quickSort(arrA));
三面 (50min)
- 前端工程化
- 发布订阅模式和观察者模式的区别
- 项目相关
HR面 (45min)
和上面两轮HR面一样,就是聊聊天。
最后
以上就是本人春招面试的记录,由于流程比较长,上面这些都是印象比较深的题目。感觉阿里的面试会稍微难一些,问的会比较有深度。字节跳动和美团偏向于代码能力,这两个全程都是用牛客面试,美团一面的时候还侧重考察了专业课水平。希望大家都可以早日拿到满意的offer。