一篇字節跳動前端面經

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一些客套話,問我有什麼問題,我說沒有,而後結束。

路漫漫其修遠兮

認識到差距也更有前進的動力,繼續加油

相關文章
相關標籤/搜索