Web 前端 中高難度問題(但願看完以後的你能夠拿到Offer^v^)

1. 解釋 event loop

  • Javascript是單線程的,全部的同步任務都會在主線程中執行。
  • 主線程以外,還有一個任務隊列。每當一個異步任務有結果了,就往任務隊列裏塞一個事件。
  • 當主線程中的任務,都執行完以後,系統會 「依次」 讀取任務隊列裏的事件。與之相對應的異步任務進入主線程,開始執行。
  • 異步任務之間,會存在差別,因此它們執行的優先級也會有區別。大體分爲 微任務(micro task,如:Promise、MutaionObserver等)和宏任務(macro task,如:setTimeout、setInterval、I/O等)。同一次事件循環中,微任務永遠在宏任務以前執行。
  • 主線程會不斷重複上面的步驟,直到執行完全部任務。

注意

  1. Promise 內部的語句是當即執行的,以上所說的微任務 Promise 指的是 Promise.then
    • macro-task(宏任務):包括總體代碼script,setTimeout,setInterval
    • micro-task(微任務):Promise,process.nextTick(nodejs)

2. 寫出輸出結果

function p(){ return new Promise(resolve => { console.log('resolve') resolve() }) } p().then(() => { console.log('hello') }) console.log('hi') // 'resolve' 'hi' 'hello'

3. 寫出輸出結果

let a = 0 let b = async () => { a = a + await 10 console.log('2', a) } b() a++ console.log('1', a) // -> '1' 1 // -> '2' 10

4. setTimeinterval 和 setTimeout 準確嗎,緣由?

因爲 javascript 的 event loop 機制,setTimeinterval 和 setTimeout 須要在主線程任務和微任務結束後執行,這就意味着若是主線程的處理時間超出了設置的時間時這兩種方法確定是不許確的javascript

5. setTimeout、setInterval、requestAnimationFrame 各有什麼特色?

setTimeoutsetInterval在 event loop 的宏任務中,當主線程結束時纔會按照任務隊列加載css

requestAnimationFrame 在主線程中執行,因此更加準確,以1秒鐘60次(大約每16.7毫秒一次)的頻率執行html

6. setTimeout、setInterval、requestAnimationFrame 各有什麼特色?

function setInterval(callback, interval) { let timer const now = Date.now let startTime = now() let endTime = startTime const loop = () => { timer = window.requestAnimationFrame(loop) endTime = now() if (endTime - startTime >= interval) { startTime = endTime = now() callback(timer) } } timer = window.requestAnimationFrame(loop) return timer } let a = 0 setInterval(timer => { console.log(1) a++ if (a === 3) cancelAnimationFrame(timer) }, 1000)

7. 函數節流與防抖

函數 & 防抖前端

8. 解釋原型鏈

在 javascript 中,一切皆對象,而每一個對象都會有一個 __proto__ 屬性, __proto__ 指向實例化該對象的構造函數的 prototype,而該構造函數的 __proto__ 又指向它的構造函數的 __proto__ 如此往復向下,直到底層爲 null 時中止,當調用一個對象的方法時,javascript 會順着這條線尋找該方法。vue

9. 繼承實現的方式

prototype,classjava

10. 爲何要使用模塊化?都有哪幾種方式能夠實現模塊化,各有什麼特色?

  • 解決命名衝突
  • 提供複用性
  • 提升代碼可維護性

特色node

11. == 和 === 區別

使用 == 時,若是兩邊值的類型不一樣會觸發類型轉換,因此會出現 Boolean('1' == 1) === true ,使用 === 時則不會webpack

12. 什麼是閉包,做用?

what

函數 A 內部有一個函數 B,函數 B 能夠訪問到函數 A 中的變量,那麼函數 B 就是閉包git

13. 寫出輸出結果

for (var i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i) }, i * 1000) } // 6個6

14. 淺拷貝?深拷貝?分別如何實現?

深拷貝 & 淺拷貝github

15. javascript 中有哪些數據類型

  • 原始數據類型: number, boolean, string, null, undefinded, symbol
  • 引用數據類型: object

16. 箭頭函數中的 this 指向

17. call,apply,bind 用法(如何改變 this 的指向)

18. 實現 call,apply,bind

Function.prototype.myCall = function(context) { if (typeof this !== 'function') { throw new TypeError('Error') } context = context || window context.fn = this const args = [...arguments].slice(1) const result = context.fn(...args) delete context.fn return result } Function.prototype.myApply = function(context) { if (typeof this !== 'function') { throw new TypeError('Error') } context = context || window context.fn = this let result if (arguments[1]) { result = context.fn(...arguments[1]) } else { result = context.fn() } delete context.fn return result } Function.prototype.myBind = function (context) { if (typeof this !== 'function') { throw new TypeError('Error') } const _this = this const args = [...arguments].slice(1) // 返回一個函數 return function F() { if (this instanceof F) { return new _this(...args, ...arguments) } return _this.apply(context, args.concat(...arguments)) } }

20. 使用 new 關鍵字後發生了什麼

  1. 新生成了一個對象
  2. 連接到原型
  3. 綁定 this
  4. 返回新對象

實現

function create() { let obj = {} let Con = [].shift.call(arguments) obj.__proto__ = Con.prototype let result = Con.apply(obj, arguments) return result instanceof Object ? result : obj }

21. intanceof 原理?

instanceof 能夠正確的判斷對象的類型,由於內部機制是經過判斷對象的原型鏈中是否是能找到類型的 prototype

function myInstanceof(left, right) { let prototype = right.prototype left = left.__proto__ while (true) { if (left === null || left === undefined) return false if (prototype === left) return true left = left.__proto__ } }

22. 垃圾回收機制?

23. 解釋冒泡,捕獲事件

冒泡:由小及大,從子元素事件發出,向父元素,父元素的父元素...直至 html 爲止

捕獲:由大及小,從父元素髮出,向其下的子元素...直至最小的元素爲止

使用 element.addEventListener(type,listener,options) ,在 options.capture 設置使用冒泡仍是捕獲,默認冒泡

24. 解釋事件代理

通常使用在有大量或者是動態渲染的html元素須要綁定事件時,以達到提升性能或動態綁定的目的。

將事件綁定在 html 元素的父元素上,經過事件流的冒泡屬性,在父元素中獲取到點擊的子元素,加以判斷後實行相應的事件。

25. 什麼是跨域?爲何瀏覽器要使用同源策略?你有幾種方式能夠解決跨域問題?瞭解預檢請求嘛?

what

當協議、域名或者端口有一個不一樣便是跨域,瀏覽器會攔截 ajax 請求,目的是爲了防止 CSRF 攻擊。簡單點說,CSRF 攻擊是利用用戶的登陸態發起惡意請求。

瀏覽器攔截的是讀取內容的請求,因此經過表單等方式的請求是不會被攔截的

僅在同域名和同域名不一樣文件夾下兩種狀況時不存在跨域,其他皆爲跨域

解決

  1. JSONP
  • JSONP 的原理很簡單,就是利用 <script> 標籤沒有跨域限制的漏洞。經過 <script> 標籤指向一個須要訪問的地址並提供一個回調函數來接收數據當須要通信時。
function jsonp(url, jsonpCallback, success) { let script = document.createElement('script') script.src = url script.async = true script.type = 'text/javascript' window[jsonpCallback] = function(data) { success && success(data) } document.body.appendChild(script) } jsonp('http://xxx', 'callback', function(value) { console.log(value) })
  1. CORS
  • 須要瀏覽器和後端同時支持。IE 8 和 9 須要經過 XDomainRequest 來實現。
  1. document.domain
  • 該方式只能用於二級域名相同的狀況下,好比 a.test.com 和 b.test.com 適用於該方式。

  • 只須要給頁面添加 document.domain = 'test.com' 表示二級域名都相同就能夠實現跨域

  1. MessageChannel
  • MessageChannel

  • 主要用於頁面和其下的 iframe 之間的通信

26. 什麼狀況會形成阻塞渲染

  1. 在 HTML 和 CSS 生成渲染樹的過程當中確定會形成阻塞渲染
  • 解決方案:文件大小,而且扁平層級,優化選擇器
  1. 在瀏覽器解析到 script 標籤時,會加載並執行 script 的內容,直到結束後纔會繼續渲染,也會形成渲染阻塞
  • 解決方案:將 script 放在 body 底部,或者設置 async 屬性爲 defer

27. 重繪(Repaint)和迴流(Reflow)

重繪僅改變節點的外觀,不影響佈局,如改變節點的 color 屬性

迴流指節點的大小或頁面的佈局發生改變

迴流一定會發生重繪,重繪不必定會引起迴流

如何減小

  1. 使用 transform 替代 top
  2. 使用 visibility 替換 display: none ,由於前者只會引發重繪,後者會引起迴流(改變了佈局)
  3. 不要把節點的屬性值放在一個循環裏當成循環裏的變量
  4. 不要使用 table 佈局,可能很小的一個小改動會形成整個 table 的從新佈局

28. 從用戶輸入URL到瀏覽器呈現頁面通過了哪些過程

參考

DNS 解析

  1. 瀏覽器根據地址去自己緩存中查找dns解析記錄,若是有,則直接返回IP地址,不然瀏覽器會查找操做系統中(hosts文件)是否有該域名的dns解析記錄,若是有則返回。
  2. 若是瀏覽器緩存和操做系統hosts中均無該域名的dns解析記錄,或者已通過期,此時就會向域名服務器發起請求來解析這個域名。
  3. 請求會先到LDNS(本地域名服務器),讓它來嘗試解析這個域名,若是LDNS也解析不了,則直接到根域名解析器請求解析
  4. 根域名服務器給LDNS返回一個所查詢餘的主域名服務器(gTLDServer)地址。
  5. 此時LDNS再向上一步返回的gTLD服務器發起解析請求。
  6. gTLD服務器接收到解析請求後查找並返回此域名對應的Name Server域名服務器的地址,這個Name Server一般就是你註冊的域名服務器(好比阿里dns、騰訊dns等)
  7. Name Server域名服務器會查詢存儲的域名和IP的映射關係表,正常狀況下都根據域名獲得目標IP記錄,連同一個TTL值返回給DNS Server域名服務器
  8. 返回該域名對應的IP和TTL值,Local DNS Server會緩存這個域名和IP的對應關係,緩存的時間有TTL值控制。
  9. 把解析的結果返回給用戶,用戶根據TTL值緩存在本地系統緩存中,域名解析過程結束。

HTTP請求發起和響應

  1. 用戶輸入URL,瀏覽器獲取到URL
  2. 瀏覽器(應用層)進行DNS解析(若是輸入的是IP地址,此步驟省略)
  3. 根據解析出的IP地址+端口,瀏覽器(應用層)發起HTTP請求,請求中攜帶(請求頭header(也可細分爲請求行和請求頭)、請求體body),

header包含:

請求的方法(get、post、put..) 協議(http、https、ftp、sftp…) 目標url(具體的請求路徑已經文件名) 一些必要信息(緩存、cookie之類)

body包含:

請求的內容

  1. 請求到達傳輸層,tcp協議爲傳輸報文提供可靠的字節流傳輸服務,它經過三次握手等手段來保證傳輸過程當中的安全可靠。經過對大塊數據的分割成一個個報文段的方式提供給大量數據的便攜傳輸。
  2. 到網絡層, 網絡層經過ARP尋址獲得接收方的Mac地址,IP協議把在傳輸層被分割成一個個數據包傳送接收方。
  3. 數據到達數據鏈路層,請求階段完成
  4. 接收方在數據鏈路層收到數據包以後,層層傳遞到應用層,接收方應用程序就得到到請求報文。
  5. 接收方收到發送方的HTTP請求以後,進行請求文件資源(如HTML頁面)的尋找並響應報文
  6. 發送方收到響應報文後,若是報文中的狀態碼錶示請求成功,則接受返回的資源(如HTML文件),進行頁面渲染。

網頁渲染

  1. 瀏覽器經過HTMLParser根據深度遍歷的原則把HTML解析成DOM Tree。
  2. 將CSS解析成CSS Rule Tree(CSSOM Tree)。
  3. 根據DOM樹和CSSOM樹來構造render Tree。
  4. layout:根據獲得的render tree來計算全部節點在屏幕的位置。
  5. paint:遍歷render樹,並調用硬件圖形API來繪製每一個節點。
  6. 當遇到 script 標籤時會等待其中 js 代碼執行完成後繼續執行上述步驟(會形成阻塞)

29. 前端性能優化

CSS

  1. 優化選擇器路徑:健全的css選擇器當然是能讓開發看起來更清晰,而後對於css的解析來講倒是個很大的性能問題,所以相比於 .a .b .c{} ,更傾向於你們寫.c{}。
  2. 壓縮文件:儘量的壓縮你的css文件大小,減小資源下載的負擔。
  3. 選擇器合併:把有共同的屬性內容的一系列選擇器組合到一塊兒,能壓縮空間和資源開銷
  4. 精準樣式:儘量減小沒必要要的屬性設置,好比你只要設置{padding-left:10px}的值,那就避免{padding:0 0 0 10px}這樣的寫法
  5. 雪碧圖:在合理的地方把一些小的圖標合併到一張圖中,這樣全部的圖片只須要一次請求,而後經過定位的方式獲取相應的圖標,這樣能避免一個圖標一次請求的資源浪費。
  6. 避免通配符:.a .b {} 像這樣的選擇器,根據從右到左的解析順序在解析過程當中遇到通配符()回去遍歷整個dom的,這樣性能問題就大大的了。
  7. 少用Float:Float在渲染時計算量比較大,儘可能減小使用。
  8. 0值去單位:對於爲0的值,儘可能不要加單位,增長兼容性

HTML

  1. 避免再HTML中直接寫css代碼。
  2. 使用Viewport加速頁面的渲染。
  3. 使用語義化標籤,減小css的代碼,增長可讀性和SEO。
  4. 減小標籤的使用,dom解析是一個大量遍歷的過程,減小無必要的標籤,能下降遍歷的次數。
  5. 避免src、href等的值爲空。
  6. 減小dns查詢的次數。

JS

  1. 儘量把script標籤放到body以後,避免頁面須要等待js執行完成以後dom才能繼續執行,最大程度保證頁面儘快的展現出來。
  2. 儘量合併script代碼,
  3. css能幹的事情,儘可能不要用JavaScript來幹。畢竟JavaScript的解析執行過於直接和粗暴,而css效率更高。
  4. 儘量壓縮的js文件,減小資源下載的負擔
  5. 儘量避免在js中逐條操做dom樣式,儘量預約義好css樣式,而後經過改變樣式名來修改dom樣式,這樣集中式的操做能減小reflow或repaint的次數。
  6. 儘量少的在js中建立dom,而是預先埋到HTML中用display:none來隱藏,在js中按需調用,減小js對dom的暴力操做。

30. 強制緩存,跳過垃圾回收機制

31. Vue 實例中的 data 爲何使用函數

32. 實現 v-modal

33. --

34. 公司技術(組件)沉澱舉例

35. get 和 post 區別(從報文角度)

36. ES5 寫原型拓展(實現 extends)

// ES5 function Animal() { this.type = 'animal' this.eat = function(){} } function Cat() { Animal.call(this) this.name = 'cat' } function inherits(Child, Parent) { var F = function () {}; F.prototype = Parent.prototype; Child.prototype = new F(); Child.prototype.constructor = Child; } // ES6 class Fruit{ constructor(){} } class Apple extends Fruit{ constructor(){ super() } }

36. 虛擬 dom 相比 原生 dom 好處

先明確:虛擬 dom (框架封裝的)不必定比 原生 dom 快 參考

好處:

簡化dom操做,讓數據與dom之間的關係更直觀更簡單

37. webpack 中 plugin 和 loader 有什麼區別

loader

用於加載某些資源文件。 由於webpack 自己只能打包commonjs規範的js文件,對於其餘資源例如 css,圖片,或者其餘的語法集,好比 jsx, coffee,是沒有辦法加載的。 這就須要對應的loader將資源轉化,加載進來。從字面意思也能看出,loader是用於加載的,它做用於一個個文件上。

plugin

用於擴展webpack的功能。它直接做用於 webpack,擴展了它的功能。固然loader也時變相的擴展了 webpack ,可是它只專一於轉化文件(transform)這一個領域。而plugin的功能更加的豐富,而不只侷限於資源的加載。

38. 瀏覽器緩存策略

一般瀏覽器緩存策略分爲兩種:強緩存和協商緩存,而且緩存策略都是經過設置 HTTP Header 來實現的。

強緩存

強緩存能夠經過設置兩種 HTTP Header 實現:Expires 和 Cache-Control 。強緩存表示在緩存期間不須要請求,state code 爲 200。

協商緩存

若是緩存過時了,就須要發起請求驗證資源是否有更新。協商緩存能夠經過設置兩種 HTTP Header 實現:Last-Modified 和 ETag 。

39. 手寫原生 ajax

相關文章
相關標籤/搜索