fe-learning icon indicating copy to clipboard operation
fe-learning copied to clipboard

记一次字节前端笔试

Open metroluffy opened this issue 5 years ago • 0 comments

字节跳动前端面经

其他与个人项目相关的就不说了,直接来看笔试题。

题目

看代码写输出

var result = [];
var a = 3;
var total = 0;
function foo(a) {
  var i = 0;
  // 注意这里的i后置加
  for (; i < 3; i++) {
    result[i] = function() {
      total += i * a;
      console.log(total);
    }
  }
}

foo(1);
result[0](); // 3
result[1](); // 6
result[2](); // 9
  • 如果将 var i = 0 改为 let, 输出是否会变化

    不会,这里 i 是定义在foo函数作用域内的。

注解:

用闭包来解释这题会更好,因为i是定义在foo函数作用域里的,result数组里的函数运行时相当于从foo函数作用域外引用i、a变量,这个时候foo循环已经退出了,i因为后置加的存在在退出循环以后等于3,a因为参数传入为1,所以输出就是3、6、9。如果这里参数不指定a的话,就会直接沿着作用域链往上查找到定义在全局作用域的a = 3,最后结果为9、18、27。如果foo不传入参数,a还是在foo函数作用域,相当于写了一条 var a; // undefined,最后输出三个NaN。

二进制加法

实现一个二进制加法,输入输出均为二进制字符串

function binaryAdd(num1: string, num2: string): string {
  // TODO
}
//Example
binaryAdd('1010', '111') // '10001'

leetcode原题,二进制求和

和链表求和、字符串加法差不多,进位进入第二次运算即可,计算完成如进位有余记得补位

function addBinary(a,b) {
    let ans = '', carry = 0
    let pa = a.length-1;
    let pb = b.length-1;
    while (pa >=0 || pb >= 0) {
        const sum = Number(a[pa] || 0) + Number(b[pb] || 0) + carry
        carry = Math.floor(sum / 2);
        ans = sum % 2 + ans
        pa--;
        pb--;
    }
    if(carry !== 0) ans = '1' + ans
    return  ans
}

实现一个带并发限制的异步队列

实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个。完善代码中Scheduler类,使得以下程序能正确输出

class Scheduler {
  add(promiseCreator) {
    // TODO
  }
  // TODO
}
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(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')
// output: 2 3 1 4
// 一开始,1、2两个任务进入队列
// 500ms时,2完成,输出2,任务3进队
// 800ms时,3完成,输出3,任务4进队
// 1000ms时,1完成,输出1
// 1200ms时,4完成,输出4
// 实现如下
class Scheduler {
  constructor () {
    this.tasks = [] // 任务缓冲队列
    this.runningTask = [] // 任务队列
  }

  // promiseCreator 是一个异步函数,return Promise
  add (promiseCreator) {
    return new Promise((resolve, reject) => {
      promiseCreator.resolve = resolve
      if (this.runningTask.length < 2) {
        this.run(promiseCreator)
      } else {
        this.tasks.push(promiseCreator)
      }
    })
  }

  run (promiseCreator) {
    this.runningTask.push(promiseCreator)
    promiseCreator().then(() => {
      promiseCreator.resolve()
      // 删除运行完的任务
      this.runningTask.splice(this.runningTask.findIndex(promiseCreator), 1)
      if (this.tasks.length > 0) {
        this.run(this.tasks.shift())
      }
    })
  }
}

小结

字节还是很考基础,面了两次下来感觉最大的问题就是这块,平时实践较多但是深度不够,比如第三题那个带并发限制的异步队列,读完题后没有较好的思路。

如果单纯从准备面试的角度,除开基础知识预备,算法题这些,JS这块可以关注一下事件循环、作用域、原型链继承,再就是各种花式异步操作等等。

metroluffy avatar Jun 08 '20 03:06 metroluffy