兩年經驗前端:位元組跳動系—石墨文件面經

Sunshine_Lin發表於2022-02-01

前言

今天給大家分享一個位元組跳動系公司——石墨文件的面經吧!廢話不多說,先看題目!

題目

一面

  • 1、['10', '10', '10', '10', '10'].map(parseInt) 的輸出值是什麼?
  • 2、你們現在的技術棧是什麼?為什麼要使用ts?
  • 3、setTimeout的執行過程(事件迴圈,同步、非同步)?
  • 4、對Promise的理解,與async、await的區別,async、await是怎麼實現的?
  • 5、解釋 requestAnimationFrame/requestIdleCallback,分別有什麼用?
  • 6、react效能優化?
  • 7、說說對flex的理解?
  • 8、迴流、重繪是什麼?如何減少迴流和重繪?
  • 9、怎麼尋找react頁面卡頓的原因?
  • 10、程式設計題:實現一個物件的 flatten 方法,如下:

    const obj = {  a: {   b: 1,   c: 2,   d: {    e: 5   }  },   b: [1, 3, {a: 2, b: 3}],   c: 3 }

    flatten(obj){} 結果返回如下:

    // { //   'a.b': 1, //   'a.c': 2, //   'a.d.e': 5, //   'b[0]': 1, //   'b[1]': 3, //   'b[2].a': 2, //   'b[2].b': 3 //   c: 3 // }

二面

  • 1、說說對web worker的理解
  • 2、service worker和強快取相比,有哪些優勢?
  • 3、說說對堆疊溢位的理解
  • 4、position中的sticky是什麼,還有哪些其他的?
  • 5、ts中,any和unknown分別是什麼意思?泛型怎麼使用?
  • 6、bind有什麼用?連續多個bind,最後this指向是什麼?
  • 7、webpack的plugin怎麼實現?
  • 8、程式設計題:
    現已知一個字串是由正整數和加減乘除四個運算子(+ - /)組成。 例如存在字串 const str = '11+2-34+5/24+10/5',現在需要將高優先順序運算,用小括號包裹起來,例如結果為 '11+2-(34)+(5/2*4)+(10/5)'。注意可能會出現連續的乘除運算,需要包裹到一起。 請用 javascript 實現這一過程

三面

  • 1、手寫體:使用TypeScript 實現一個 get 函式來獲取它的屬性值

    const data = { name: 'tom', age: 18, address: 'xxx' }
  • 2、ts中的 any 、 unknown 的區別
  • 3、有用過ts中的 keyof 嗎?
  • 4、for in/for of的區別?
  • 5、Promise值穿透?

解答

一面

1、['10', '10', '10', '10', '10'].map(parseInt) 的輸出值是什麼?

可轉化為:

['10', '10', '10', '10', '10'].map((num, index) => parseInt(num, index))
// [10, NaN, 2, 3, 4]

'10' 0 -> 10:    進製為0,則預設10進位制
'10' 1 -> NaN:    1進位制不存在
'10' 2 -> 2:    2 * 1 + 2 * 0
'10' 3 -> 3:    3 * 1 + 3 * 0
'10' 4 -> 4:    4 * 1 + 4 * 0

2、你們現在的技術棧是什麼?為什麼要使用ts?

typescript JavaScript 的超集,它本質其實是是在 JavaScript 上新增了 可選靜態型別 基於類的物件導向程式設計

typescript 的特點

  • 可以在編譯期間發現並糾正錯誤
  • 提高可維護性
  • 提高協同開發的效率
  • 支援強型別、介面、泛型、模組

3、setTimeout的執行過程(事件迴圈,同步、非同步)?

事件迴圈

  • 1、執行同步程式碼
  • 2、 1 中生成的 微任務 先執行
  • 3、 1 中生產的 巨集任務 再執行

同步

簡單來說就是:排隊。程式碼有前後順序,必須按照順序去執行
非同步

非同步任務可以不阻塞後面的程式碼執行,而是可以同時進行,並且執行完後會有一個非同步的回撥。

想起一個故事可以很好的解釋 同步 非同步

  • 同步:你打電話去書店借書,老闆接電話時讓你等著,他去找書,你只能守著電話乾等著
  • 非同步:你打電話去書店借書,老闆接電話後說等他找到書再打回給你,然後掛電話了,這段找書的時間你可以自由活動

4、對Promise的理解,與async、await的區別,async、await是怎麼實現的?

Promise的理解

顧名思義, Promise 就是 承諾 的意思,表現在了Promise的狀態一旦改變則不會再變了,如果狀態為 fulfilled 則執行 then ,如果狀態為 rejected 則執行 catch ,Promise 也支援 鏈式呼叫 。我覺得Promise最大的用處就是 解決了回撥地獄,提高了程式碼的可讀性 。常用的方法有 resolve、reject、then、catch、race、all、allSettled、any、finally

async await
async/await 的作用是 用同步的方式執行非同步的操作 ,它的實現原理,我個人理解就是利用了 Promise 的不斷巢狀,再加上 generator函式 的步驟控制,實現了按順序執行非同步操作的效果

補充:async函式返回的是一個Promise

5、解釋 requestAnimationFrame/requestIdleCallback,分別有什麼用?

  • requestAnimationFrame:

    • 一般間隔是 16ms ,因為大部分電腦都是 每秒60幀 ,所以 1000 / 60 ≈ 16ms
    • 會把每一幀中的所有DOM操作集中起來,在一次重繪或迴流中完成,且時間間隔緊緊跟隨瀏覽器的重新整理頻率
    • 如果有隱藏或不可見的元素,將不會進行重繪或迴流,減少了cpu、gpu的記憶體使用量
    • 如需取消則使用 cancelAnimationFrame
  • requestIdleCallback:我的理解就是找瀏覽器空閒時間去執行傳入的回撥,具體也沒在專案中使用過

6、react效能優化?

7、說說對flex的理解?

彈性佈局 ,設定了 display: flex 的盒子為 彈性盒子 ,子元素會自動變成 彈性專案 ,盒子有一根主軸,預設是水平,並且有一個交叉軸(跟主軸垂直)。

彈性盒子的樣式:

  • flex-direction:定義主軸方向
  • flex-wrap:是否允許換行
  • flex-flow:flex-direction 和 flex-wrap的簡寫
  • justify-content:主軸方向上的對齊方式
  • align-items:交叉軸方向的對齊方式
  • align-content:多根軸線的對齊方式

彈性專案的樣式:

  • order:定義專案的排列順序,數值越小排列越靠前,預設0
  • flex-grow:定義專案的放大比例,預設為 0
  • flex-shrink:定義專案的縮小比例,預設為1
  • flex-basis:定義了在分配多餘空間之前,專案佔據的主軸空間,預設auto
  • flex:flex-grow、flex-shrink、flex-basis的簡寫
  • align-self:允許單個專案設定不同的交叉軸對齊方式

    8、迴流、重繪是什麼?如何減少迴流和重繪?

    重繪迴流

  • 迴流:尺寸、佈局改變時,引起頁面重新構建
  • 重繪:元素外觀、風格改變時,不影響佈局,則為重繪
  • 區別:迴流一定引起重繪,重繪不一定引起迴流
  • 瀏覽器幫忙:瀏覽器維護一個佇列,把所有引起迴流、重繪的操作放入這個佇列,等佇列到了一定數量或者到了一定的時間間隔,瀏覽器就會清空佇列,進行批量處理。

避免重繪、迴流

  • 1、批量修改DOM或者樣式
  • 2、複雜動畫使用絕對定位讓它脫離文件流,不然會印日分元素或後續元素的頻繁迴流
  • 3、GPU加速:transform、opacity、filters、will-change等樣式

    9、判斷一個物件是陣列的方法?

  • Object.prototype.toString.call(xxx)
  • Array.isArray(xxx)
  • xxx instaceOf Array

    10、怎麼尋找react頁面卡頓的原因?

    11、程式設計題:實現一個物件的 flatten 方法,如下:

    const obj = {  a: {   b: 1,   c: 2,   d: {    e: 5   }  },   b: [1, 3, {a: 2, b: 3}],   c: 3 }

    flatten(obj){} 結果返回如下:

    // { //   'a.b': 1, //   'a.c': 2, //   'a.d.e': 5, //   'b[0]': 1, //   'b[1]': 3, //   'b[2].a': 2, //   'b[2].b': 3 //   c: 3 // }

    解題

    const isObject = (target) => {
    return typeof target === 'object' && target !== null
    }
    
    const flatten = (obj) => {
    if (!isObject) return
    
    const res = {}
    const dfs = (cur, prefix) => {
      if (isObject(cur)) {
        if (Array.isArray(cur)) {
          cur.forEach((item, index) => dfs(item, `${prefix}[${index}]`))
        } else {
          for(let key in cur) {
            dfs(cur[key], `${prefix}${prefix ? '.' : ''}${key}`)
          }
        }
      } else {
        res[prefix] = cur
      }
    }
    dfs(obj, '')
    
    return res
    }

二面

1、說說對web worker的理解

  • 1、開啟一個子執行緒,並在此子執行緒進行一些大資料處理或者耗時的操作
  • 2、使用 postMessage onmessage ,實現主執行緒和子執行緒之間的通訊
  • 3、使用 onerror 監聽子執行緒掛了沒
  • 4、 web worker 並沒有改變JavaScript單執行緒的事實

2、service worker和強快取相比,有哪些優勢?

service快取沒用過。。

3、說說對堆疊溢位的理解?

常見的情況發生在 大數量遞迴 死迴圈 時,就會造成 棧溢位 ,因為每次執行程式碼都需要分配一定空間的記憶體,以上兩種情況都會使執行空間超出最大限度,從而報錯

4、position中的sticky是什麼,還有哪些其他的?

  • static:預設
  • relative:相對定位,相對於自身定位
  • absolute:絕對定位,相對於非static的第一個祖宗元素定位
  • fixed:相對於瀏覽器視窗進行定位
  • inherit:規定應該從父元素繼承 position 屬性的值
  • sticky:吸頂定位

5、ts中,any和unknown分別是什麼意思?泛型怎麼使用?

  • any:變數如果是 any 型別,繞過所有型別檢查,直接可使用
  • unknown:變數如果是 unknow 型別,需要判斷完是什麼型別之後才能使用

6、bind有什麼用?連續多個bind,最後this指向是什麼?

bind 的作用是改變函式執行的指向,且不會立即執行,而是返回一個新的函式,可以自主呼叫這個函式的執行(此函式不可當做建構函式)

連續多個bind之後this指向始終指向第一個

7、webpack的plugin怎麼實現?

一個plugin就是一個類,類裡有一個 apply方法 ,每次打包時都會呼叫這個apply,而這個apply方法接受一個引數物件,其中有一個 plugin 方法,此方法中有許多 鉤子函式 ,且可以決定靜態檔案的生成,修改等等

8、程式設計題:

現已知一個字串是由正整數和加減乘除四個運算子(+ - /)組成。 例如存在字串 const str = '11+2-34+5/24+10/5',現在需要將高優先順序運算,用小括號包裹起來,例如結果為 '11+2-(34)+(5/2*4)+(10/5)'。注意可能會出現連續的乘除運算,需要包裹到一起。 請用 javascript 實現這一過程

解答
我比較菜,用的方法也是臨時想出來的,沒有優化,大家將就著看吧:

const checkType = (str) => {
  if (['*', '/'].includes(str)) return 'high'
  if (['+', '-'].includes(str)) return 'low'
  return 'number'
}

const addBrackets = (formula) => {
  const strs = formula.split('')
  let i = 0, j = 1, high = false, res = []
  while(j < strs.length) {
    const jType = checkType(strs[j])
    if (jType === 'low' && !high) {
      i = ++j
      j++
    }else if (jType === 'low' && high) {
      res.push(j++)
      i = j++
      high = false
    }else if (jType === 'high') {
      j++
      !high && res.push(i)
      high = true
    }else {
      j++
    }
  }
  if (high) res.push(strs.length)
  let add = 0
  for(let i = 0; i < res.length; i++) {
    const index = res[i]
    strs.splice(index + add, 0, add % 2 ? ')' : '(')
    add++
  }
  return strs.join('')
}

三面

1、手寫體:使用TypeScript 實現一個 get 函式來獲取它的屬性值

const data = { name: 'tom', age: 18, address: 'xxx' }

解答:

const get = <T extends object, K extends keyof T>(obj: T, key: K): T[K] => {
  return obj[key]
}

2、ts中的 any 、 unknown 的區別?

  • any:變數如果是 any 型別,繞過所有型別檢查,直接可使用
  • unknown:變數如果是 unknow 型別,需要判斷完是什麼型別之後才能使用

    3、有用過ts中的 keyof 嗎?

    將一個interface的所有key,匯聚成一個聯合型別,可以用來對傳入key的限制,比如:

    interface Target {
    name: string,
    age: number
    }
    
    const fn = (obj: Target, key: keyof Target) => {}
    
    const obj: Target = { name: 'sunshine', age: 18 }
    
    fn(obj, name) // 成功
    fn(obj, age) // 成功
    fn(obj, height) // 報錯

    4、for in/for of的區別?

  • for in:遍歷物件的key或者陣列的索引
  • for of:遍歷可迭代物件的值,如陣列、Set

    5、Promise值穿透

    then或catch沒有傳入函式的話,會發生值穿透,原理是Promise內部檢測如果傳入的是非函式,則會拿上一次的結果包裝成一個返回Promise的函式,達到穿透效果

例如:

Promise.resolve('foo')
    .then(Promise.resolve('bar'))
    .then(function(result){
      console.log(result) // foo
    })

但是如果傳入的是函式的話:

Promise.resolve('foo')
    .then(() => Promise.resolve('bar'))
    .then(function(result){
      console.log(result) // bar
    })

結語

由於本人React太菜,所以不敢答題有關React的題目

我是林三心,一個熱心的前端菜鳥程式設計師。如果你上進,喜歡前端,想學習前端,那我們們可以交朋友,一起摸魚哈哈,摸魚群,加我請備註【思否】

image.png

相關文章