# 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,冲!兄弟们,看看帝国破坏龙马龙的坚持,常年夺冠,我们是否也该坚持下去?
#字节跳动##面经##校招##前端工程师#