一篇字节跳动前端面经

hr小姐姐说一共有1轮笔试 + 3轮技术面 + 1轮hr面,面试地点在中关村天使大厦,岗位是1-3年前端

笔试

笔试分为多选 简答 判断 手写代码四部分,下面只写了印象比较深的几道。css

多选

一、position为relative的元素可使用top和left进行定位吗
答:能够。
我本身没见过这种写法,就没敢选,而后错。前端

二、如下哪一个是加密算法
答:RES、DES。
md5不算加密算法。vue

简答

这部分题目是给出代码,让你写输出
一、nginx

setTimeout(() => {console.log(1)})
const promise = new Promise(resolve => {
setTimeout(() => {console.log(2)})
  resolve()
})
promise.then(() => {console.log(3)})

答:312。
考察macro/micro task面试

二、算法

for(var i = 1; i < 3; i++) {
  setTimeout(() => {console.log(i)})
}

答:3 3
考察异步,这个题简直是必考题
变种:json

for(let i = 1; i< 3; i++) {
  setTimeout(() => {console.log(i)})
}

答:1 2
用let的话就会每轮循环都是一个崭新的isegmentfault

三、api

function A() {
  this.a = 'hi'
  console.log(this.a)
}
A.prototype.a = 'hello'
const a = new A()
console.log(a.a)

答:hi hi
考察原型链,A.prototype.a = 'hello',修改的是a原型上的a属性,与a自己的a属性无瓜。
浏览器运行截图简答第三题跨域

四、

[] == false

答:true
考察类型转换,双等运算两边先转换为Number

五、

[1,2,3].push(4)

答:4
考察经常使用函数返回值, 数组的push和unshift都返回最新数组的长度

判断

判断就5道题,挺简单的,没啥印象

手写代码

手写一个节流函数,这个网上一搜一大把就不说了

一面

笔试写了大概30-40分钟,一面的面试官就来了,看答题状况的时候顺便要求介绍一下本身,而后针对题目作了一些讲解,而后开始问问题。
一、再手写一个防抖,我写了一个第一次触发事件不会调用回调的,面试官又问若是但愿首次也会调用怎么写,代码以下

var debounce = function(fn, delayTime, immediate) {
  var timeId;
  return function() {
    var context = this, args = arguments;
    if(immediate) {
      var callNow = !timeId;
      if(callNow) {
        fn.apply(context, args);
      }
    }
    timeId && clearTimeout(timeId);
    timeId = setTimeout(function() {
      fn.apply(context, args);
    }, delayTime)
  }
}

而后还聊了一下时间戳和定时器的方式实现节流的不一样,须要注意箭头函数是不可使用arguments对象的,因此返回的函数必需要写成return function() {}

二、有什么实现深拷贝的方法吗
      我一开始觉得他说api,就回答JSON.parse(JSON.stringfy())和MessageChannel,他问有什么问题吗。我说不能解决复制函数和环的问题。他又问那你能本身实现一个吗,继续手写代码

function isObject(obj) {
  return obj !== null && typeof obj === 'object'
}
function cloneDeep(obj) {
  let result = {}
  const keys = Object.keys(obj);
  for(let i = 0, len = keys.length; i < len; i++) {
    if(isObject(obj[keys[i]])){
      result[keys[i]] = cloneDeep(obj[keys[i]])
    } else {
      result[keys[i]] = obj[keys[i]]
    }
  }
  return resultset
}

      写完以后他又问我应该如何判断一个变量是数组,答Array.isArray()和Object.prototype.toString.call(arr) === '[object Array]',回来反思发现多是写深拷贝的时候忘记了数组的状况,而后他才问的判断数组。

三、如何用css画一个三角形
答:heigh: 0; width: 0; border: 100px, solid, transparent; border-bottom: 100px, solid, yellow;

四、怎么实现垂直居中
答:position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); 还有flex;

五、简单说一下前端优化策略
答:减小请求,他:具体应该怎么减小,我:好比图片懒加载,配置svg-sprite-loader打包一张svg图面,而后就是减小dom操做,减小浏览器回流重绘次数,减小做用域链的查找,减小对象的深度查找。他:还有吗。我:暂时想不起其它了
优化涉及的东西太多了,之后再单独总结吧。

六、new一个对象的时候发生了什么
这个问题是讲解笔试简答第三题时候问的
正确答案:1.建立一个空对象; 2.设置建立对象的__proto__属性的值为构造函数的prototype属性的值; 3.将第一步建立的空对象做为this的上下文,执行构造函数代码; 若是构造函数没有返回对象,那么返回this

七、看你简历上写最近在看vue源码,那你知道nextTick咋实现的吗
答:2.6的版本是promise,mutationObserver,setTimeout,setImmediate

      至此面试官说一面差很少就到这里,算法啥的留给二面吧,他给个人一面评价是知识广度不够(由于笔试错了比较多),可是感受人比较有灵性,能够进入二面,而后就去叫下一个boss了。

二面

      二面面试官看起来比前一个要严厉好多,觉得要问一些算法题,结果"一面反馈基础不够扎实,那我就再问一点" "GG"
一、import和require的区别
答:import输出引用,require输出拷贝。他:还有吗。 我:不知道了。他:还有require是运行时加载,import是编译时输出接口。

二、说一下浏览器的事件传播机制
答:不知道
正确答案: 事件传播分为三个阶段:捕获,目标对象,冒泡。其中捕获是事件对象从window派发到目标对象父级的过程;目标阶段是事件对象派发到目标元素时的阶段,若是事件类型指示不冒泡,那事件传播在此阶段终止;冒泡和捕获相反,是以目标对象父级到window的过程。

三、手写一个bind
答:不知道。
平时用的都是call和apply来改this,bind只用过一两次,手写call也看过,可是我连bind的参数是啥都没印象,写个锤子。
正确答案:

///使用call
Function.prototype.cvBind = function() {
  var self = this
  var context = [].shift.call(arguments)
  var args = Array.from(arguments)
  return function() {
    return self.call(context, ...args)
  }
}
// 不用call
Function.prototype.cvBind = function() {
  var context = [].shift.call(arguments)
  context.fn = this
  var args = []
  var argument = [].slice.call(arguments, 0)
  for(var i = 0, len = argument.length; i < len; i++) {
    args.push('argument[' + i + ']')
  }
  return function() {
    var result = eval('context.fn(' + args + ')')
    delete context.fn
    return result
  }
}
// 测试
var obj = {
  a: 'local',
  log: function(x, y) {
    console.log(this.a, x, y)
  }
}
var a = 'window'
obj.log('arg1', 'arg2')
var func = obj.log.cvBind(window, 'arg1', 'arg2')
func()

须要注意不用call的版本须要拷贝一次arguments,否则return的函数中args数组里都是undefined,上面的代码不考虑参数是引用类型变量。

2019.10.29更新
由于return的是一个function(){}而不是箭头函数,因此存在本身的arguments而不能使用闭包中的arguments,这里拷贝一遍是能够的能够,可是也能够返回一个箭头函数来直接使用父做用域中的arguments

四、写一个继承
答:不知道
正确答案:
4.1类式继承,经过构造函数实现继承

//父类
function Parent(name) {
  this.name = name || 'parent'
}
Parent.prototype.say = function() {
  return this.name
}
//子类
function Child() {}

4.1.1 父类对象继承

Child.prototype = new Parent('child')

var child = new Child()
child.say()

这种继承方式,子类继承父类自身属性和父类原型上的属性,可是缺点在于初始化父类对象指给子类原型时,并不能肯定父类构造函数的初始化参数。
4.1.2 改造子类构造函数

function Child() {
  Parent.apply(this, arguments)
}

弟中弟方法,只能继承父类自身方法
4.1.3 共享原型

Child.prototype = Parent.prototype

弟中弟中弟,共享一个原型,子类修改会影响父类(然而面试的时候脑子里浮现的就是这种)
4.1.4 临时构造函数

function inherit(Child, Parent) {
  var F = function() {}
  F.prototype = Parent.protoType
  Child.protoype = new F()
}

利用一个空函数F()充当子类父类之间的代理,既能够实现父类原型属性的继承,也能够在子类原型上随意拓展
使用Object.create()能够达到相同效果

Child.prototype = Object.create(Parent.prototype)

4.1.5 关于protptype.constructor
整理资料的时候,发现有些在继承后又写了一句Child.prototype.constructor = Child,有些就没有。首先这个constructor时建立实例对象的构造函数的引用,而后就是这句话到底有用没用,下面是ctrl cv自MDN的两个例子以及结论
示例1:

function Parent() {};
function CreatedConstructor() {}

CreatedConstructor.prototype = Object.create(Parent.prototype);

CreatedConstructor.prototype.create = function create() {
  return new this.constructor();
}

new CreatedConstructor().create().create(); // error undefined is not a function since constructor === Parent

在上面的示例中,将显示异常,由于构造函数连接到Parent。为了不它,只需分配将要使用的必要构造函数。

function Parent() {}; 
function CreatedConstructor() {} 

CreatedConstructor.prototype = Object.create(Parent.prototype); 
CreatedConstructor.prototype.constructor = CreatedConstructor; // set right constructor for further using

CreatedConstructor.prototype.create = function create() { 
  return new this.constructor();
} 

new CreatedConstructor().create().create(); // it's pretty fine

示例2:

function ParentWithStatic() {}

ParentWithStatic.startPosition = { x: 0, y:0 };
ParentWithStatic.getStartPosition = function getStartPosition() {
  return this.startPosition;
} 

function Child(x, y) {
  this.position = {
    x: x,
    y: y
  };
}

Child.prototype = Object.create(ParentWithStatic.prototype); 
Child.prototype.constructor = Child;

Child.prototype.getOffsetByInitialPosition = function getOffsetByInitialPosition() {
  var position = this.position;
  var startPosition = this.constructor.getStartPosition(); // error undefined is not a function, since the constructor is Child

  return {
    offsetX: startPosition.x - position.x,
    offsetY: startPosition.y - position.y
  }
};

对于此示例,就须要保持父构造函数继续正常工做。

结论:手动设置或更新构造函数可能会致使不一样且有时使人困惑的后果。为了防止它,只需在每一个特定状况下定义构造函数的角色。在大多数状况下,不使用构造函数,而且不须要从新分配构造函数。

4.2 经过复制属性实现继承
浅拷贝和4.1.3的共享原型没区别,深拷贝继承以后修改父类,子类不会改变。都有问题不过也是一种思路,顺带一提。

五、跨域有哪些解决方案
答:iframe, jsonp, cors。他:用过jsonp吗。答:没有,用的都是cors。他:那说一下cors是怎么解决跨域问题的。我:不知道。他:那请求头里有哪些相关的字段。我:(我知道你真的很给机会了可是对不起我是个菜鸡我真的)不知道。他:用过nginx吗。我:没有。
正确答案:点这里

balabala一些客套话,问我有什么问题,我说没有,而后结束。

路漫漫其修远兮

认识到差距也更有前进的动力,继续加油

相关文章
相关标签/搜索