# 6面字节终过三面
6面字节终过三面
背景:
7月17号暑假短学期后,由于受到导师的不停分派无意义的任务,对研究生不在那么向往。于是放弃考研,开始了秋招提前批的生涯。当时前端已经有4个月没有看,基础已经几乎忘得差不多了。于是打算投字节暑期的实习生,但收到字节HR秋招提前批的邀请,于是抱着单车变摩托的心态参加了字节的提前批面试。
所有的面试题都是精选的,八股文有,但是忘了很多
提前批面试:
一面(7-21):
1.Array.prototype定义的考察
arr = [1,2,3] arr.sum()
解答:这题主要考的是在Array.prototype上定义方法。
Array.prototype.sum(){
let array = this;
let res = 0
for(let i =0;i<array.length;i++){
res+=array[i]
}
return res
}
2.函数和var变量的优先级
alert(a);
a();
var a = 3;
function a (){ alert(10) }
alert(a);
a = 6;
a()
解答:这题主要考虑的是声明函数和var变量的优先级,我们知道函数声明优先于变量声明,所以我们可以得到如下形式的代码(注释为输出)
function a(){alert(10)};
alert(a); //结果是function a(){alert(10)}
a(); //弹框弹出10
var a = 3;
alert(a); //弹出框输出3
a = 6;
a(); //a此时是变量,不是函数,输出是Uncaught TypeError: a is not a function
3.let块作用域和闭包
将下面代码改成正确形式,输出0到9,采用2种方式:
for(var i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
解答:首先,原题的输出是10个10,所以我们要更改。
第一种方式通过let实现,如下:
for(let i = 0; i < 10; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
第二种方式通过闭包,保存变量i实现。如下:
for (var i = 0; i < 10; i++) {
((j) => (setTimeout(() => {
console.log(j);
}, 0)))(i);
}
4.flex=1的含义
解答:flex-grow = 1,flex-shrink = 1, flex-basis = auto.详细的请转移知乎。
5.怪异(IE)模型和标准模型
解答:IE模型的宽/高 = content + padding + border
标准模型的宽/高 = content
6.算法题,翻转不包含字母的字符串
const str = '123abd3-adfz-34-akjkfaf';
function reverseStr(str) {
}
输出:321abd-3adfz-43-akjkfaf
解答:这题不是很常见,但还是秒给答案,兄弟们,算法题不可少刷呀。
function reverseStr(str){
var res =""
var needReserve = ""
function ReserveNotAlp(needReserve){
for(let i=needReserve.length-1;i>=0;i--){
res+=needReserve[i]
}
}
for(let i=0;i<str.length;i++){
if((str[i]>='a'&&str[i]<='z')||(str[i]>='A'&&str[i]<='Z')){//碰到字母不翻转
ReserveNotAlp(needReserve)
res+=str[i]
needReserve=""
}else{
needReserve+=str[i]
}
}
return res;
}
console.log(reverseStr('123abd3-adfz-34-akjkfaf'))
一面总结:凭借着算法功底和5天的面经侥幸过了,一面面试官看起来就玉树临风,风流倜傥。所以放我一马,感激万分。
二面(7/26):
1.Promise的链式调用
new Promise((resolve, reject) => {
reject(1)
}).catch(() => {
console.log(2)
}).then(() => console.log(3), (v) => console.log(v))
解答:该题考察的是Promise的链式调用,了解了catch()是then()的语法糖,所以catch后的then语句是会执行的。以及then()返回的是一个promise。
2 3
2.GUI线程与Js线程互斥问题
function demo() {
const now = Date.now();
document.body.style.backgroundColor = 'red';
while(Date.now() - now <= 2000) { continue; }
document.body.style.backgroundColor = 'blue';
}
解答:当时认为是先变红,然后2s后变成蓝色。面试官提示说,你知道GUI线程和Js线程的互斥吗。正确答案是2s后直接变蓝。
至于为什么,我是这么认为的。Js线程执行到document.body.style.backgroundColor = 'red';,Js线程知道这个是GUI线程该做的事,于是把该任务放到GUI线程中的任务队列里(并未执行),然后Js线程知道到while循环,在这忙等了2s。然后碰到document.body.style.backgroundColor = 'blue';GUI线程把它压到任务队列里(并未执行)。此时Js执行已经完毕,于是GUI线程执行,清空GUI线程的任务,最后一个任务是变蓝,于是是2s后直接变蓝。
3.翻转链表
let node = {
val: 3,
next: {
val: 4,
next: {
val: 5,
next: null,
},
},
};
解答:很容易的算法题,秒给答案
function reserveNode(root){
if(root==null||root.next==null) return root;
let pre =null,cur = root;
while(cur!=null){
let last = cur.next;
cur.next = pre;
pre = cur;
cur = last
}
return pre;
}
4.手写快排,但不能创建新数组
解答:基础题,秒给答案。快排有两种写法,一种是交换比pivot大和pivot小的元素(不创建新数组),一种是把比pivot小的数放进数组里,把比pivot大的数放进数组里。然后连接起来。(创建了数组保存变量),以下给出交换的方案。
function Sort(arr){
function partition(arr,left,right){
let value = arr[left];
let _left = left,_right = right;
while(_left<_right){
while(arr[_right]>=value&&_left<_right){
_right--;
}
while(arr[_left]<=value&&_left<_right){
_left++;
}
[arr[_right],arr[_left]] = [arr[_left],arr[_right]]
}
[arr[left],arr[_left]] = [arr[_left],arr[left]]
return _left;
}
function quickSort(arr,left,right){
if(left>=right) return ;
let pivot = partition(arr,left,right);
quickSort(arr,left,pivot-1);
quickSort(arr,pivot+1,right);
}
quickSort(arr,0,arr.length-1);
return arr;
}
二面总结:算法题出了2道,还是很容易的,直接秒了。但是Promise的链式调用答的不是很好,但面试官看在算法基础扎实的情况下,让我过了。嘻嘻。
三面(7/29):
1.把setTimeout封装成Promise
解答:这题卡住了(嗅大了),一直不知道resolve该放在哪里,后来发猛然发现resolve是一个函数,可以直接放在setTimeout里。
function timeout(delay) {
return new Promise(resolve => setTimeout(resolve, delay));
}
timeout(2000)
.then(() => {
console.log("houdunren.com");
return timeout(2000);
})
.then(value => {
console.log("hdcms.com");
});
2.把fs.readFile的callback回调函数封装成Promise
解答:这题之前看过,所以写出来了。
const fs = require('fs')
function timeout(fileName){
return new Promise((resolve,reject)=>{
fs.readFile(fileName,'utf8',(err,data)=>{
if(err) reject(err)
else resolve(data)
})
})
}
3.把fs中所有的callback回调函数,封装成Promise
解答:之前也看过,但写的时候不知道…arg该放在那里。卡住了,我实在太弱了。以下为正确答案。
const fs = require('fs')
const promisify = function(callBack) {
return function(...args) { //...args应该放在这里,返回一个函数,拿到函数的参数。
return new Promise((resolve, reject) => {
callBack(...args, (err, data) => {
if (err) {
reject(err)
return;
}
resolve(data)
})
})
}
}
const fsReadFile = promisify(fs.readFile);
fsReadFile('./test.txt', 'utf-8').then((data) => console.log(data)).catch((err) => reject(err));
4.懂Vue吗?手写一下双向绑定的实现。
解答:因为之前表现的很拉胯,所以面试官现在只是调侃一下,已经意兴阑珊了。但最关键的我写不来。我只看过源码,但还没时间过。大致思想是劫持getter+setter和订阅者观察者模式+Compile的实现,然后写了10行代码就装不下去了。
5.你有什么想要问我的吗?
解答:
Q1:面试官,这次为什么没有问算法题? 面试官说:你之前的面评说你的算法基础不错,我就不问了哈。[悲伤哭泣]
Q2:感觉自己凉了,于是说道面试官如果挂了,可以帮我转实习生的面试吗,能否省去几场呀? 面试官说:好的,我帮你安排一下。
[果不其然,面完就收到HR电话我什么时候有空去实习,因为当时收到一个实习offer,所以脑残的说等这个实习实习完就去,然后就没消息了,悲伤!]
三面总结:三面面试官不错,竟然还能让我去实习。哈哈开心,无奈自己的水平太拉胯。因为也就看了10天的前端,所以运气还是很不错的,哇咔咔。
实习生面试:
一面(8/2):
1.两栏布局,左边固定100px,右边占满剩余空间
解答:我想到两种方案实现。一个是flex:1实现,一个是浮动布局,面试官说不要用flex布局。兄弟们。二栏布局,三栏布局(圣光杯布局和双飞翼布局)还是要熟练哈。
<div>
<div class="left">左边</div>
<div class="right">右边</div>
</div>
.left{
width:100px;
float:left;
}
.right{
margin-left:100px;
width:100%
}
*2.实现一个Promise.all方法 *
解答:了解了Promise.all的意思就可迎刃而解了。但是面试官说我的代码没有考虑Promise的异步,无法得到有序的Promise。
Promise.all = function(promises) {
let res = [];
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
Promise.resolve(promises[i]).then((data)=>{
res.push(data)
if(res.length==promises.length){
resolve(res)
}
}).catch((err)=>{
reject(err)
})
}
})
}
Warning:面试官说我的代码没有考虑Promise的异步,无法得到有序的Promise。我不知道怎么改,希望有大神能指出,感激不惊
3.eventEmitter 实现,on,once,off,emit
解答:这题我蒙了,我没复习到eventEmiiter的实现,但是之前写过发布-订阅者模式,所以写的坑坑洼洼,写的一般般。这里贴网上的代码。
class EventEmitter {
constructor() {
this._events = {}
}
on(event, cb) {
if (!this._events[event]) {
this._events[event] = [];
}
this._events[event].push(cb);
return this;
}
emit(event, ...args) {
let arr = this._events[event];
for (let cb of arr) {
cb(...args);
}
return this;
}
off(event, cb) {
if (!cb) {
return;
}
if (this._events[event]) {
this._events[event] = this._events[event].filter((cbitem) => {
return cb !== cbitem;
})
}
return this;
}
once(event, cb) {
let fn = function(...args) {
cb.apply(this, ...args);
this.off(event, fn);
}
fn = fn.bind(this)
this.on(event, fn);
return this;
}
}
*4.算法题:判断最大岛屿,返回最大岛屿的面积。 *
输入:
[ [0,1,0,0,0], [0,1,0,1,0], [1,1,0,0,0], [0,1,0,0,0], ]
结果:5。
解答:秒给思路+秒出结果。dfs,把访问的地方做个标记下次不访问即可。然后边界判断一下
function getLargestIsland(arr){
let temp =0;
if(arr.length==0) return 0;
function dfs(arr,i,j){
if(i<0||i>=arr.length||j<0||j>=arr[0].length||arr[i][j]==0||arr[i][j]==2) return
arr[i][j]=2;
temp++;
dfs(arr,i-1,j);
dfs(arr,i+1,j);
dfs(arr,i,j+1);
dfs(arr,i,j-1);
}
let maxIsland = 0;
for(let i=0;i<arr.length;i++){
for(let j=0;j<arr[i].length;j++){
if(arr[i][j]===0||arr[i][j]===2) continue;
temp=0;
dfs(arr,i,j);
maxIsland = maxIsland>temp? maxIsland:temp;
}
}
return maxIsland;
}
彩蛋:面试官说这是提前批面试,所以不能给过哈,但实习的话,这一面给过。哈哈,我立马转实习。
一面总结:算法题很容易,eventEmitter写的不是很好,其余的八股文答的都很好。嘻嘻,士别三日当刮目相待。
二面(8/3):
1.js中call和apply有什么区别?
解答:两个都是改变this指向。call后跟的参数是一个一个跟的(一般用于重写Array的map,reduce,filter的实现会用到),apply跟的是数组。面试官说还有,我面试结束后查了一下,知乎上显示的就这一个区别,希望有大佬能不吝赐教。小弟感激不尽。
2.算法题:在二叉树中找到两个节点的最近公共祖先
解答:秒给思路和立马写出。代码如下:
function lowestCommonAncestor( root , o1 , o2 ) {
if(root.val ==o1 || root.val ==o2 || root==null) return null;
let left = lowestCommonAncestor(root.left,o1,o2);
let right = lowestCommonAncestor(root.right,o1,o2);
if(left==null && right==null) return null;//o1,o2都不在树里面,特例
if(left==null) return right;
if(right==null) return left;
return root;
}
3.脑筋急转弯:A和B轮流掷硬币,投到正面就赢。A先投,B在投。问A赢的概率
解答:当时无脑说是1/2,面试官打趣道要是1/2的话,就不是面试了哈。哈哈,然后想了5分钟,列出如下等式,结果为2/3。
4.脑筋急转弯:1000瓶药水,1瓶有毒药,服用后一小时毒发,毒药可以无限稀释,那么一小时内用几只小白鼠能够找出毒药?
解答:面试官说这题不该出,有些公众号都给了答案,诶。我符合到,是的。面试官,你关注的公众号我没关注,不如,我们换一道题把。面试官笑道,那就这题吧。[伤心],想了10分钟,面试官一直提示,我还是不会。
5.看题输出:var的变量提升
var a = 10;
(function () {
console.log(a)
a = 5
console.log(window.a)
var a = 20;
console.log(a)
})()
var b = {
a,
c: b
}
console.log(b.c);
解答:知道var变量的提升和匿名函数this指向,该题就很容易了。注意c:b的时候中的b是undefined,因为var b的变量提升。我一开始就答成了循环引用,后来才反映过来。
undefined 10 20 undefined
二面总结:面试官人不错,好像很想让我进去当实习生的,哇哈哈。基本都答上了,除了1000瓶药水的题。
三面(8/6):
1.手写bind
解答:蛮容易的,注意返回的是个函数,以及第一个参数是要绑定的对象,不能取。
Function.prototype.myBind = function(thisArg) {
if (typeof this !== 'function') {
return;
}
var _self = this;
var args = Array.prototype.slice.call(arguments, 1) //从第二个参数截取
return function() {
return _self.apply(thisArg, args.concat(Array.prototype.slice.call(arguments))); // 注意参数的处理
}
}
2.算法题:给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
解答:秒出结果。
function isValidKuoHao(str){
if(str.length%2!=0) return false;//如果是奇数就不可能是合法的字符串
let stack = [];
let map = new Map();
map.set('(',')');
map.set('[',']');
map.set('{','}');
for(let i=0;i<str.length;i++){
if(map.has(str[i])){
stack.push(str[i])
}else{
let target = stack.pop();
if(str[i]!=map.get(target)) return false;
}
}
return true;
}
console.log(isValidKuoHao("()"));
console.log(isValidKuoHao("()[]{}"));
console.log(isValidKuoHao("(]"));
console.log(isValidKuoHao("([)]"));
console.log(isValidKuoHao("{[]}"));
3.JS实现一个带并发限制的异步调度器Scheduler,最多两个任务。
解答:蛮简单的,面经上好像有,当时写的时候除了点bug。但问题不大。
class Scheduler {
constructor() {
this.needRunTasks = []
this.runTasks = []
}
add(prmoiseFn) {
return new Promise((resolve, reject) => {
prmoiseFn.resolve = resolve; //保存Promise状态,现在不能执行
if (this.runTasks.length < 2) {
this.run(prmoiseFn)
} else {
this.needRunTasks.push(prmoiseFn)
}
})
}
run(prmoiseFn) {
this.runTasks.push(prmoiseFn)
prmoiseFn().then(() => {
prmoiseFn.resolve()
this.runTasks.splice(this.runTasks.indexOf(prmoiseFn), 1) //移除执行后的任务
if (this.needRunTasks.length > 0) {
this.run(this.needRunTasks.shift())
}
})
}
}
const timeout = (time) => new Promise(resolve => setTimeout(resolve, time))
const scheduler = new Scheduler()
const addTask = (time, order) => scheduler.add(() => timeout(time)).then(() => console.log(order))
addTask(400, 4)
addTask(200, 2)
addTask(300, 3)
addTask(100, 1)
4.脑筋急转弯:红 黄 蓝13 15 17.其中任意两种颜色的一个球可以转换为第三种颜色的球2个。例如1个红和1个蓝可以转为2个黄。红:12 黄:14 蓝:19。问三种颜色的球满足什么关系时,通过转换,最后只有一种颜色的球。
解答:秘技:无论是做算法题还是这些题。如果给的例子很难,推都要花很长时间的话。那么千万不要用例子,自己举一些简单的例子,然后慢慢推规律,慢慢难起来。
我举了 [1,1,1], [2,1,2]的例子,就开始推导了。
最后的情况: [1,1,n]
倒数第二种情况: [2,2,n-2],[3,0,n-1],[0,3,n-1] //[0,3,n-1]无法继续下推
倒数第三种情况(只考虑[2,2,n-2]): [4,1,n-3],[1,4,n-3],[3,3,n-4];
所以发现:当三种颜色有任意两种颜色一样的那么可以最后只剩一种颜色的球,或是存在任意两种颜色球的绝对值之差为3时。
当时问面试官是否对时,面试官说对的。我脱口而出,我实在是太聪明了,然后面试官尬笑表示尴尬。哈哈。
彩蛋:当时面完,由于之前有一家大公司催我去实习,我拿到了offer.所以问面试官能否今早告诉我消息时,面试官说,我这没问题。到时审批需要到周三下offer,我帮你催HR吧。开心,嘻嘻,超激动!
三面总结:蛮容易的,哈哈。面试官三面的面试官说话语气很好相处,很和蔼。嘻嘻,开心。
总结:
6面字节终过!振奋!
学习技巧:总共花了15天准备面经。每天8小时左右学习。上午2小时看面经,下午3小时看面经。晚上刷3小时算法题。
面经一定要看懂,要找知乎或者那些赞很多的博客或者MD5官方文档等,不然,写的人都是模模糊糊的,你肯定也是模模糊糊的。
每次看面经的时候,像面试官一样问自己,满意吗?如果不满意的话,重新叙述,直到能几乎覆盖所有的博客/知乎的知识点才算完成。
那些场景题(手写map,reduce,异步调度,promise封装) 理解后抄一遍,然后边看边默,最后默写出来。
算法题:1天20道,把牛客的研发爱考题,每题刷2~4遍。做到一眼扫过去,代码在脑海自动浮现。
最后,希望大家能拿到自己满意的offer,冲!兄弟们,看看帝国破坏龙马龙的坚持,常年夺冠,我们是否也该坚持下去?
#字节跳动##面经##校招##前端工程师#
查看13道真题和解析