談面試前端工程師

談面試前端工程師

  • 打戰需要周全的準備,而找一份年薪幾十萬的工作對於個人來說不亞於一場大戰
  • 有一天,你若需要招人,則需要在極短的時間內從各方面考核對方是否合適
  • 通過面試可以推動自己主動了解行業新技術,尤其是長期呆在一家技術不那麼好的公司
  • 面試的內容數不勝數(亦或千奇百怪),面試官和面試者的背景也不盡相同,或許我們只能儘力而為。將每次面試當成一次修鍊,好好享受

以下是近期朋友去面試前端工程師所遇到的一些問題,整理如下:

vue

v-model 修飾符有哪些

答:.number、.trim、.lazy

Tip: 用法如下:

<input v-model.lazy="msg">

<input v-model.number="age" type="number">

<input v-model.trim="msg">

v-model 的原理

答:v-model 可以在表單或組件上創建雙向綁定。v-model 的本質其實是一個語法糖,即名為 value 的 props 以及 input 事件。

  • 在組件上使用
<custom-input v-model="count"></custom-input>   

等於

<custom-input :value="count" @input='changCount'></custom-input> 
  • 在表單上使用
<input v-model="message" placeholder="edit me">

等於

<input type="text" :value='message' @input='handleInput' placeholder="edit me">

Tip:更多介紹請看 v-model

v-if 與 v-for 一起使用

答:不推薦同時使用 v-if 和 v-for。若一定要一起使用,可以使用 template,就像這樣:

<li v-for="todo in todos" >
    <template v-if="!todo.isComplete">
        {{ todo }}
    </template>
</li>

請簡單介紹下 vuex

答:在 vue 中我們可以使用 props 和 emit 解決父子組件之間的通訊,而非父子之間的通訊則可以使用 bus(中央事件匯流排)。而 Vuex 作為 vue 的一個插件,解決的問題與 bus 類似,更具體些,就是 vuex 能解決多個組件共享狀態的需求。

Vuex 的核心概念有 State、Getters、Mutations、Actions:

  • state,即 vuex 的數據
  • getters,可以認為是 store 的計算屬性
  • mutations,更改 vuex 中 state(數據)的唯一方式
  • actions,類似 mutation,但不能直接修改 state

Tip: 更多介紹請看 vuex基礎

vuex重刷還有嗎,如何做持久化

答:沒有了。可以配合 sessionStorage 做持久化,亦或使用 npm 包(vuex-persistedstate)。

Tip:

// App.vue
created(){
    //頁面載入時讀取 localStorage 里的狀態資訊
    if(localStorage.getItem("userMsg")){
        this.$store.replaceState(...)
    }
    //在頁面刷新時保存到 localStorage
    window.addEventListener("beforeunload",()=>{
        localStorage.setItem(...)
    })
  }

vue-router 默認是什麼模式

答:hash

javascript

是否了解 es11,?.??用過嗎

答:有所了解。例如 es11 中有 BigInt、可選鏈操作符( ?. )、空值合併操作符(??)。

?. 和 ?? 用法如下:

const obj = {
  a: 1,
  b: {
    c: 2
  }
};

// ?. 操作符的功能類似於 . 鏈式操作符,不同之處在於,
// 在引用為空 null 或者 undefined 的情況下不會引起錯誤,該表達式短路返回值是 undefined。
console.log(obj.d?.c) // undefined

console.log(obj.b?.c) // 2

console.log(obj?.someNonExistentMethod?.()) // undefined
// 當左側的操作數為 null 或者 undefined 時,返回其右側操作數,否則返回左側操作數
console.log(null ?? 'default string') // default string

// || vs ??
console.log(0 || 42) // 42
console.log(0 ?? 42) // 0

Tip:有關 ES2020、ES2021、ES2022 等特性可以查閱:kangax es2016plusbabel 插件列表

什麼是事件循環

答:js 是單執行緒的語言,同一時刻只能執行也給任務,只有第一個任務完成,才能接著做第二個任務,如果上一個任務是計算量很大,cup 很忙導致無法接著做下一個任務,這種情況沒問題,但如果上一個任務是只是在等待(如 ajax 請求),cup 很閑而導致下一個任務也只能跟著一起等待,那麼就不好了,所以需要事件循環這個機制來解決此問題。

事件循環機制如下:

  1. 所有同步任務都在主執行緒上執行,形成一個執行棧
  2. 除了主執行緒,還有一個任務隊列,只要非同步任務有了結果就會往任務隊列中放一個事件
  3. 當執行棧為空,主執行緒就會去讀任務隊列,放入執行棧中執行
  4. 如此循環

Tip:關於事件循環更詳細的介紹,有多種不同版本,筆者傾向於下面這種

主執行緒:
同步程式碼1->同步程式碼2->...->同步程式碼N

事件隊列:
- 宏任務隊列:事件1->事件2->...->事件N
- 微任務隊列: 事件1->事件2->...->事件N

①,執行棧中所有同步任務執行完畢
②,執行完微任務隊列中所有事件對應回調
③,取出宏任務隊列中一個事件,將對應回調加入執行棧

依次循環①②③①②③...

宏任務和微任務都有哪些

答:

  • 微任務:Pormise.then、MutationObserver
  • 宏任務:setTimeout、setInterval、requestAnimationFrame

給數組增加一個方法,只能存入唯一的值

答:

Array.prototype.uniquePush = function (...values) {
  values.forEach(v => {
    if (!this.includes(v)) {
      this.push(v)
    }
  })
}

let array = [1, 2, 3]
array.uniquePush(4, 3)
// array:  [ 1, 2, 3, 4 ]
console.log('array: ', array)

扁平化嵌套數組

例如有這麼一個數組:

const arr = [1, 2, [3, 4, 5, [6, 7, 8], 9], 10, [11, 12]]

將其扁平化(層級不限)成:

[ 1,  2, 3, 4,  5, 6,  7, 8, 9, 10, 11, 12 ]

可提供多種方式

答:

  • 使用數組的 flat() 方法
const flated1 = arr.flat(Infinity)
console.log('flated1: ', flated1);
  • 使用遞歸
function flat(arr, result = []) {
  arr.forEach(item => {
    let wrap = Array.isArray(item) ? flat(item) : [item]
    result.push(...wrap)
  })
  return result
}

const flated2 = flat(arr)
console.log('flated2: ', flated2);
  • 使用 toString() 方法
const flated3 = arr.toString().split(',').map(item => Number(item))
console.log('flated3: ', flated3)

Tip:Array.prototype.toString() 返回一個表示數組及其元素的字元串。並會調用每項的 toString() 方法:

let obj = {
  toString() {
    return 'i am obj'
  }
}

let arr2 = ['a', 3, obj]
// a,3,i am obj
console.log(arr2.toString())

請完成 getTree() 方法

// 將數據轉成樹的結構
function getTree(arr) {
 
}

// parent 為 -1 的是根節點。只有一個根節點
let flatTree = [
  { name: 'b', id: 2, parent: 1 },
  { name: 'a', id: 1, parent: -1 },
  { name: 'c', id: 3, parent: 1 },
  { name: 'd', id: 4, parent: 3 },
  { name: 'e', id: 5, parent: 3 },
]

let tree = getTree(flatTree)
// {"name":"a","id":1,"parent":-1,"children":[{"name":"b","id":2,"parent":1},{"name":"c","id":3,"parent":1,"children":[{"name":"d","id":4,"parent":3},{"name":"e","id":5,"parent":3}]}]}
console.log(JSON.stringify(tree))

答:

function getTree(arr) {
  arr.forEach(item => {
    if (item.parent === -1) {
      return
    }
    let pId = item.parent
    let parentNode = arr.find(item => item.id === pId)
    if (!parentNode.children) {
      parentNode.children = []
    }
    parentNode.children.push(item)
  })

  const root = arr.find(item => item.parent === -1)
  return root
}

刷新頁面 sesstionStorage 會清空嗎?

答:不會

刷新頁面,將在控制台依次輸出1、2、3…;若用新的 tab 訪問,又會依次輸出1、2、3…

let msg = sessionStorage.getItem('a')
if (!msg) {
    sessionStorage.setItem('a', '1')
} else {
    sessionStorage.setItem('a', (+msg) + 1)
}

console.log(sessionStorage.getItem('a'));

Tip: 更多介紹請看 sessionStorage

new Date(‘abc’) 會如何

答:控制台輸出 Invalid Date

async 中的 await 後面如果報錯,會發生什麼

答:會中斷 await 後續程式碼的執行,async 方法會立即結束。

Tip

  • 不明白題意,請看如下程式碼
async function foo() {
  // obj 是一個不存在的變數
  await obj
  return 1
}

foo().then(v => {
  console.log(v)
}).catch(v => {
  console.log(`catch, ${v}`)
})

// 輸出:catch, ReferenceError: obj is not defined
  • 為什麼會這樣可以查看:async

class 的構造函數能否使用 async

答:不能。

請看示例:

class Dog {
  async constructor() {
    this.name = 'apple'
  }
  async say() {
    console.log(this.name);
  }
}

new Dog().say()

// Class constructor may not be an async method

若用數組來實現棧、隊列,需要用到數組的什麼方法

答:棧是先進後出,所以會用到 push 、pop;而隊列是先進先出,故用到 push、shift

如何解決 0.1 + 0.2 != 0.3

答:此問題的出現是因為浮點數不能精確地用二進位表示所有小數,會出現一些意外的結果,比如:

let a = 0.1
let b = 0.2
let c = 0.3

// 0.30000000000000004
console.log(a + b)

有三種方法可以解決:

  • Number.EPSILON
  • 轉為整數再比較。即將數字提升 10 的 N 次方
  • 使用 toFixed()

請看示例:

function equal(a, b) {
  return Math.abs(a - b) < Number.EPSILON
}
let isEqual = equal(a + b, c)
// isEqual:  true
console.log('isEqual: ', isEqual)
let seed = Math.pow(10, 10)
let isEqual2 = (a * seed + b * seed) === c * seed
// isEqual2:  true
console.log('isEqual2: ', isEqual2);
let isEqual3 = parseFloat((a + b).toFixed(10)) === c
// isEqual3:  true
console.log('isEqual3: ', isEqual3)

http

什麼是協商快取、什麼是強制快取

答:快取的作用很好理解,通過復用以前的資源,可以提高網站的性能,提升用戶體驗。

通常第一次獲取資源後,會根據返回的資訊(respone header)得知如何快取,例如做強快取、協商快取或不做快取。

強快取的特性是,下次直接從快取中讀取,不會發送到伺服器,返回的狀態碼是 200(from cache)

協商快取的特性,也是直接從快取中讀取,不過得先到伺服器那裡詢問一下快取資源是否可用,返回的狀態碼是 304(not modified)

與強快取相關的 http 頭有 cache-controlExpires。比如Cache-control: max-age=N的頭,相應的快取的壽命就是N,對於不含這個屬性的請求,則會去查看是否包含 Expires 屬性。

與協商快取相關的是 EtagLast-Modified。比如,用戶再次訪問給定的URL(設有ETag欄位),若資源過期了,客戶端就發送值為ETag的If-None-Match header欄位。Last-Modified 由於精確度比 ETag 要低,所以這是一個備用機制

Tip: 對於單頁面,我們可以通過更新頁面(例如 index.html)中引用資源的路徑,讓瀏覽器重新載入資源,更多介紹請看 MDN HTTP 快取靜態資源的快取

https 為什麼安全

答:https 即超文本傳輸安全協議。https 經由 http 進行通訊,但利用 SSL/TLS(安全協議) 來加密數據包。之所以說 https 安全,主要是因為它有一套比較好的數據傳輸方案。

比如 A 向 B 發送消息,A 會通過 B 提供的公鑰對數據進行加密後再傳輸。並且 A 不是直接從 B 那裡獲得公鑰,而是從認證機構取得。

也就能保證:

  • 數據是以密文的形式進行傳輸,且只能被服務端配對的的私鑰進行解密
  • A 取得的公鑰來自認證機構,保證了公鑰確實是 B 的,而非偽造的

Tip:相關術語介紹:

  • 加密演算法和解密演算法統稱為密碼演算法
  • 密鑰是使用密碼演算法中輸入的一段參數。同一個明文在相同的密碼演算法和不同的密鑰下會產生不同的密文。根據密鑰的使用方法,可分為對稱加密和公鑰加密
  • 對稱加密,加密和解密使用相同的密鑰
  • 公鑰加密,分為加密密鑰(或稱公鑰)和解密密鑰(私鑰),其中任何人都可以獲取公鑰,而私鑰只能自己使用
  • 傳輸層安全性協議(英語:Transport Layer Security,縮寫:TLS)及其前身安全套接層(英語:Secure Sockets Layer,縮寫:SSL)是一種安全協議,目的是為互聯網通訊提供安全及數據完整性保障

:http 的默認埠是 80,而 https 的默認埠是 443。

描述下瀏覽器輸入 url 回車後的整個過程

答:
大概流程:

  1. 首先拿到伺服器 ip(即域名解析)
  2. 接著和伺服器建立連接,傳遞數據(即相互通訊)
  3. 伺服器處理請求
  4. 瀏覽器接收響應

更具體一些:

域名解析
拿到伺服器 ip 需要用到 域名解析(將好記的域名解析成 ip)。

解析流程大致如下圖所示:

  1. 客戶端:我要訪問 www.163.com,請告訴我 ip
  2. 本地 dns 伺服器:快取里找不到,去 dns 根伺服器詢問
  3. dns 根伺服器:這個域名由 .com 負責
  4. .com 域伺服器:163.com 域名應該知道
  5. 163.com 域伺服器:ip 是 1.1.1.xx
  6. 本地 dns 伺服器取得 www.163.com 對應的 ip,寫入快取,返回 ip 給客戶端

Tip

  • 真實的解析過程情況會更多,比如會搜索系統中 hosts 文件。筆者很久以前用過一個叫 SwitchHosts(能管理、切換多個 hosts) 的工具,開發中用於切換不同的環境,比如開發環境、測試環境等等。
  • dns(即域名系統),用於管理有意義的域名和ip的對應關係

相互通訊
主要是涉及 TCP/IP協議(一個協議族,能夠在多個不同網路間實現資訊傳輸,也是 Internet 最基本的協議)。

大致流程如下:

  1. 瀏覽器發起 http 請求
  2. 經由應用層
  3. 然後到傳輸層,其中 tcp 會使用三次握手建立連接,後面則會使用四次握手中斷連接
  4. 在往下就是網路層,比如 ip 定址
  5. 最後是網路介面層,這裡有傳輸數據的物理媒介
  6. 服務端接收則會反過來,依次是網路介面層,網路層,傳輸層,應用層

Tip:不同種類的應用程式會根據自己的需要來使用應用層的不同協議,比如瀏覽器在這裡會使用 http 協議。

瀏覽器接收響應
大致流程如下:

  • 瀏覽器接收到資源後,會根據響應內容做不同的處理
  • 比如響應碼是 301 則永久重定向,如果是 304 則去讀快取
  • 比如資源使用 gzip 壓縮了,就得解壓
  • 比如資源類型是影片(Content-Type:video/mp4),瀏覽器則會播放
  • 比如是否對資源做快取

Tip:有關構建 dom 樹、預載入掃描器、構建 cssom樹、迴流、重繪等可以看 這裡

零碎

  • 如果輸入的不是合法的 url,而是關鍵字,那麼瀏覽器就會去搜索該關鍵字
  • 如果之前已經訪問過,那麼會先走強快取,如果過期就走協商快取
  • 可能還會涉及 sts,例如通過 http 而非 https 來訪問淘寶、百度、京東的首頁,則會發生一個 307 的重定向,並轉到 https 上來

webpack

簡單說一下 tree-shaking (樹搖)

答:用於描述移除 JavaScript 上下文中的未引用程式碼

Tip: 更多細節請看 tree-shaking

說一下你知道的 loader

答:由於 webpack 只能識別 javascript,其他資源都需要相應的 loader 來做翻譯。

與css相關的有:css-loader、style-loader、less-loader、sass-loader、postcss-loader

配合圖片的有:url-loader、file-loader

與 svg 相關的有:svgo-loader

與 js 有關的有:babel-loader、ts-loader

與 vue 相關的有:vue-loader、vue-style-loader

Tip:以下是相應 loader 的簡易介紹:

  • css-loader,將 css 文件翻譯成 webpack 能識別
  • style-loader,將 css 注入 dom。
  • less-loader,用於將 less 轉為 css。類似的有 sass-loader、stylus-loader
  • postcss-loader,webpack 它用來使用 postcss
  • url-loader,將圖片轉為 base64
  • file-loader,配合 url-loader 可以生成圖片
  • label-loader,用於在 webpack 中使用 babel
  • ts-loader,與 typescript 相關
  • svgo-loader,與 svg 相關,用於 svg 優化
  • vue-loader,它允許你以一種名為單文件組件 (SFCs)的格式撰寫 Vue 組件
  • vue-style-loader,是一個基於 style-loader 的 fork,作為依賴項包含在 vue-loader 中,無需下載 1. vue-style-loader 即可使用。

請說一說 loader 和 plugin

答:
webpack 沒有特殊配置只能識別 javascript,而前端還有css、圖片等其他資源。

loader 用於對模組的源程式碼進行轉換,可以理解成」翻譯官「,比如我們需要使用 css,那麼可以用 css-loader,如果需要使用圖片,我們可以使用 url-loader 和 file-loader,如果需要將箭頭函數翻譯成普通函數,可以使用 babel-loader。loader 本質上是導出為函數的 JavaScript 模組。

而插件的目的是用於解決 loader 無法是現實其他事,plugin 強調一個事件監聽能力,能在 webpack 內部監聽一些事件,並且能改變一些文件打包後輸出的結果。比如我們可以使用 html-webpack-plugin 來幫我們創建 html 並自動引用打包後的資源。plugin 本質上是一個具有 apply 方法的類。

Tip:有關 loader 和 plugin 的本質介紹可以查看 這裡

請說一下 vue-loader

答:vue-loader 是一個 webpack 的 loader,它允許我們能以單文件組件的格式編寫 vue 組件。同時它也提供了一些其他特性,比如:提供熱重載、提供 scoped css、允許為 vue 組件的每個部分使用其他的 webapck loader(如 style 部分使用 sass,template 部分使用 pug)。總之,webpack 配合 vue-loader 能幫助我們編寫 vue 應用。

Tip: 更多介紹請看 vue-loader

其他

能否說說 lodash.js 的深拷貝

答:沒看過 lodash.js,但我用過 jQuery 的 extend 方法,如果要深拷貝,則傳 true 給第一個參數

深度作用選擇器

修改 element-ui 樣式,比如將表格組件的表頭改為紅色,通常是不起作用,就像下面這樣:

<template>
  <el-table></el-table>
</template>
  
<style scoped>
.el-table thead {
  color: red;
}
</style>

我們可以怎麼做?

答:可以使用 深度作用選擇器。即三個大於號(>>>),就像這樣:

<style scoped>
>>> .el-table thead {
  color: red;
}
</style>

子域名跨域 Document.domain

不修改後端伺服器,只在前端設置,如何讓 a.baidu.com 和 b.baidu.com 能相互訪問

答:可以使用 Document.domain

平時你是如何封裝 axios

答:會簡單封裝下 axios。比如創建一個 request.js 的文件,裡面返回一個 axios 實例,再做一下請求攔截和響應攔截。請求攔截器可以統一給所有請求傳遞一些資訊給伺服器,比如 token,在響應攔截中,比如返回 code 不是2000,則拋出錯誤,比如是 3000 就說明需要重新登錄等等。

如何將二進位數據顯示成圖片

答:或許可以利用 URL、Blob。就像這樣:

var blob = new Blob(...);
var url = URL.createObjectURL(blob)
imageElement.setAttribute('src', url)

一般通過什麼途徑來學習

答:比較雜,比如:騰訊 Web 前端、百度前端、淘寶前端、凹凸實驗室、奇舞團,或者 github(看開源項目)、npm、babel、掘金、簡書、部落格園、紙質書、線上影片(如 慕課網、bilibili、極客學院)、技術相關的官網(如 node、express、webpack)、mdn、維基百科等等

簡單談一下跨域

答:跨域方式有很多,但現在主流的有兩種,一種是開發環境和生成環境都使用 cors(跨域資源共享),一種是開發環境用 proxy,而生產環境用 nginx。

推薦的是 cors,它能決定瀏覽器是否阻止前端 JavaScript 程式碼獲取跨域請求的響應。前端無需變化,工作量在後端。而後端的開發環境和生成環境是一套程式碼,所以也很方便使用。如果後端不願意,前端就用 proxy + nginx。

Tip:更多細節可以查看 這裡

請說一下 bfc(塊級格式化上下文)

答:bfc 有個特性,如果一個元素具有 bfc,那麼內部子元素在怎麼折騰,都不會影響外部元素。

所以 bfc 元素不會有 margin 重疊。bfc 元素也能用來清除浮動的影響,子元素浮動會導致父元素高度塌陷,則會影響後面布局,這有悖於前面的特性。

創建 bfc 有多種方式,比如 overflow:auto/hiddendisplay:table-cell

是否用過 chrome 中 Performance

答:用過。通常在無痕模式下進行來分析性能,比如可以通過勾選記憶體其來分析記憶體使用情況,或使用調用樹來找出花費時間較長的函數等。

是否了解過微服務、微前端

答:是的。微前端第一提出好似在 2016年底,將微服務這個被應用於服務端的技術擴展到前端領域。微前端是一種多個團隊通過獨立發布功能的方式來共同構建現代化 web 應用的技術手段及方法策略。比如 螞蟻金融開源一套完整的微前端解決方案,即 qiankun(對於用戶而言只是一個類似 jQuery 的庫)。

是否自己寫過 npm 包

答:寫過。

Tip:如何發布一個 npm 包請看 這裡

結束

在羅列一點剩餘的題目:

  • 介紹一下 vue,就當我是 vue 小白
  • 是否了解 csrf,能否簡單談一下
  • 如果做單頁面應用的首頁優化
  • 是否了解 vue 中的 $listeners、$attrs
  • 請介紹一下 http1 和 http2 的關係和區別
  • 比如讓你制定項目規範,你會從什麼方面著手
  • 請說說你知道的 http 頭部(headers)
  • 請說說你知道的 http 狀態碼
  • 是否使用過 corss-env

把心放平,輕裝上陣,祝君好運。

Tags: