# 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,冲!兄弟们,看看帝国破坏龙马龙的坚持,常年夺冠,我们是否也该坚持下去?

#字节跳动##面经##校招##前端工程师#
全部评论
Promise.all想得到有序的promise输出的话,可以用async await去进行包裹哦
2 回复 分享
发布于 2021-08-09 10:51
做到一眼扫过去,代码在脑海自动浮现。这是啥境界啊
2 回复 分享
发布于 2021-08-13 22:32
很强,希望我也顺利😮
1 回复 分享
发布于 2021-08-09 20:01
flex:1 不是 1 1 0% 嘛
1 回复 分享
发布于 2021-08-10 23:46
我去三面我们可能是同一个面试官,算法题一模一样
1 回复 分享
发布于 2021-08-12 01:43
括号题最后是不是应该判断是否全部出栈 否则会出错🤔
1 回复 分享
发布于 2021-08-12 03:22
最近公共祖先那道题 第二行应该是 if(root.val ==o1 || root.val ==o2 || root==null) return root; 嘛
1 回复 分享
发布于 2021-08-14 16:33
Prmoise.all最后resolve得到的是一个数组结果,只需要根据下标与传入的Promise对应一下就可以了,push的话得到的就只是所有Promise最后的执行顺序了
3 回复 分享
发布于 2021-08-09 11:59
lz面的是哪个部门呀
点赞 回复 分享
发布于 2021-08-08 18:44
所以实习面试过了拿的是实习offer还是校招的?
点赞 回复 分享
发布于 2021-08-08 20:10
实习没有hr面吗
点赞 回复 分享
发布于 2021-08-09 14:07
太强了
点赞 回复 分享
发布于 2021-08-09 15:10
楼主这是面完提前批再去面的实习吗
点赞 回复 分享
发布于 2021-08-09 16:47
大佬,我问个问题,如果面试算法用牛客系统,需要自己写输入输出,链表和树的题,怎么快速构建出树或链表来调试程序,测试用例
点赞 回复 分享
发布于 2021-08-09 22:53
楼主什么时候入职丫?
点赞 回复 分享
发布于 2021-08-11 16:25
怎么这么多js手写题
点赞 回复 分享
发布于 2021-08-11 22:43
Vue双向数据绑定手写要写成什么程度啊?Watcher Dep 这种都得写出来吗?
点赞 回复 分享
发布于 2021-08-12 00:34
大佬太强了😔
点赞 回复 分享
发布于 2021-08-15 22:53

相关推荐

26 140 评论
分享
牛客网
牛客企业服务