2020前端 web 最新 面試題 128 題彙總含詳細答案

霖呆呆的近期面試128題彙總(含超詳細答案) (本文轉自羣友)

前言

你盼世界,我盼望你無bug。Hello 大家好!我是霖呆呆!

哈哈😄,這是一篇面試總結文章,抱歉,webpackHTTP系列的先暫緩一下更新哈,Sorry~

這篇文章是呆呆自己近期的一些面試彙總,算了一下有128道,基本都寫了比較完善的答案,就算沒有寫也有推薦一些好的文章鏈接,文章篇幅較大,整理總結也花費了很多的時間和心血,題目也是根據公司規模的大小從易到難,算是見證了呆呆面試過程中的不足與成長吧 😅。還希望能幫助到正在一起努力求生存的小夥伴們。

另外,看到標題了嗎?"掘金技術徵文",嘻嘻,要不要考慮幫這麼"用心"的呆呆拿拿大獎呢?放心放心,要真成了好處少不了你們😋。

所有文章內容都已整理至 https://github.com/LinDaiDai/niubility-coding-js 快來給我Star呀😊~

(因爲近期面的主要都是一些深圳的中小公司,他們也還在招聘中,所以不便透露公司名稱,還請大家理解…)

深圳某做海外加速器公司

4月22日上午

1. DIV+CSS佈局的好處

  1. 代碼精簡,且結構與樣式分離,易於維護
  2. 代碼量減少了,減少了大量的帶寬,頁面加載的也更快,提升了用戶的體驗
  3. 對SEO搜索引擎更加友好,且H5又新增了許多語義化標籤更是如此
  4. 允許更多炫酷的頁面效果,豐富了頁面
  5. 符合W3C標準,保證網站不會因爲網絡應用的升級而被淘汰

缺點:
不同瀏覽器對web標準默認值不同,所以更容易出現對瀏覽器的兼容性問題。

2. 如何解決a標點擊後hover事件失效的問題?

改變a標籤css屬性的排列順序

只需要記住LoVe HAte原則就可以了:

link→visited→hover→active

比如下面錯誤的代碼順序:

a:hover{
  color: green;
  text-decoration: none;
}
a:visited{ /* visited在hover後面,這樣的話hover事件就失效了 */
  color: red;
  text-decoration: none;
}

正確的做法是將兩個事件的位置調整一下。

注意⚠️各個階段的含義:

a:link:未訪問時的樣式,一般省略成a
a:visited:已經訪問後的樣式
a:hover:鼠標移上去時的樣式
a:active:鼠標按下時的樣式

3. 點擊一個input依次觸發的事件

const text = document.getElementById('text');
text.onclick = function (e) {
  console.log('onclick')
}
text.onfocus = function (e) {
  console.log('onfocus')
}
text.onmousedown = function (e) {
  console.log('onmousedown')
}
text.onmouseenter = function (e) {
  console.log('onmouseenter')
}

答案:

'onmouseenter'
'onmousedown'
'onfocus'
'onclick'

4. 響應式的好處

對某些數據的修改就能自動更新視圖,讓開發者不用再去操作DOM,有更多的時間去思考業務邏輯。

5. Vue的優點及缺點

首先Vue最核心的兩個特點,響應式組件化

響應式:這也就是vue.js最大的優點,通過MVVM思想實現數據的雙向綁定,通過虛擬DOM讓我們可以用數據來操作DOM,而不必去操作真實的DOM,提升了性能。且讓開發者有更多的時間去思考業務邏輯。

組件化:把一個單頁應用中的各個模塊拆分到一個個組件當中,或者把一些公共的部分抽離出來做成一個可複用的組件。所以組件化帶來的好處就是,提高了開發效率,方便重複使用,使項目的可維護性更強。

虛擬DOM,當然,這個不是vue中獨有的。

缺點:基於對象配置文件的寫法,也就是options寫法,開發時不利於對一個屬性的查找。另外一些缺點,在小項目中感覺不太出什麼,vuex的魔法字符串,對ts的支持。兼容性上存在一些問題。

6. Vue中hash模式和history模式的區別

  • 最明顯的是在顯示上,hash模式的URL中會夾雜着#號,而history沒有。
  • Vue底層對它們的實現方式不同。hash模式是依靠onhashchange事件(監聽location.hash的改變),而history模式是主要是依靠的HTML5 history中新增的兩個方法,pushState()可以改變url地址且不會發送請求,replaceState()可以讀取歷史記錄棧,還可以對瀏覽器記錄進行修改。
  • 當真正需要通過URL向後端發送HTTP請求的時候,比如常見的用戶手動輸入URL後回車,或者是刷新(重啓)瀏覽器,這時候history模式需要後端的支持。因爲history模式下,前端的URL必須和實際向後端發送請求的URL一致,例如有一個URL是帶有路徑path的(例如www.lindaidai.wang/blogs/id),如果後端沒有對這個路徑做處理的話,就會返回404錯誤。所以需要後端增加一個覆蓋所有情況的候選資源,一般會配合前端給出的一個404頁面。

hash:

window.onhashchange = function(event){
  // location.hash獲取到的是包括#號的,如"#heading-3"
  // 所以可以截取一下
	let hash = location.hash.slice(1);
}

7. react的實現原理?有什麼優缺點?

(技術棧是Vue,沒用過react)

8. react的控制組件和非控制組件

同上

深圳某電商公司

4月22日下午

1. null和undefined的區別

  • null表示一個"無"的對象,也就是該處不應該有值;而undefined表示未定義
  • 在轉換爲數字時結果不同,Number(null)0,而undefinedNaN

使用場景上:

null

  • 作爲函數的參數,表示該函數的參數不是對象
  • 作爲對象原型鏈的終點

undefined:

  • 變量被聲明瞭,但沒有賦值時,就等於undefined
  • 調用函數時,應該提供的參數沒有提供,該參數等於undefined
  • 對象沒有賦值屬性,該屬性的值爲undefined
  • 函數沒有返回值時,默認返回undefined

2. 冒泡排序算法和數組去重

冒泡排序

function bubbleSort (arr) {
  for (let i = 0; i < arr.length; i++) {
    let flag = true;
    for (let j = 0; j < arr.length - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        flag = false;
        let temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
    if (flag) break;
  }
}

這個是優化過後的冒泡排序。用了一個flag來優化,它的意思是:如果某一次循環中沒有交換過元素,那麼意味着排序已經完成了。

冒泡排序總會執行(N-1)+(N-2)+(N-3)+…+2+1趟,但如果運行到當中某一趟時排序已經完成,或者輸入的是一個有序數組,那麼後邊的比較就都是多餘的,爲了避免這種情況,我們增加一個flag,判斷排序是否在中途就已經完成(也就是判斷有無發生元素交換)

數組去重

  1. Array.form(new Set(arr))
  2. [...new Set(arr)]
  3. for循環嵌套,利用splice去重
  4. 新建數組,利用indexOf或者includes去重
  5. 先用sort排序,然後用一個指針從第0位開始,配合while循環去重

當然還有很多,例如用filter、reduce、Map、Object等,具體可以看:

JavaScript數組去重(12種方法)

Array.form(new Set(arr))[...new Set(arr)]

var arr = [1,1,2,5,6,3,5,5,6,8,9,8];
console.log(Array.from(new Set(arr)))
// console.log([...new Set(arr)])

for循環嵌套,利用splice去重

function unique (origin) {
  let arr = [].concat(origin);
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] == arr[j]) {
        arr.splice(j, 1);
        j--;
      }
    }
  }
  return arr;
}
var arr = [1,1,2,5,6,3,5,5,6,8,9,8];
console.log(unique(arr))

新建數組,利用indexOf去重:

function unique (arr) {
  let res = []
  for (let i = 0; i < arr.length; i++) {
    if (!res.includes(arr[i])) {
      res.push(arr[i])
    }
  }
  return res;
}
var arr = [1,1,2,5,6,3,5,5,6,8,9,8];
console.log(unique(arr))

先用sort排序,然後用一個指針從第0位開始,配合while循環去重

function unique (arr) {
  arr = arr.sort(); // 排序之後的數組
  let pointer = 0;
  while (arr[pointer]) {
    if (arr[pointer] != arr[pointer + 1]) { // 若這一項和下一項不相等則指針往下移
      pointer++;
    } else { // 否則刪除下一項
      arr.splice(pointer + 1, 1);
    }
  }
  return arr;
}
var arr = [1,1,2,5,6,3,5,5,6,8,9,8];
console.log(unique(arr))

深圳某雲產品公司

4月23日上午

(從這家公司開始面試稍微有些難度了,面試官小哥哥人也很好,剛開始是一個高冷男神,但是在呆呆的猛烈回答下也終於還是對我露出了微笑😄,還說他也是掘友,有看過我的文章…掘友真是無處不在啊,感動😹)

1. 描述一下Promise

這道題我會先大概介紹一下Promise

Promise 是一個對象,它代表了一個異步操作的最終完成或者失敗。由於它的then方法和catch、finally方法會返回一個新的Promise所以可以允許我們鏈式調用,解決了傳統的回調地獄問題。

再說一下then以及catch方法:

(此處我是直接拿我之前的一篇文章《45道Promise題》那裏的總結)

  1. Promise的狀態一經改變就不能再改變。(見3.1)
  2. .then.catch都會返回一個新的Promise。(上面的👆1.4證明了)
  3. catch不管被連接到哪裏,都能捕獲上層未捕捉過的錯誤。(見3.2)
  4. Promise中,返回任意一個非 promise 的值都會被包裹成 promise 對象,例如return 2會被包裝爲return Promise.resolve(2)
  5. Promise.then 或者 .catch 可以被調用多次, 但如果Promise內部的狀態一經改變,並且有了一個值,那麼後續每次調用.then或者.catch的時候都會直接拿到該值。(見3.5)
  6. .then 或者 .catchreturn 一個 error 對象並不會拋出錯誤,所以不會被後續的 .catch 捕獲。(見3.6)
  7. .then.catch 返回的值不能是 promise 本身,否則會造成死循環。(見3.7)
  8. .then 或者 .catch 的參數期望是函數,傳入非函數則會發生值透傳。(見3.8)
  9. .then方法是能接收兩個參數的,第一個是處理成功的函數,第二個是處理失敗的函數,再某些時候你可以認爲catch.then第二個參數的簡便寫法。(見3.9)
  10. .finally方法也是返回一個Promise,他在Promise結束的時候,無論結果爲resolved還是rejected,都會執行裏面的回調函數。

另外也可以說一下finally方法:

  1. .finally()方法不管Promise對象最後的狀態如何都會執行

  2. .finally()方法的回調函數不接受任何的參數,也就是說你在.finally()函數中是沒法知道Promise最終的狀態是resolved還是rejected

  3. 它最終返回的默認會是一個上一次的Promise對象值,不過如果拋出的是一個異常則返回異常的Promise對象。

最後可以說一下all以及race方法:

  • Promise.all()的作用是接收一組異步任務,然後並行執行異步任務,並且在所有異步操作執行完後才執行回調。
  • .race()的作用也是接收一組異步任務,然後並行執行異步任務,只保留取第一個執行完成的異步操作的結果,其他的方法仍在執行,不過執行結果會被拋棄。
  • Promise.all().then()結果中數組的順序和Promise.all()接收到的數組順序一致。
  • all和race傳入的數組中如果有會拋出異常的異步任務,那麼只有最先拋出的錯誤會被捕獲,並且是被then的第二個參數或者後面的catch捕獲;但並不會影響數組中其它的異步任務的執行。

2. Promise.all中如果有一個拋出異常了會如何處理

這個,在上一題已經說到了:

all和race傳入的數組中如果有會拋出異常的異步任務,那麼只有最先拋出的錯誤會被捕獲,並且是被then的第二個參數或者後面的catch捕獲;但並不會影響數組中其它的異步任務的執行。

3. Promise爲什麼能鏈式調用

由於它的then方法和catch、finally方法會返回一個**新的Promise**所以可以允許我們鏈式調用

4. 描述一下EventLoop的執行過程

  • 一開始整個腳本作爲一個宏任務執行

  • 執行過程中同步代碼直接執行,宏任務進入宏任務隊列,微任務進入微任務隊列

  • 當前宏任務執行完出隊,檢查微任務列表,有則依次執行,直到全部執行完

  • 執行瀏覽器UI線程的渲染工作

  • 檢查是否有Web Worker任務,有則執行

  • 執行完本輪的宏任務,回到2,依此循環,直到宏任務和微任務隊列都爲空

(具體可以看這裏:https://juejin.im/post/5e58c618e51d4526ed66b5cf#heading-1)

5. docoment,window,html,body的層級關係

層級關係

window > document > html > body
  • windowBOM的核心對象,它一方面用來獲取或設置瀏覽器的屬性和行爲,另一方面作爲一個全局對象。
  • document對象是一個跟文檔相關的對象,擁有一些操作文檔內容的功能。但是地位沒有window高。
  • html元素對象和document元素對象是屬於html文檔的DOM對象,可以認爲就是html源代碼中那些標籤所化成的對象。他們跟div、select什麼對象沒有根本區別。

(我是這樣記的,整個瀏覽器中最大的肯定就是窗口window了,那麼進來的我不管你是啥,就算你是document也得給我盤着)

6. addEventListener函數的第三個參數

第三個參數涉及到冒泡和捕獲,是true時爲捕獲,是false則爲冒泡

7. 有寫過原生的自定義事件嗎

  • 使用Event

  • 使用customEvent (可以傳參數)

  • 使用document.createEvent('CustomEvent')和initCustomEvent()

創建自定義事件

原生自定義事件有三種寫法:

  1. 使用Event
let myEvent = new Event('event_name');
  1. 使用customEvent (可以傳參數)
let myEvent = new CustomEvent('event_name', {
	detail: {
		// 將需要傳遞的參數放到這裏
		// 可以在監聽的回調函數中獲取到:event.detail
	}
})
  1. 使用document.createEvent('CustomEvent')和initCustomEvent()
let myEvent = document.createEvent('CustomEvent');// 注意這裏是爲'CustomEvent'
myEvent.initEvent(
	// 1. event_name: 事件名稱
	// 2. canBubble: 是否冒泡
	// 3. cancelable: 是否可以取消默認行爲
)
  • createEvent:創建一個事件
  • initEvent:初始化一個事件

可以看到,initEvent可以指定3個參數。

(有些文章中會說還有第四個參數detail,但是我查看了W3C上並沒有這個參數,而且實踐了一下也沒有效果)

事件的監聽

自定義事件的監聽其實和普通事件的一樣,使用addEventListener來監聽:

button.addEventListener('event_name', function (e) {})

事件的觸發

觸發自定義事件使用dispatchEvent(myEvent)

注意⚠️,這裏的參數是要自定義事件的對象(也就是myEvent),而不是自定義事件的名稱('myEvent')

案例

來看個案例吧:

// 1.
// let myEvent = new Event('myEvent');
// 2.
// let myEvent = new CustomEvent('myEvent', {
//   detail: {
//     name: 'lindaidai'
//   }
// })
// 3.
let myEvent = document.createEvent('CustomEvent');
myEvent.initEvent('myEvent', true, true)

let btn = document.getElementsByTagName('button')[0]
btn.addEventListener('myEvent', function (e) {
  console.log(e)
  console.log(e.detail)
})
setTimeout(() => {
  btn.dispatchEvent(myEvent)
}, 2000)

8. 冒泡和捕獲的具體過程

冒泡指的是:當給某個目標元素綁定了事件之後,這個事件會依次在它的父級元素中被觸發(當然前提是這個父級元素也有這個同名稱的事件,比如子元素和父元素都綁定了click事件就觸發父元素的click)。

捕獲則是從上層向下層傳遞,與冒泡相反。

(非常好記,你就想想水底有一個泡泡從下面往上傳的,所以是冒泡)

來看看這個例子:

<!-- 會依次執行 button li ul -->
<ul onclick="alert('ul')">
  <li onclick="alert('li')">
    <button onclick="alert('button')">點擊</button>
  </li>
</ul>
<script>
  window.addEventListener('click', function (e) {
    alert('window')
  })
  document.addEventListener('click', function (e) {
    alert('document')
  })
</script>

冒泡結果:button > li > ul > document > window

捕獲結果:window > document > ul > li > button

9. 所有的事件都有冒泡嗎?

並不是所有的事件都有冒泡的,例如以下事件就沒有:

  • onblur
  • onfocus
  • onmouseenter
  • onmouseleave

11. 描述下原型鏈

12. 手寫new

function myNew (fn, ...args) {
  let instance = Object.create(fn.prototype);
  let result = fn.call(instance, ...args)
  return typeof result === 'object' ? result : instance;
}

13. typeof和instanceof的區別

typeof表示是對某個變量類型的檢測,基本數據類型除了null都能正常的顯示爲對應的類型,引用類型除了函數會顯示爲'function',其它都顯示爲object

instanceof它主要是用於檢測某個構造函數的原型對象在不在某個對象的原型鏈上

14. typeof爲什麼對null錯誤的顯示

這只是 JS 存在的一個悠久 Bug。在 JS 的最初版本中使用的是 32 位系統,爲了性能考慮使用低位存儲變量的類型信息,000 開頭代表是對象然而 null 表示爲全零,所以將它錯誤的判斷爲 object 。

15. 詳細說下instanceof

instanceof它主要是用於檢測某個構造函數的原型對象在不在某個對象的原型鏈上

算了,直接手寫實現吧:

function myInstanceof (left, right) {
  let proto = Object.getPrototypeOf(left);
  while (true) {
    if (proto === null) return false;
    if (proto === right.prototype) return true;
    proto = Object.getPrototypeOf(proto)
  }
}

16. 一句話描述一下this

指向最後調用函數的那個對象,是函數運行時內部自動生成的一個內部對象,只能在函數內部使用

17. 函數內的this是在什麼時候確定的?

函數調用時,指向最後調用的那個對象

18. apply/call/bind的相同和不同

19. webpack中的loader和plugin有什麼區別

(答案參考童歐巴的一篇webpack面試文章哦:「吐血整理」再來一打Webpack面試題(持續更新))

loader它是一個轉換器,只專注於轉換文件這一個領域,完成壓縮、打包、語言編譯,它僅僅是爲了打包。並且運行在打包之前。

而plugin是一個擴展器,它豐富了webpack本身,爲其進行一些其它功能的擴展。它不侷限於打包,資源的加載,還有其它的功能。所以它是在整個編譯週期都起作用。

20. HTTP和TCP的不同

HTTP的責任是去定義數據,在兩臺計算機相互傳遞信息時,HTTP規定了每段數據以什麼形式表達纔是能夠被另外一臺計算機理解。

而TCP所要規定的是數據應該怎麼傳輸才能穩定且高效的傳遞與計算機之間。

(還可以再擴展)

21. TCP和UDP的區別

  1. TCP是一個面向連接的、可靠的、基於字節流的傳輸層協議。
  2. UDP是一個面向無連接的傳輸層協議。

TCP爲什麼可靠,是因爲它有三次握手來保證雙方都有接受和發送數據的能力。

字節流服務:將大塊數據分割爲以報文段爲單位的數據包進行管理

22. 介紹一下虛擬DOM

虛擬DOM本質就是用一個原生的JavaScript對象去描述一個DOM節點。是對真實DOM的一層抽象。

由於在瀏覽器中操作DOM是很昂貴的。頻繁的操作DOM,會產生一定的性能問題,因此我們需要這一層抽象,在patch過程中儘可能地一次性將差異更新到DOM中,這樣保證了DOM不會出現性能很差的情況。

另外還有很重要的一點,也是它的設計初衷,爲了更好的跨平臺,比如Node.js就沒有DOM,如果想實現SSR(服務端渲染),那麼一個方式就是藉助Virtual DOM,因爲Virtual DOM本身是JavaScript對象。

Vue2.x中的虛擬DOM主要是借鑑了snabbdom.jsVue3中借鑑inferno.js算法進行優化。

23. 盒模型

24. 輸入URL到頁面的呈現

看三元的《(1.6w字)瀏覽器靈魂之問,請問你能接得住幾個?》

分別從網絡,解析,渲染來說

面試的問題基本都答出來了,當然後面還有一個技術總監的電話面,主要是問了一些工作相關的問題。

其實這家公司開出的條件也挺讓呆呆心動的,包括氛圍感覺也挺好,只不過可能還不是自己想要的吧,所以最終也是沒去,挺可惜的…如果面試我的那位小哥哥哥看到了這裏,還請不要難過哈,我們江湖會再見的😂。

深圳某房地產公司

4月27日

一面

5道筆試題

並詳細說一下前面三道

二面

1. JSON的原理以及手寫一個實現

基本原理:主要就是利用 script 標籤的src屬性沒有跨域的限制,通過指向一個需要訪問的地址,由服務端返回一個預先定義好的 Javascript 函數的調用,並且將服務器數據以該函數參數的形式傳遞過來,此方法需要前後端配合完成。

執行過程:

  • 前端定義一個解析函數(如: jsonpCallback = function (res) {})
  • 通過params的形式包裝script標籤的請求參數,並且聲明執行函數(如cb=jsonpCallback)
  • 後端獲取到前端聲明的執行函數(jsonpCallback),並以帶上參數且調用執行函數的方式傳遞給前端
  • 前端在script標籤返回資源的時候就會去執行jsonpCallback並通過回調函數的方式拿到數據了。

缺點:

  • 只能進行GET請求

優點:

  • 兼容性好,在一些古老的瀏覽器中都可以運行

代碼實現:

(具體可以看我的這篇文章:JSONP原理及實現

<script>
    function JSONP({
        url,
        params = {},
        callbackKey = 'cb',
        callback
    }) {
        // 定義本地的唯一callbackId,若是沒有的話則初始化爲1
        JSONP.callbackId = JSONP.callbackId || 1;
        let callbackId = JSONP.callbackId;
        // 把要執行的回調加入到JSON對象中,避免污染window
        JSONP.callbacks = JSONP.callbacks || [];
        JSONP.callbacks[callbackId] = callback;
        // 把這個名稱加入到參數中: 'cb=JSONP.callbacks[1]'
        params[callbackKey] = `JSONP.callbacks[${callbackId}]`;
        // 得到'id=1&cb=JSONP.callbacks[1]'
        const paramString = Object.keys(params).map(key => {
            return `${key}=${encodeURIComponent(params[key])}`
        }).join('&')
        // 創建 script 標籤
        const script = document.createElement('script');
        script.setAttribute('src', `${url}?${paramString}`);
        document.body.appendChild(script);
        // id自增,保證唯一
        JSONP.callbackId++;

    }
    JSONP({
        url: 'http://localhost:8080/api/jsonps',
        params: {
            a: '2&b=3',
            b: '4'
        },
        callbackKey: 'cb',
        callback (res) {
            console.log(res)
        }
    })
    JSONP({
        url: 'http://localhost:8080/api/jsonp',
        params: {
            id: 1
        },
        callbackKey: 'cb',
        callback (res) {
            console.log(res)
        }
    })
</script>

2. 瀏覽器爲什麼要跨域?如果是因爲安全的話那小程序或者其他的爲什麼沒有跨域?

跨域的產生來源於現代瀏覽器所通用的同源策略,所謂同源策略,是指只有在地址的:

  1. 協議名
  2. 域名
  3. 端口名

均一樣的情況下,才允許訪問相同的cookie、localStorage,以及訪問頁面的DOM或是發送Ajax請求。若在不同源的情況下訪問,就稱爲跨域。

例如以下爲同源:

http://www.example.com:8080/index.html
http://www.example.com:8080/home.html

以下爲跨域:

http://www.example.com:8080/index.html
http://www3.example.com:8080/index.html

注意⚠️:

但是有兩種情況:http默認的端口號爲80https默認端口號爲443

所以:

http://www.example.com:80 === http://www.example.com
https://www.example.com:443 === https://www.example.com

爲什麼瀏覽器會禁止跨域?

簡答

首先,跨域只存在於瀏覽器端,因爲我們知道瀏覽器的形態是很開放的,所以我們需要對它有所限制。

其次,同源策略主要是爲了保證用戶信息的安全,可分爲兩種:Ajax同源策略和DOM同源策略。

Ajax同源策略主要是使得不同源的頁面不能獲取cookie且不能發起Ajax請求,這樣在一定層度上防止了CSRF攻擊。

DOM同源策略也一樣,它限制了不同源頁面不能獲取DOM,這樣可以防止一些惡意網站在自己的網站中利用iframe嵌入正規的網站並迷惑用戶,以此來達到竊取用戶信息。

深答

  • 首先,跨域只存在於瀏覽器端。瀏覽器它爲web提供了訪問入口,並且訪問的方式很簡單,在地址欄輸入要訪問的地址或者點擊某個鏈接就可以了,正是這種開放的形態,所以我們需要對它有所限制。

  • 所以同源策略它的產生主要是爲了保證用戶信息的安全,防止惡意的網站竊取數據。分爲兩種:Ajax同源策略與DOM同源策略:

    • Ajax同源策略它主要做了這兩種限制:1.不同源頁面不能獲取cookie;2.不同源頁面不能發起Ajax請求。我認爲它是防止CSRF攻擊的一種方式吧。因爲我們知道cookie這個東西它主要是爲了解決瀏覽器與服務器會話狀態的問題,它本質上是存儲在瀏覽器或本地文件中一個小小的文本文件,那麼它裏面一般都會存儲了用戶的一些信息,包括隱私信息。如果沒有Ajax同源策略,惡意網站只需要一段腳本就可以獲取你的cookie,從而冒充你的身份去給其它網站發送惡意的請求。
    • DOM同源策略也一樣,它限制了不同源頁面不能獲取DOM。例如一個假的網站利用iframe嵌套了一個銀行網站mybank.com,並把寬高或者其它部分調整的和原銀行網站一樣,僅僅只是地址欄上的域名不同,若是用戶沒有注意的話就以爲這個是個真的網站。如果這時候用戶在裏面輸入了賬號密碼,如果沒有同源策略,那麼這個惡意網站就可以獲取到銀行網站中的DOM,也就能拿到用戶的輸入內容以此來達到竊取用戶信息的攻擊。

    同源策略它算是瀏覽器安全的第一層屏障吧,因爲就像CSRF攻擊,它只能限制不同源頁面cookie的獲取,但是攻擊者還可能通過其它的方式來達到攻擊效果。

    (注,上面提到的iframe限制DOM查詢,案例如下)

    // HTML
    <iframe name="yinhang" src="www.yinhang.com"></iframe>
    // JS
    // 由於沒有同源策略的限制,釣魚網站可以直接拿到別的網站的Dom
    const iframe = window.frames['yinhang']
    const node = iframe.document.getElementById('你輸入賬號密碼的Input')
    console.log(`拿到了這個${node},我還拿不到你剛剛輸入的賬號密碼嗎`)
    

參考:

  • https://segmentfault.com/a/1190000015597029
  • https://juejin.im/post/5cad99796fb9a068ab40a29a

3. CORS跨域的原理

跨域資源共享(CORS)是一種機制,是W3C標準。它允許瀏覽器向跨源服務器,發出XMLHttpRequestFetch請求。並且整個CORS通信過程都是瀏覽器自動完成的,不需要用戶參與。

而使用這種跨域資源共享的前提是,瀏覽器必須支持這個功能,並且服務器端也必須同意這種"跨域"請求。因此實現CORS的關鍵是服務器需要服務器。通常是有以下幾個配置:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers
  • Access-Control-Allow-Credentials
  • Access-Control-Max-Age

具體可看:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS#Preflighted_requests

過程分析:

簡單回答

  • 當我們發起跨域請求時,如果是非簡單請求,瀏覽器會幫我們自動觸發預檢請求,也就是 OPTIONS 請求,用於確認目標資源是否支持跨域。如果是簡單請求,則不會觸發預檢,直接發出正常請求。

  • 瀏覽器會根據服務端響應的 header 自動處理剩餘的請求,如果響應支持跨域,則繼續發出正常請求,如果不支持,則在控制檯顯示錯誤。

詳細回答

  • 瀏覽器先根據同源策略對前端頁面和後臺交互地址做匹配,若同源,則直接發送數據請求;若不同源,則發送跨域請求。

  • 服務器收到瀏覽器跨域請求後,根據自身配置返回對應文件頭。若未配置過任何允許跨域,則文件頭裏不包含 Access-Control-Allow-origin 字段,若配置過域名,則返回 Access-Control-Allow-origin + 對應配置規則裏的域名的方式

  • 瀏覽器根據接受到的 響應頭裏的 Access-Control-Allow-origin 字段做匹配,若無該字段,說明不允許跨域,從而拋出一個錯誤;若有該字段,則對字段內容和當前域名做比對,如果同源,則說明可以跨域,瀏覽器接受該響應;若不同源,則說明該域名不可跨域,瀏覽器不接受該響應,並拋出一個錯誤。

CORS中有簡單請求非簡單請求,簡單請求是不會觸發CORS的預檢請求的,而非簡單請求會。

“需預檢的請求”要求必須首先使用 OPTIONS 方法發起一個預檢請求到服務器,以獲知服務器是否允許該實際請求。"預檢請求“的使用,可以避免跨域請求對服務器的用戶數據產生未預期的影響。

(關於更多CORS的內容可以看我的另一篇文章:CORS原理及實現

4. CORS預請求OPTIONS就一定是安全的嗎?

5. 在深圳的網頁上輸入百度,是怎麼把這個請求發到北京的

這個當時面試官和我說的是,中間會經過很多的站點,比如會經過湖南,或者其它城市,由各個城市的這些站點一層一層分發下去。

6. 輸入URL到頁面的呈現

7. Vue的響應式原理

8. 那在這個響應式中一個數據改變它是怎麼通知要更新的,也就是如何把數據和頁面關聯起來?

面的最慘的一次…因爲這次面試是當天下午6點纔去面的,在這之前呆呆已經經過了3輪面試的折磨,所以身心疲憊很不在狀態。當然最主要的是自己確實準備的還不夠充分,其實現在回過頭來看看這些題都不太難的…

當天也小小的自閉了一下,整理好狀態第二天好好總結吧 😄。

深圳某海外直播公司

4月28日

(當時是電話面,一個小時20分鐘,問了我大概五六十道題,我能想到的一共是50題,還有一些記不起來了)

1. CommonJS和ES6模塊的區別

  • CommonJS模塊是運行時加載,ES6 Modules是編譯時輸出接口
  • CommonJS輸出是值的拷貝;ES6 Modules輸出的是值的引用,被輸出模塊的內部的改變會影響引用的改變
  • CommonJs導入的模塊路徑可以是一個表達式,因爲它使用的是require()方法;而ES6 Modules只能是字符串
  • CommonJS this指向當前模塊,ES6 Modules this指向undefined
  • 且ES6 Modules中沒有這些頂層變量:argumentsrequiremoduleexports__filename__dirname

關於第一個差異,是因爲CommonJS 加載的是一個對象(即module.exports屬性),該對象只有在腳本運行完纔會生成。而 ES6 模塊不是對象,它的對外接口只是一種靜態定義,在代碼靜態解析階段就會生成。

(具體可以看我的這篇文章:https://juejin.im/post/5eaacd175188256d4345ea3a)

2. 模塊的異步加載

模塊的異步加載可以使用AMD或者CMD規範。

(具體可以看我的這篇文章:https://juejin.im/post/5eaacd175188256d4345ea3a)

3. 開發一個模塊要考慮哪些問題?

封閉開放式原則、安全性

(應該還有,但是沒想到)

4. 實現一個一組異步請求按順序執行你有哪些方法?

  1. 利用reduce,初始值傳入一個Promise.resolve(),之後往裏面不停的疊加.then()。(類似於這裏https://juejin.im/post/5e58c618e51d4526ed66b5cf#heading-51)
  2. 利用forEach,本質和reduce原理相同。(類似於這裏https://juejin.im/post/5e58c618e51d4526ed66b5cf#heading-53)
  3. 還可以用ES9中的for...await...of來實現。

5. Promise.all()是併發的還是串行的?

併發的。不過Promise.all().then()結果中數組的順序和Promise.all()接收到的數組順序一致。

6. 平時寫過哪些正則表達式

  • 之前有用過用正則去除輸入框的首尾空格,正則表達式爲:var trimReg = /(^\s+)|(\s+$)/g;不過後來由於Vue中有一個修飾符.trim,使用起來更方便(如v-model.trim="msg")就用這種方式多一些;再或者也可以用ES10新出的trimStarttrimEnd來去除首尾空格。
  • 用於校驗手機號的正則:var phoneReg = /^1[3456789]\d{9}$/g
  • 用正則寫一個根據name獲取cookie中的值的方法:
function getCookie(name) {
  var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]*)'));
  if (match) return unescape(match[2]);
}

(詳細介紹可以看這裏:每日一題-JS篇-根據name獲取cookie中值的方法

7. 正則裏的非如何實現的

^要是放在[]裏的話就表示"除了^後面的內容都能匹配",也就是非的意思。

例如:

(除了l,其它都變成了"帥")

var str = 'lindaidai';
console.log(str.replace(/[^l]/g, '帥'));
// l帥帥帥帥帥帥帥帥

反之,如果是不在[]裏的話則表示開頭匹配:

(只有l變成了"帥")

var str = 'lindaidai';
console.log(str.replace(/^l/g, '帥'));

8. webpack幾種hash的實現原理

  • hash是跟整個項目的構建相關,只要項目裏有文件更改,整個項目構建的hash值都會更改,並且全部文件都共用相同的hash值。(粒度整個項目)
  • chunkhash是根據不同的入口進行依賴文件解析,構建對應的chunk(模塊),生成對應的hash值。只有被修改的chunk(模塊)在重新構建之後纔會生成新的hash值,不會影響其它的chunk。(粒度entry的每個入口文件)
  • contenthash是跟每個生成的文件有關,每個文件都有一個唯一的hash值。當要構建的文件內容發生改變時,就會生成新的hash值,且該文件的改變並不會影響和它同一個模塊下的其它文件。(粒度每個文件的內容)

(具體可以看我簡書上的這篇文章:https://www.jianshu.com/p/486453d81088)

這裏只是說明了三種hash的不同…至於原理暫時沒了解。

9. webpack如果使用了hash命名,那是每次都會重寫生成hash嗎

這個問題在上一個問題中已經說明了,要看webpack的配置。

有三種情況:

  • 如果是hash的話,是和整個項目有關的,有一處文件發生更改則所有文件的hash值都會發生改變且它們共用一個hash值;
  • 如果是chunkhash的話,只和entry的每個入口文件有關,也就是同一個chunk下的文件有所改動該chunk下的文件的hash值就會發生改變
  • 如果是contenthash的話,和每個生成的文件有關,只有當要構建的文件內容發生改變時纔會給該文件生成新的hash值,並不會影響其它文件。

10. webpack中如何處理圖片的?

webpack中有兩種處理圖片的loader

  • file-loader:解決CSS等中引入圖片的路徑問題;(解決通過url,import/require()等引入圖片的問題)
  • url-loader:當圖片小於設置的limit參數值時,url-loader將圖片進行base64編碼(當項目中有很多圖片,通過url-loader進行base64編碼後會減少http請求數量,提高性能),大於limit參數值,則使用file-loader拷貝圖片並輸出到編譯目錄中;

(詳細使用可以查看這裏:霖呆呆的webpack之路-loader篇

11. 說一下回流和重繪

迴流

觸發條件:

當我們對 DOM 結構的修改引發 DOM 幾何尺寸變化的時候,會發生迴流的過程。

例如以下操作會觸發迴流:

  1. 一個 DOM 元素的幾何屬性變化,常見的幾何屬性有widthheightpaddingmarginlefttopborder 等等, 這個很好理解。

  2. 使 DOM 節點發生增減或者移動

  3. 讀寫 offset族、scroll族和client族屬性的時候,瀏覽器爲了獲取這些值,需要進行迴流操作。

  4. 調用 window.getComputedStyle 方法。

迴流過程:由於DOM的結構發生了改變,所以需要從生成DOM這一步開始,重新經過樣式計算生成佈局樹建立圖層樹、再到生成繪製列表以及之後的顯示器顯示這整一個渲染過程走一遍,開銷是非常大的。

重繪

觸發條件:

當 DOM 的修改導致了樣式的變化,並且沒有影響幾何屬性的時候,會導致重繪(repaint)。

重繪過程:由於沒有導致 DOM 幾何屬性的變化,因此元素的位置信息不需要更新,所以當發生重繪的時候,會跳過生存佈局樹建立圖層樹的階段,直接到生成繪製列表,然後繼續進行分塊、生成位圖等後面一系列操作。

如何避免觸發迴流和重繪

  1. 避免頻繁使用 style,而是採用修改class的方式。
  2. 將動畫效果應用到position屬性爲absolutefixed的元素上。
  3. 也可以先爲元素設置display: none,操作結束後再把它顯示出來。因爲在display屬性爲none的元素上進行的DOM操作不會引發迴流和重繪
  4. 使用createDocumentFragment進行批量的 DOM 操作。
  5. 對於 resize、scroll 等進行防抖/節流處理。
  6. 避免頻繁讀取會引發迴流/重繪的屬性,如果確實需要多次使用,就用一個變量緩存起來。
  7. 利用 CSS3 的transformopacityfilter這些屬性可以實現合成的效果,也就是CPU加速。

參考來源:https://juejin.im/post/5df5bcea6fb9a016091def69#heading-57

12. 盒模型及如何轉換

box-sizing: content-box(W3C盒模型,又名標準盒模型):元素的寬高大小表現爲內容的大小。

box-sizing: border-box(IE盒模型,又名怪異盒模型):元素的寬高表現爲內容 + 內邊距 + 邊框的大小。背景會延伸到邊框的外沿。

13. 實現水平垂直居中的幾種方式

這裏我是按照子弈的總結答的:
https://juejin.im/post/5d690c726fb9a06b155dd40d#heading-81

  • Flex佈局(子元素是塊級元素)
.box {
  display: flex;
  width: 100px;
  height: 100px;
  background-color: pink;
}

.box-center{
  margin: auto;
  background-color: greenyellow;
}
  • Flex佈局
.box {
  display: flex;
  width: 100px;
  height: 100px;
  background-color: pink;
  justify-content: center;
  align-items: center;
}

.box-center{
  background-color: greenyellow;
}
  • 絕對定位實現(定位元素定寬定高)
.box {
  position: relative;
  height: 100px;
  width: 100px;
  background-color: pink;
}

.box-center{
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  top: 0;
  margin: auto;
  width: 50px;
  height: 50px;
  background-color: greenyellow;
}

14. flex的兼容性怎樣

簡單回答:

IE6~9不支持,IE10~11部分支持flex的2012版,但是需要-ms-前綴。

其它的主流瀏覽器包括安卓和IOS基本上都支持了。

詳細回答:

  • IE10部分支持2012,需要-ms-前綴
  • Android4.1/4.2-4.3部分支持2009 ,需要-webkit-前綴
  • Safari7/7.1/8部分支持2012, 需要-webkit-前綴
  • IOS Safari7.0-7.1/8.1-8.3部分支持2012,需要-webkit-前綴

15. 你知道到哪裏查看兼容性嗎

可以到Can I use上去查看,官網地址爲:https://caniuse.com/

16. 移動端中css你是使用什麼單位

比較常用的

  • em:定義字體大小時以父級的字體大小爲基準;定義長度單位時以當前字體大小爲基準。例父級font-size: 14px,則子級font-size: 1em;font-size: 14px;;若定義長度時,子級的字體大小如果爲14px,則子級width: 2em;width: 24px
  • rem:以根元素的字體大小爲基準。例如htmlfont-size: 14px,則子級1rem = 14px
  • %:以父級的寬度爲基準。例父級width: 200px,則子級width: 50%;height:50%;width: 100px;height: 100px;
  • vw和vh:基於視口的寬度和高度(視口不包括瀏覽器的地址欄工具欄和狀態欄)。例如視口寬度爲1000px,則60vw = 600px;
  • vmin和vmaxvmin爲當前vwvh中較小的一個值;vmax爲較大的一個值。例如視口寬度375px,視口高度812px,則100vmin = 375px;100vmax = 812px;

不常用的:

  • ex和chex以字符"x"的高度爲基準;例如1ex表示和字符"x"一樣長。ch以數字"0"的寬度爲基準;例如2ch表示和2個數字"0"一樣長。

移動端佈局總結

  1. 移動端佈局的方式主要使用rem和flex,可以結合各自的優點,比如flex佈局很靈活,但是字體的大小不好控制,我們可以使用rem和媒體查詢控制字體的大小,媒體查詢視口的大小,然後不同的上視口大小下設置設置html的font-size。
  2. 可單獨製作移動端頁面也可響應式pc端移動端共用一個頁面。沒有好壞,視情況而定,因勢利導

(總結來源:玲瓏)

17. rem和em的區別

em:

定義字體大小時以父級的字體大小爲基準;定義長度單位時以當前字體大小爲基準。例父級font-size: 14px,則子級font-size: 1em;font-size: 14px;;若定義長度時,子級的字體大小如果爲14px,則子級width: 2em;width: 24px

rem:

以根元素的字體大小爲基準。例如htmlfont-size: 14px,則子級1rem = 14px

18. 在移動端中怎樣初始化根元素的字體大小

一個簡易版的初始化根元素字體大小。

頁面開頭處引入下面這段代碼,用於動態計算font-size

(假設你需要的1rem = 20px)

(function () {
  var html = document.documentElement;
  function onWindowResize() {
    html.style.fontSize = html.getBoundingClientRect().width / 20 + 'px';
  }
  window.addEventListener('resize', onWindowResize);
  onWindowResize();
})();
  • document.documentElement:獲取document的根元素
  • html.getBoundingClientRect().width:獲取html的寬度(窗口的寬度)
  • 監聽windowresize事件

一般還需要配合一個meta頭:

<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-sacle=1.0, maximum-scale=1.0, user-scalable=no" />

19. 移動端中不同手機html默認的字體大小都是一樣的嗎

如果沒有人爲取改變根元素字體大小的話,默認是1rem = 16px;根元素默認的字體大小是16px

20. 你做過哪些動畫效果

實話實說沒太做過。

21. 如果讓你實現一個一直旋轉的動畫你會如何做

css代碼:

<style>
  .box {
    width: 100px;
    height: 100px;
    background-color: red;
    animation: spin 2s linear infinite;
  }
  @keyframes spin {
    from { transform: rotate(0deg) }
    to { transform: rotate(360deg) }
  }
</style>

html代碼:

<div class="box"></div>

22. animation介紹一下

語法:

animation: name duration timing-function delay iteration-count direction;
描述
animation-name 規定需要綁定到選擇器的 keyframe 名稱。(mymove)
animation-duration 規定完成動畫所花費的時間,以秒或毫秒計。(2s)
animation-timing-function 規定動畫的速度曲線。(ease|linear|ease-in|cubic-bezier(n,n,n,n))
animation-delay 規定在動畫開始之前的延遲。(2s)
animation-iteration-count 規定動畫應該播放的次數。(n | infinite) n次/無限
animation-direction 規定是否應該輪流反向播放動畫。(normal | alternate) 正常/反向

23. animation有一個steps()功能符知道嗎?

一句話介紹:steps()功能符可以讓動畫不連續。

地位和作用:和貝塞爾曲線(cubic-bezier()修飾符)一樣,都可以作爲animation的第三個屬性值。

和貝塞爾曲線的區別:貝塞爾曲線像是滑梯且有4個關鍵字(參數),而steps像是樓梯坡道且只有numberposition兩個關鍵字。

語法:

steps(number, position)
  • number: 數值,表示把動畫分成了多少段
  • position: 表示動畫是從時間段的開頭連續還是末尾連續。支持startend兩個關鍵字,含義分別如下:
    • start:表示直接開始。
    • end:表示戛然而止。是默認值。

具體可以看這裏:https://www.zhangxinxu.com/wordpress/2018/06/css3-animation-steps-step-start-end/

24. 用過哪些移動端的調試工具

  • Chrome瀏覽器 -> more tools -> Remote devices -> chrome://inspect/#devices
  • Mac + IOS + Safari

25. 說一下原型鏈

26. 詳細說一下instanceof

27. V8的垃圾回收是發生在什麼時候?

V8引擎幫助我們實現了自動的垃圾回收管理,利用瀏覽器渲染頁面的空閒時間進行垃圾回收。

28. 具體說一下垃圾回收機制

(這裏我用的是:https://juejin.im/post/5e8b261ae51d4546c0382ab4#heading-20 裏的總結)

棧內存的回收:

棧內存調用棧上下文切換後就被回收,比較簡單。

堆內存的回收:

V8的堆內存分爲新生代內存和老生代內存,新生代內存是臨時分配的內存,存在時間短,老生代內存存在時間長。

新生代內存回收機制:

  • 新生代內存容量小,64位系統下僅有32M。新生代內存分爲From、To兩部分,進行垃圾回收時,先掃描From,將非存活對象回收,將存活對象順序複製到To中,之後調換From/To,等待下一次回收

老生代內存回收機制

  • 晉升:如果新生代的變量經過多次回收依然存在,那麼就會被放入老生代內存中
  • 標記清除:老生代內存會先遍歷所有對象並打上標記,然後對正在使用或被強引用的對象取消標記,回收被標記的對象
  • 整理內存碎片:把對象挪到內存的一端

(當然想要詳細瞭解的話也可以看我的這篇文章:JavaScript進階-內存機制(表情包初探)

29. 在項目中如何把http的請求換成https

由於我在項目中是會對axios做一層封裝,所以每次請求的域名也是寫在配置文件中,有一個baseURL字段專門用於存儲它,所以只要改這個字段就可以達到替換所有請求httphttps了。

當然後面我也有瞭解到:

利用meta標籤把http請求換爲https:

<meta http-equiv ="Content-Security-Policy" content="upgrade-insecure-requests">

30. 知道meta標籤有把http換成https的功能嗎?

參考上一題👆。

31. http請求可以怎麼攔截

在瀏覽器和服務器進行傳輸的時候,可以被nginx代理所攔截,也可以被網關攔截。

32. https的加密方式

HTTPS主要是採用對稱密鑰加密和非對稱密鑰加密組合而成的混合加密機制進行傳輸。

也就是發送密文的一方用"對方的公鑰"進行加密處理"對稱的密鑰",然後對方在收到之後使用自己的私鑰進行解密得到"對稱的密鑰",這在確保雙發交換的密鑰是安全的前提下使用對稱密鑰方式進行通信。

33. 混合加密的好處

對稱密鑰加密和非對稱密鑰加密都有它們各種的優缺點,而混合加密機制就是將兩者結合利用它們各自的優點來進行加密傳輸。

比如既然對稱密鑰的優點是加解密效率快,那麼在客戶端與服務端確定了連接之後就可以用它來進行加密傳輸。不過前提是得解決雙方都能安全的拿到這把對稱密鑰。這時候就可以裏用非對稱密鑰加密來傳輸這把對稱密鑰,因爲我們知道非對稱密鑰加密的優點就是能保證傳輸的內容是安全的。

所以它的好處是即保證了對稱密鑰能在雙方之間安全的傳輸,又能使用對稱加密方式進行通信,這比單純的使用非對稱加密通信快了很多。以此來解決了HTTP中內容可能被竊聽的問題。

其它HTTP相關的問題:

如:

  • HTTPS的工作流程

  • 混合加密機制的好處

  • 數字簽名

  • ECDHE握手和RSA握手

  • 向前安全性

這些問題都可以看到我的這篇文章:HTTPS面試問答

34. 瀏覽器如何驗證服務器的身份

這道題主要可以從數字簽名數字證書上來答。

當時我答的時候就把證書的頒發流程HTTPS數字證書的驗證過程完整的說了一遍就算過了。

具體可以看HTTPS面試問答中的第5、6、7問。

35. ETag首部字段說一下

這個無非就是配合If-None-Match來達到一個協商緩存的作用。值爲服務器某個資源的唯一標識。

具體可以看我的這篇文章:霖呆呆你來說說瀏覽器緩存吧

36. 你們的token一般是存放在哪裏的

Token其實就是訪問資源的憑證

一般是用戶通過用戶名和密碼登錄成功之後,服務器將登陸憑證做數字簽名,加密之後得到的字符串作爲token

它在用戶登錄成功之後會返回給客戶端,客戶端主要有這麼幾種存儲方式:

  1. 存儲在localStorage中,每次調用接口的時候都把它當成一個字段傳給後臺
  2. 存儲在cookie中,讓它自動發送,不過缺點就是不能跨域
  3. 拿到之後存儲在localStorage中,每次調用接口的時候放在HTTP請求頭的Authorization字段裏

(很明顯我答的不夠專業,歡迎補充,感謝😊)

37. token會不會被僞造?

(很明顯我答的不夠專業,歡迎補充,感謝😊)

38. redis中一般用來存什麼

用戶登錄成功之後的一些信息

(很明顯我答的不夠專業,歡迎補充,感謝😊)

39. 前後端如何驗證一個用戶是否下線了

(很明顯我答的不夠專業,歡迎補充,感謝😊)

40. CSP白名單知道嗎?

(很明顯我答的不夠專業,歡迎補充,感謝😊)

41. nginx有配置過嗎?

只會配置一些跨域方面的問題。

events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass  http://localhost:8887;
            add_header  Access-Control-Allow-Origin *;
        }
    }
}

利用ngnix跨域的關鍵就是在配置文件中設置server項,然後設置其中的location屬性,proxy_pass:需要代理的服務器地址,add_header:給響應報文中添加首部字段,例如Access-Control-Allow-Origin設置爲*,即允許所有的請求源請求。

具體可以看:Yiming君-面試題:nginx有配置過嗎?反向代理知道嗎?

42. 反向代理知道嗎?

我們將請求發送到服務器,然後服務器對我們的請求進行轉發,我們只需要和代理服務器進行通信就好。所以對於客戶端來說,是感知不到服務器的。

43. 有用過抓包工具嗎?

唔…沒有…

44. 你平常用的電腦是Mac嗎?

(…默默的點頭)

45. Fiddler有用過嗎?

唔…知道…

46. Vue的diff算法

這裏我是按照:

https://mp.weixin.qq.com/s/2xyP4jVevuXrGov_VsWfvA

中的第13題答的。

47. Vue中computed和methods的區別

48. 例如要獲取當前時間你會放到computed還是methods裏?

放在methods中,因爲computed會有惰性,並不能知道new Date()的改變。

49. 你們的權限功能是怎麼做的?

小小的寫了一篇文章,可以看這裏:數據權限如何控制

50. 那你在判斷權限的時候是用的字符串匹配還是位運算?

和面試官扯了一堆我數據權限判斷的具體過程,其中可能有多個權限:並的情況000011110001&000011110002,或的情況000011110001|000011110002,以及如何做的權限匹配。最後面試官:

"所以那還是用的字符串匹配咯?"

尬…我比較low…用的字符串匹配…

(哇,真的絕了…1個小時20分鐘50多道題,答的我口渴😂,不過也可以看出有很多移動端的我都沒有答上來,面試官也表示理解,畢竟我主要是以PC端爲主,所以竟然也算是過了,很感謝這位面試官細心的幫我分析一些問題)

後來有了解這位面試官近期也跳槽去騰訊了,果然面完呆呆之後他就去大廠了,好人一生平安😂🙏。

深圳某國內直播公司

這家公司其實是上家公司的總部,因爲面完上家之後,HR也知道我的顧慮,想要去一個大點的團隊,所以就把我推薦去了他們的總部。非常Nice的HR小姐姐,好感動,就算你看不到我的文章,我也還是要感謝你 😂。

5月8日

一面(前端副總監)

1. 輸入URL到頁面呈現

(必問…)

看三元的《(1.6w字)瀏覽器靈魂之問,請問你能接得住幾個?》

分別從網絡,解析,渲染來說

2. 爲什麼說script標籤建議放在body下面?

JS代碼在加載完之後是立即執行的,且JS代碼執行時會阻塞頁面的渲染。

3. 爲什麼說script標籤會阻塞頁面的渲染呢?渲染線程和js引擎線程不是分開的嗎?

JS屬於單線程,當我們在加載script標籤內容的時候,渲染線程會被暫停,因爲script標籤裏可能會操作DOM的,所以如果你加載script標籤又同時渲染頁面肯定就衝突了,因此說渲染線程(GUI)和js引擎線程互斥。

4. 協商緩存說一下

Last-Modefied配合If-Modified-Since

ETag配合If-None-Match

也是個常見的問題了,不瞭解的小夥伴可以看我的這篇文章:霖呆呆你來說說瀏覽器緩存吧

(當時面試官還重複了一下我說的這4個頭部字段,自己回顧了一下我說的對不對,好可愛~)

5. HTTP中的Keep-Alive有了解過嗎?

Keep-AliveHTTP的一個頭部字段Connection中的一個值,它是保證我們的HTTP請求能建立一個持久連接。也就是說建立一次TCP連接即可進行多次請求和響應的交互。它的特點就是只要有一方沒有明確的提出斷開連接,則保持TCP連接狀態,減少了TCP連接和斷開照成的額外開銷。

另外,在HTTP/1.1中所有的連接默認都是持久連接的,但是HTTP/1.0並未標準化。

6. 跨域有了解嗎?如何解決跨域?

我工作中碰到主要是利用CORS來解決跨域問題,說了一下它的原理以及後臺需要如何做。

另外說到了JSONP的原理,以及它的優點:兼容性好;缺點:只能進行GET請求,且有安全問題。

還有說到了ngnix反向代理來解決跨域。

其它的,我當時說我有看過一篇文章裏面詳細的介紹10多種跨域解決方案,但是自己沒有過多的去了解。

哈哈,其實也就是秋風大大的這篇文章10種跨域解決方案(附終極大招)

7. WebSocket有了解過嗎?它也可以跨域的

這個當時答的沒用過。

我知道它是能使得客戶端和服務器之間存在持久的連接,而且雙方都可以隨時開始發送數據,這種方式本質沒有使用 HTTP 的響應頭,因此也沒有跨域的限制。

(多的不會了)

8. 前端安全方面?XSS?CSRF?

(必問…)

(以下回答參考子弈小哥哥的面試分享:兩年工作經驗成功面試阿里P6總結

以及蔡徐坤小哥哥的2萬字 | 前端基礎拾遺90問)

XSS

XSS(Cross Site Script)跨站腳本攻擊。指的是攻擊者向網頁注入惡意的客戶端代碼,通過惡意的腳本對客戶端網頁進行篡改,從而在用戶瀏覽網頁時,對用戶瀏覽器進行控制或者獲取用戶隱私數據的一種攻擊方式。

主要是分爲三種:

存儲型:即攻擊被存儲在服務端,常見的是在評論區插入攻擊腳本,如果腳本被儲存到服務端,那麼所有看見對應評論的用戶都會受到攻擊。

反射型:攻擊者將腳本混在URL裏,服務端接收到URL將惡意代碼當做參數取出並拼接在HTML裏返回,瀏覽器解析此HTML後即執行惡意代碼

DOM型:將攻擊腳本寫在URL中,誘導用戶點擊該URL,如果URL被解析,那麼攻擊腳本就會被運行。和前兩者的差別主要在於DOM型攻擊不經過服務端

如何防禦XSS攻擊

  • 輸入檢查:對輸入內容中的script<iframe>等標籤進行轉義或者過濾
  • 設置httpOnly:很多XSS攻擊目標都是竊取用戶cookie僞造身份認證,設置此屬性可防止JS獲取cookie
  • 開啓CSP,即開啓白名單,可阻止白名單以外的資源加載和運行

CSRF

CSRF攻擊(Cross-site request forgery)跨站請求僞造。是一種劫持受信任用戶向服務器發送非預期請求的攻擊方式,通常情況下,它是攻擊者藉助受害者的 Cookie 騙取服務器的信任,但是它並不能拿到Cookie,也看不到Cookie的內容,它能做的就是給服務器發送請求,然後執行請求中所描述的命令,以此來改變服務器中的數據,也就是並不能竊取服務器中的數據。

防禦主要有三種:

驗證Token:瀏覽器請求服務器時,服務器返回一個token,每個請求都需要同時帶上token和cookie纔會被認爲是合法請求

驗證Referer:通過驗證請求頭的Referer來驗證來源站點,但請求頭很容易僞造

設置SameSite:設置cookie的SameSite,可以讓cookie不隨跨站請求發出,但瀏覽器兼容不一

點擊挾持

  • 誘使用戶點擊看似無害的按鈕(實則點擊了透明 iframe 中的按鈕).
  • 監聽鼠標移動事件,讓危險按鈕始終在鼠標下方.
  • 使用 HTML5 拖拽技術執行敏感操作(例如 deploy key).

預防策略:

  1. 服務端添加 X-Frame-Options 響應頭,這個 HTTP 響應頭是爲了防禦用 iframe 嵌套的點擊劫持攻擊。 這樣瀏覽器就會阻止嵌入網頁的渲染。
  2. JS 判斷頂層視口的域名是不是和本頁面的域名一致,不一致則不允許操作,top.location.hostname === self.location.hostname
  3. 敏感操作使用更復雜的步驟(驗證碼、輸入項目名稱以刪除)。

(這個來源於LuckyWinty: http://www.imooc.com/article/295400)

9. setTimeout的執行原理(EventLoop)

(必問…)

(回答參考:https://juejin.im/post/5e621f5fe51d452700567c32)

setTimeout的運行機制:執行該語句時,是立即把當前定時器代碼推入事件隊列,當定時器在事件列表中滿足設置的時間值時將傳入的函數加入任務隊列,之後的執行就交給任務隊列負責。但是如果此時任務隊列不爲空,則需等待,所以執行定時器內代碼的時間可能會大於設置的時間

說了一下它屬於異步任務,然後說了一下還有哪些宏任務以及微任務,最後說了一下EventLoop的執行過程。

  • 一開始整個腳本作爲一個宏任務執行

  • 執行過程中同步代碼直接執行,宏任務進入宏任務隊列,微任務進入微任務隊列

  • 當前宏任務執行完出隊,檢查微任務列表,有則依次執行,直到全部執行完

  • 執行瀏覽器UI線程的渲染工作

  • 檢查是否有Web Worker任務,有則執行

  • 執行完本輪的宏任務,回到2,依此循環,直到宏任務和微任務隊列都爲空

(具體可以看這裏:https://juejin.im/post/5e58c618e51d4526ed66b5cf#heading-1)

10. requestAnimationFrame有了解過嗎?

(啪啪啪,不長記性,其實之前面試有被問過,但是忘了再去了解了,這就吃虧了,沒答上來)

requestAnimationFrame是瀏覽器用於定時循環操作的一個接口,類似於setTimeout,主要用途是按幀對網頁進行重繪。對於JS動畫,用requestAnimationFrame 會比 setInterval 效果更好。

具體可以看:https://juejin.im/post/5e621f5fe51d452700567c32

11. requestAnimationFrame和setTimeout的區別?

同上…

12. 平常工作中ES6+主要用到了哪些?

(下面看着很多,但我肯定不是全答哈,挑了幾個來回答)

ES6

  1. Class

  2. 模塊importexport

  3. 箭頭函數

  4. 函數默認參數

  5. ...擴展運輸符允許展開數組

  6. 解構

  7. 字符串模版

  8. Promise

  9. let const

  10. Proxy、Map、Set

  11. 對象屬性同名能簡寫

ES7

  1. includes

  2. **求冪運算符

ES8

  1. async/await
  2. Object.values()和Object.entries()
  3. padStart()和padEnd()
  4. Object.getOwnPropertyDescriptors()
  5. 函數參數允許尾部,

ES9

  1. for...await...of
  2. ...展開符合允許展開對象收集剩餘參數
  3. Promise.finally()
  4. 正則中的四個新功能

ES10

  1. flat()
  2. flatMap()
  3. fromEntries()
  4. trimStarttrimEnd
  5. matchAll
  6. BigInt
  7. try/catch中報錯允許沒有err異常參數
  8. Symbol.prototype.description
  9. Function.toString()調用時呈現原本源碼的樣子

(還不瞭解的小夥伴可以看看浪裏哥的這篇:盤點ES7、ES8、ES9、ES10新特性

13. 如何在前端實現一個圖片壓縮

實話實話沒做過,但是後來面試官告訴我:可以使用canvas來實現。具體做法等我寫篇文章哈。

(當時我還反問了一句面試官:那批量圖片壓縮要怎麼做呢?把他驚的…然後他和我說挺複雜的…)

14. 你上家公司主要是做什麼的?

15. 團隊多少人呢?

1個前端(我),1個小程序老哥(IOS轉行的),6個後臺。

16. 項目中有碰到什麼難的問題嗎?如何解決的?

例舉了我最經典的bpmn.js,以此來引出我寫了很多關於這方面的教材,以及建立了微信羣,爲國內的bpmn.js社區貢獻了一份力量…怎麼高大上怎麼來…

當然也有提到我GitHub上的bpmn-chinese-document項目只有100多的Star,他說理解,畢竟這東西用的人不是很多。

17. 期望薪資多少?

喊了個挺高的數,老哥笑了笑,你這個工作年限我們可能給不到,然後扯了點別的。

18. 還有什麼想要問我的嗎?

  • 團隊人員分佈情況
  • 技術棧
  • 我進去主要是負責哪塊內容
  • 年終獎/季度獎
  • 調薪的頻率以及幅度
  • 加班多不多

(其實後面這些問題應該是等到HR面的時候問的,但是感覺和面試官挺聊的來的我就先打聽了)

二面(CTO)

1. JSONP的實現原理

絕了…又來

2. XSS攻擊以及如何預防?

絕了…又來X2

3. 不使用框架如何實現組件按需加載以及原理

當時答的是是用import來按需引入,以及提到了Vue.use

但後來有去了解,babel-plugin-import就可以實現。

4. 你們這個是自己寫的組件庫嗎?

估計面試官看錯了…雖然我的項目有個組件庫的功能,但是是基於Ant Design of Vue二次開發的。

5. 還有什麼想要問我的嗎??

沒了…

三面(HR)

問的問題有點多,我挑一些記得住的哈

1. 第一家公司爲什麼離職?第二家爲什麼離職?

2. 第一家工資多少?第二家多少?

3. 兩家公司主要是做什麼的?規模是多大?

4. 之前都是你一個前端嗎?

5. 有了解過我們公司嗎?感覺怎麼樣?

6. 因爲我們現在整個研發團隊人也不是太多就30多個,前端加上總監可能就4個,你會考慮這麼一個團隊嗎?

7. 有拿到其它的offer嗎?

8. 拒絕一些offer的原因是什麼?

9. 你的期望薪資我們可能給不到,你怎麼想的?

10. 平常的興趣愛好是什麼?

11. 老家在哪裏?…

12. 現在住哪裏?…

13. 還有什麼想要問我的嗎???

面試推薦好文

(還有很多大佬的很多好文,不是呆呆不寫在這裏啊,是因爲呆呆暫時只刷了這些,抱歉了😂)

後語

你盼世界,我盼望你無bug。這篇文章就介紹到了這裏。

我是一隻正在努力求生存的呆呆,也在這條路上不斷的總結和成長,希望自己能夠堅持✊。

"風浪沒平息 我宣告奔跑的意義"

"這不是叛逆 我只是淋了一場雨"

喜歡霖呆呆的小夥還希望可以關注霖呆呆的公衆號 LinDaiDai 或者掃一掃下面的二維碼👇👇👇.

我會不定時的更新一些前端方面的知識內容以及自己的原創文章🎉

你的鼓勵就是我持續創作的主要動力 😊.

相關推薦:

《全網最詳bpmn.js教材》

《【建議改成】讀完這篇你還不懂Babel我給你寄口罩》

《【建議星星】要就來45道Promise面試題一次爽到底(1.1w字用心整理)》

《【建議👍】再來40道this面試題酸爽繼續(1.2w字用手整理)》

《【何不三連】比繼承家業還要簡單的JS繼承題-封裝篇(牛刀小試)》

《【何不三連】做完這48道題徹底弄懂JS繼承(1.7w字含辛整理-返璞歸真)》

《【精】從206個console.log()完全弄懂數據類型轉換的前世今生(上)》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章