前端面试-复习篇上
一、CSS
#1. 盒模型
頁面渲染時,dom?元素所采用的 布局模型。可通過box-sizing進行設置。根據計算寬高的區域可分為
- content-box?(W3C?標準盒模型)
- border-box?(IE?盒模型)
- padding-box
- margin-box?(瀏覽器未實現)
#2. BFC
塊級格式化上下文,是一個獨立的渲染區域,讓處于?BFC?內部的元素與外部的元素相互隔離,使內外元素的定位不會相互影響。
IE下為?Layout,可通過?zoom:1?觸發
觸發條件:
- 根元素
- position: absolute/fixed
- display: inline-block / table
- float?元素
- ovevflow !== visible
規則:
- 屬于同一個?BFC?的兩個相鄰?Box?垂直排列
- 屬于同一個?BFC?的兩個相鄰?Box?的?margin?會發生重疊
- BFC?中子元素的?margin box?的左邊, 與包含塊 (BFC)?border box的左邊相接觸 (子元素?absolute?除外)
- BFC?的區域不會與?float?的元素區域重疊
- 計算?BFC?的高度時,浮動子元素也參與計算
- 文字層不會被浮動層覆蓋,環繞于周圍
應用:
- 阻止margin重疊
- 可以包含浮動元素 —— 清除內部浮動(清除浮動的原理是兩個div都位于同一個?BFC?區域之中)
- 自適應兩欄布局
- 可以阻止元素被浮動元素覆蓋
#3.層疊上下文
元素提升為一個比較特殊的圖層,在三維空間中 (z軸) 高出普通元素一等。
觸發條件
- 根層疊上下文(html)
- position
- css3屬性
- flex
- transform
- opacity
- filter
- will-change
- webkit-overflow-scrolling
層疊等級:層疊上下文在z軸上的排序
- 在同一層疊上下文中,層疊等級才有意義
- z-index的優先級最高
#4. 居中布局
水平居中
- 行內元素:?text-align: center
- 塊級元素:?margin: 0 auto
- absolute + transform
- flex + justify-content: center
垂直居中
- line-height: height
- absolute + transform
- flex + align-items: center
- table
水平垂直居中
- absolute + transform
- flex + justify-content + align-items
#5. 選擇器優先級
- !important?> 行內樣式 >?#id?>?.class?>?tag?>?*?> 繼承 > 默認
- 選擇器 從右往左 解析
#6.去除浮動影響,防止父級高度塌陷
- 通過增加尾元素清除浮動
- :after / <br> : clear: both
- 創建父級?BFC
- 父級設置高度
#7.link 與 @import 的區別
- link功能較多,可以定義?RSS,定義?Rel?等作用,而@import只能用于加載?css
- 當解析到link時,頁面會同步加載所引的?css,而@import所引用的?css?會等到頁面加載完才被加載
- @import需要?IE5?以上才能使用
- link可以使用?js?動態引入,@import不行
#8. CSS預處理器(Sass/Less/Postcss)
CSS預處理器的原理: 是將類?CSS語言通過?Webpack?編譯 轉成瀏覽器可讀的真正?CSS。在這層編譯之上,便可以賦予?CSS?更多更強大的功能,常用功能:
- 嵌套
- 變量
- 循環語句
- 條件語句
- 自動前綴
- 單位轉換
- mixin復用
面試中一般不會重點考察該點,一般介紹下自己在實戰項目中的經驗即可~
#9.CSS動畫
transition: 過渡動畫
- transition-property: 屬性
- transition-duration: 間隔
- transition-timing-function: 曲線
- transition-delay: 延遲
- 常用鉤子:?transitionend
animation / keyframes
- animation-name: 動畫名稱,對應@keyframes
- animation-duration: 間隔
- animation-timing-function: 曲線
- animation-delay: 延遲
- animation-iteration-count: 次數
- infinite: 循環動畫
- animation-direction: 方向
- alternate: 反向播放
- animation-fill-mode: 靜止模式
- forwards: 停止時,保留最后一幀
- backwards: 停止時,回到第一幀
- both: 同時運用?forwards / backwards
- 常用鉤子:?animationend
動畫屬性: 盡量使用動畫屬性進行動畫,能擁有較好的性能表現
- translate
- scale
- rotate
- skew
- opacity
- color
#二、JavaScript
#1. 原型 / 構造函數 / 實例
- 原型(prototype): 一個簡單的對象,用于實現對象的 屬性繼承。可以簡單的理解成對象的爹。在?Firefox?和?Chrome?中,每個JavaScript對象中都包含一個__proto__(非標準)的屬性指向它爹(該對象的原型),可obj.__proto__進行訪問。
- 構造函數: 可以通過new來 新建一個對象 的函數。
- 實例: 通過構造函數和new創建出來的對象,便是實例。 實例通過__proto__指向原型,通過constructor指向構造函數。
以Object為例,我們常用的Object便是一個構造函數,因此我們可以通過它構建實例。
// 實例 const instance = new Object()則此時, 實例為instance, 構造函數為Object,我們知道,構造函數擁有一個prototype的屬性指向原型,因此原型為:
// 原型 const prototype = Object.prototype這里我們可以來看出三者的關系:
- 實例.__proto__ === 原型
- 原型.constructor === 構造函數
- 構造函數.prototype === 原型
#2.原型鏈:
原型鏈是由原型對象組成,每個對象都有?__proto__?屬性,指向了創建該對象的構造函數的原型,__proto__?將對象連接起來組成了原型鏈。是一個用來實現繼承和共享屬性的有限的對象鏈
- 屬性查找機制: 當查找對象的屬性時,如果實例對象自身不存在該屬性,則沿著原型鏈往上一級查找,找到時則輸出,不存在時,則繼續沿著原型鏈往上一級查找,直至最頂級的原型對象Object.prototype,如還是沒找到,則輸出undefined;
- 屬性修改機制: 只會修改實例對象本身的屬性,如果不存在,則進行添加該屬性,如果需要修改原型的屬性時,則可以用:?b.prototype.x = 2;但是這樣會造成所有繼承于該對象的實例的屬性發生改變。
#3. 執行上下文(EC)
執行上下文可以簡單理解為一個對象:
它包含三個部分:
- 變量對象(VO)
- 作用域鏈(詞法作用域)
- this指向
它的類型:
- 全局執行上下文
- 函數執行上下文
- eval執行上下文
代碼執行過程:
- 創建 全局上下文 (global EC)
- 全局執行上下文 (caller) 逐行 自上而下 執行。遇到函數時,函數執行上下文 (callee) 被push到執行棧頂層
- 函數執行上下文被激活,成為?active EC, 開始執行函數中的代碼,caller?被掛起
- 函數執行完后,callee?被pop移除出執行棧,控制權交還全局上下文 (caller),繼續執行
#4.變量對象
- 變量對象,是執行上下文中的一部分,可以抽象為一種 數據作用域,其實也可以理解為就是一個簡單的對象,它存儲著該執行上下文中的所有 變量和函數聲明(不包含函數表達式)。
- 活動對象 (AO): 當變量對象所處的上下文為?active EC?時,稱為活動對象。
#5. 作用域
執行上下文中還包含作用域鏈。理解作用域之前,先介紹下作用域。作用域其實可理解為該上下文中聲明的 變量和聲明的作用范圍。可分為 塊級作用域 和 函數作用域
特性:
- 聲明提前: 一個聲明在函數體內都是可見的, 函數優先于變量
- 非匿名自執行函數,函數變量為 只讀 狀態,無法修改
#6.作用域鏈
我們知道,我們可以在執行上下文中訪問到父級甚至全局的變量,這便是作用域鏈的功勞。作用域鏈可以理解為一組對象列表,包含 父級和自身的變量對象,因此我們便能通過作用域鏈訪問到父級里聲明的變量或者函數。
由兩部分組成:
- [[scope]]屬性: 指向父級變量對象和作用域鏈,也就是包含了父級的[[scope]]和AO
- AO: 自身活動對象
如此?[[scopr]]包含[[scope]],便自上而下形成一條 鏈式作用域。
#7. 閉包
閉包屬于一種特殊的作用域,稱為 靜態作用域。它的定義可以理解為: 父函數被銷毀 的情況下,返回出的子函數的[[scope]]中仍然保留著父級的單變量對象和作用域鏈,因此可以繼續訪問到父級的變量對象,這樣的函數稱為閉包。
閉包會產生一個很經典的問題:
多個子函數的[[scope]]都是同時指向父級,是完全共享的。因此當父級的變量對象被修改時,所有子函數都受到影響。
??解決:**
- 變量可以通過 函數參數的形式 傳入,避免使用默認的[[scope]]向上查找
- 使用setTimeout包裹,通過第三個參數傳入
- 使用 塊級作用域,讓變量成為自己上下文的屬性,避免共享
#8. script 引入方式:
- html?靜態<script>引入
- js?動態插入<script>
- <script defer>: 異步加載,元素解析完成后執行
- <script async>: 異步加載,但執行時會阻塞元素渲染
#9. 對象的拷貝
淺拷貝: 以賦值的形式拷貝引用對象,仍指向同一個地址,修改時原對象也會受到影響
- Object.assign
- 展開運算符(...)
深拷貝: 完全拷貝一個新對象,修改時原對象不再受到任何影響
- JSON.parse(JSON.stringify(obj)): 性能最快
- 具有循環引用的對象時,報錯
- 當值為函數、undefined、或symbol時,無法拷貝
- 遞歸進行逐一賦值
#10. new運算符的執行過程
- 新生成一個對象
- 鏈接到原型:?obj.__proto__ = Con.prototype
- 綁定this: apply
- 返回新對象(如果構造函數有自己?retrun?時,則返回該值)
#11. instanceof原理
能在實例的 原型對象鏈 中找到該構造函數的prototype屬性所指向的 原型對象,就返回true。即:
// __proto__: 代表原型對象鏈 instance.[__proto__...] === instance.constructor.prototype// return true#12. 代碼的復用
當你發現任何代碼開始寫第二遍時,就要開始考慮如何復用。一般有以下的方式:
- 函數封裝
- 繼承
- 復制extend
- 混入mixin
- 借用apply/call
#13. 繼承
在 JS 中,繼承通常指的便是 原型鏈繼承,也就是通過指定原型,并可以通過原型鏈繼承原型上的屬性或者方法。
最優化: 圣杯模式
var inherit = (function(c,p){var F = function(){};return function(c,p){F.prototype = p.prototype;c.prototype = new F();c.uber = p.prototype;c.prototype.constructor = c;} })();使用?ES6?的語法糖?class / extends
#14. 類型轉換
大家都知道 JS 中在使用運算符號或者對比符時,會自帶隱式轉換,規則如下:
- -、*、/、%:一律轉換成數值后計算
- +:
- 數字 + 字符串 = 字符串, 運算順序是從左到右
- 數字 + 對象, 優先調用對象的valueOf -> toString
- 數字 +?boolean/null?-> 數字
- 數字 +?undefined?->?NaN
- [1].toString() === '1'
- {}.toString() === '[object object]'
- NaN !== NaN?、+undefined?為?NaN
#15. 類型判斷
判斷?Target?的類型,單單用?typeof?并無法完全滿足,這其實并不是?bug,本質原因是?JS?的萬物皆對象的理論。因此要真正完美判斷時,我們需要區分對待:
- 基本類型(null): 使用?String(null)
- 基本類型(string / number / boolean / undefined) +?function: - 直接使用?typeof即可
- 其余引用類型(Array / Date / RegExp Error): 調用toString后根據[object XXX]進行判斷
很穩的判斷封裝:
let class2type = {} 'Array Date RegExp Object Error'.split(' ').forEach(e => class2type[ '[object ' + e + ']' ] = e.toLowerCase()) function type(obj) {if (obj == null) return String(obj)return typeof obj === 'object' ? class2type[ Object.prototype.toString.call(obj) ] || 'object' : typeof obj }#16. 模塊化
模塊化開發在現代開發中已是必不可少的一部分,它大大提高了項目的可維護、可拓展和可協作性。通常,我們 在瀏覽器中使用?ES6?的模塊化支持,在?Node?中使用?commonjs?的模塊化支持。
分類:
- es6: import / export
- commonjs: require / module.exports / exports
- amd: require / defined
require與import的區別
- require支持 動態導入,import不支持,正在提案 (babel?下可支持)
- require是 同步 導入,import屬于 異步 導入
- require是 值拷貝,導出值變化不會影響導入值;import指向 內存地址,導入值會隨導出值而變化
#17. 防抖與節流
防抖與節流函數是一種最常用的 高頻觸發優化方式,能對性能有較大的幫助。
- 防抖 (debounce): 將多次高頻操作優化為只在最后一次執行,通常使用的場景是:用戶輸入,只需再輸入完成后做一次輸入校驗即可。
- 節流(throttle): 每隔一段時間后執行一次,也就是降低頻率,將高頻操作優化成低頻操作,通常使用場景: 滾動條事件 或者?resize?事件,通常每隔?100~500 ms執行一次即可。
#18. 函數執行改變this
- 由于 JS 的設計原理: 在函數中,可以引用運行環境中的變量。因此就需要一個機制來讓我們可以在函數體內部獲取當前的運行環境,這便是this。
因此要明白?this?指向,其實就是要搞清楚 函數的運行環境,說人話就是,誰調用了函數。例如
- obj.fn(),便是?obj?調用了函數,既函數中的?this === obj
- fn(),這里可以看成?window.fn(),因此?this === window
但這種機制并不完全能滿足我們的業務需求,因此提供了三種方式可以手動修改?this?的指向:
- call: fn.call(target, 1, 2)
- apply: fn.apply(target, [1, 2])
- bind: fn.bind(target)(1,2)
#19. ES6/ES7
由于?Babel的強大和普及,現在?ES6/ES7?基本上已經是現代化開發的必備了。通過新的語法糖,能讓代碼整體更為簡潔和易讀。
聲明
- let / const: 塊級作用域、不存在變量提升、暫時性死區、不允許重復聲明
- const: 聲明常量,無法修改
解構賦值
class / extend: 類聲明與繼承
Set / Map: 新的數據結構
異步解決方案:
- Promise的使用與實現
- generator:
- yield: 暫停代碼
- next(): 繼續執行代碼
await / async: 是generator的語法糖,?babel中是基于promise實現。
async function getUserByAsync(){let user = await fetchUser();return user; }const user = await getUserByAsync() console.log(user)#20. AST
抽象語法樹 (Abstract Syntax Tree),是將代碼逐字母解析成 樹狀對象 的形式。這是語言之間的轉換、代碼語法檢查,代碼風格檢查,代碼格式化,代碼高亮,代碼錯誤提示,代碼自動補全等等的基礎。例如:
function square(n){return n * n }通過解析轉化成的AST如下圖:
#21. babel編譯原理
- babylon?將?ES6/ES7?代碼解析成?AST
- babel-traverse?對?AST?進行遍歷轉譯,得到新的?AST
- 新 AST 通過?babel-generator?轉換成?ES5
#22. 函數柯里化
在一個函數中,首先填充幾個參數,然后再返回一個新的函數的技術,稱為函數的柯里化。通常可用于在不侵入函數的前提下,為函數 預置通用參數,供多次重復調用。
const add = function add(x) {return function (y) {return x + y} }const add1 = add(1)add1(2) === 3 add1(20) === 21#23. 數組(array)
- map: 遍歷數組,返回回調返回值組成的新數組
- forEach: 無法break,可以用try/catch中throw new Error來停止
- filter: 過濾
- some: 有一項返回true,則整體為true
- every: 有一項返回false,則整體為false
- join: 通過指定連接符生成字符串
- push / pop: 末尾推入和彈出,改變原數組, 返回推入/彈出項
- unshift / shift: 頭部推入和彈出,改變原數組,返回操作項
- sort(fn) / reverse: 排序與反轉,改變原數組
- concat: 連接數組,不影響原數組, 淺拷貝
- slice(start, end): 返回截斷后的新數組,不改變原數組
- splice(start, number, value...): 返回刪除元素組成的數組,value為插入項,改變原數組
- indexOf / lastIndexOf(value, fromIndex): 查找數組項,返回對應的下標
- reduce / reduceRight(fn(prev, cur),?defaultPrev): 兩兩執行,prev?為上次化簡函數的return值,cur為當前值(從第二項開始)
數組亂序:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; arr.sort(function () {return Math.random() - 0.5; });數組拆解: flat: [1,[2,3]] --> [1, 2, 3]
Array.prototype.flat = function() {this.toString().split(',').map(item => +item ) }#三、瀏覽器
#1. 跨標簽頁通訊
不同標簽頁間的通訊,本質原理就是去運用一些可以 共享的中間介質,因此比較常用的有以下方法:
- 通過父頁面window.open()和子頁面postMessage
- 異步下,通過?window.open('about: blank')?和?tab.location.href = '*'
- 設置同域下共享的localStorage與監聽window.onstorage
- 重復寫入相同的值無法觸發
- 會受到瀏覽器隱身模式等的限制
- 設置共享cookie與不斷輪詢臟檢查(setInterval)
- 借助服務端或者中間層實現
#2. 瀏覽器架構
- 用戶界面
- 主進程
- 內核
- 渲染引擎
- JS?引擎
- 執行棧
- 事件觸發線程
- 消息隊列
- 微任務
- 宏任務
- 消息隊列
- 網絡異步線程
- 定時器線程
#3. 瀏覽器下事件循環(Event Loop)
事件循環是指: 執行一個宏任務,然后執行清空微任務列表,循環再執行宏任務,再清微任務列表
- 微任務?microtask(jobs): promise / ajax / Object.observe(該方法已廢棄)
- 宏任務?macrotask(task): setTimout / script / IO / UI Rendering
#4. 從輸入 url 到展示的過程
- DNS?解析
- TCP?三次握手
- 發送請求,分析?url,設置請求報文(頭,主體)
- 服務器返回請求的文件 (html)
- 瀏覽器渲染
- HTML parser?-->?DOM Tree
- 標記化算法,進行元素狀態的標記
- dom?樹構建
- HTML parser?-->?DOM Tree
- CSS parser --> Style Tree
- 解析?css?代碼,生成樣式樹
- attachment?-->?Render Tree
- 結合 dom樹 與 style樹,生成渲染樹
- layout: 布局
- GPU painting: 像素繪制頁面
#5. 重繪與回流
當元素的樣式發生變化時,瀏覽器需要觸發更新,重新繪制元素。這個過程中,有兩種類型的操作,即重繪與回流。
- 重繪(repaint): 當元素樣式的改變不影響布局時,瀏覽器將使用重繪對元素進行更新,此時由于只需要UI層面的重新像素繪制,因此 損耗較少
- 回流(reflow): 當元素的尺寸、結構或觸發某些屬性時,瀏覽器會重新渲染頁面,稱為回流。此時,瀏覽器需要重新經過計算,計算后還需要重新頁面布局,因此是較重的操作。會觸發回流的操作:
- 頁面初次渲染
- 瀏覽器窗口大小改變
- 元素尺寸、位置、內容發生改變
- 元素字體大小變化
- 添加或者刪除可見的?dom?元素
- 激活?CSS?偽類(例如::hover)
- 查詢某些屬性或調用某些方法
- clientWidth、clientHeight、clientTop、clientLeft
- offsetWidth、offsetHeight、offsetTop、offsetLeft
- scrollWidth、scrollHeight、scrollTop、scrollLeft
- getComputedStyle()
- getBoundingClientRect()
- scrollTo()
回流必定觸發重繪,重繪不一定觸發回流。重繪的開銷較小,回流的代價較高。
最佳實踐:
css
- 避免使用table布局
- 將動畫效果應用到position屬性為absolute或fixed的元素上
javascript
- 避免頻繁操作樣式,可匯總后統一 一次修改
- 盡量使用class進行樣式修改
- 減少dom的增刪次數,可使用 字符串 或者?documentFragment?一次性插入
- 極限優化時,修改樣式可將其display: none后修改
- 避免多次觸發上面提到的那些會觸發回流的方法,可以的話盡量用 變量存住
#6. 存儲
我們經常需要對業務中的一些數據進行存儲,通常可以分為 短暫性存儲 和 持久性儲存。
- 短暫性的時候,我們只需要將數據存在內存中,只在運行時可用
- 持久性存儲,可以分為 瀏覽器端 與 服務器端
- 瀏覽器:
- cookie: 通常用于存儲用戶身份,登錄狀態等
- http?中自動攜帶, 體積上限為?4K, 可自行設置過期時間
- localStorage / sessionStorage: 長久儲存/窗口關閉刪除, 體積限制為?4~5M
- indexDB
- cookie: 通常用于存儲用戶身份,登錄狀態等
- 服務器:
- 分布式緩存?redis
- 數據庫
- 瀏覽器:
#7. Web Worker
現代瀏覽器為JavaScript創造的 多線程環境。可以新建并將部分任務分配到worker線程并行運行,兩個線程可 獨立運行,互不干擾,可通過自帶的 消息機制 相互通信。
基本用法:
// 創建 worker const worker = new Worker('work.js');// 向主進程推送消息 worker.postMessage('Hello World');// 監聽主進程來的消息 worker.onmessage = function (event) {console.log('Received message ' + event.data); }限制:
- 同源限制
- 無法使用?document / window / alert / confirm
- 無法加載本地資源
#8. 內存泄露
- 意外的全局變量: 無法被回收
- 定時器: 未被正確關閉,導致所引用的外部變量無法被釋放
- 事件監聽: 沒有正確銷毀 (低版本瀏覽器可能出現)
- 閉包: 會導致父級中的變量無法被釋放
- dom?引用:?dom?元素被刪除時,內存中的引用未被正確清空
可用?chrome?中的?timeline?進行內存標記,可視化查看內存的變化情況,找出異常點。
#四、服務端與網絡
#1. http/https 協議
1.0 協議缺陷:
- 無法復用鏈接,完成即斷開,重新慢啟動和?TCP 3次握手
- head of line blocking: 線頭阻塞,導致請求之間互相影響
1.1 改進:
- 長連接(默認?keep-alive),復用
- host?字段指定對應的虛擬站點
- 新增功能:
- 斷點續傳
- 身份認證
- 狀態管理
- cache?緩存
- Cache-Control
- Expires
- Last-Modified
- Etag
2.0:
- 多路復用
- 二進制分幀層: 應用層和傳輸層之間
- 首部壓縮
- 服務端推送
https: 較為安全的網絡傳輸協議
- 證書(公鑰)
- SSL?加密
- 端口?443
TCP:
- 三次握手
- 四次揮手
- 滑動窗口: 流量控制
- 擁塞處理
- 慢開始
- 擁塞避免
- 快速重傳
- 快速恢復
緩存策略: 可分為 強緩存 和 協商緩存
- Cache-Control/Expires: 瀏覽器判斷緩存是否過期,未過期時,直接使用強緩存,Cache-Control的?max-age?優先級高于?Expires
- 當緩存已經過期時,使用協商緩存
- 唯一標識方案:?Etag(response?攜帶) &?If-None-Match(request攜帶,上一次返回的?Etag): 服務器判斷資源是否被修改
- 最后一次修改時間:?Last-Modified(response) & If-Modified-Since(request,上一次返回的Last-Modified)
- 如果一致,則直接返回 304 通知瀏覽器使用緩存
- 如不一致,則服務端返回新的資源
- Last-Modified?缺點:
- 周期性修改,但內容未變時,會導致緩存失效
- 最小粒度只到?s,?s?以內的改動無法檢測到
- Etag?的優先級高于Last-Modified
#2. 常見狀態碼
- 1xx: 接受,繼續處理
- 200: 成功,并返回數據
- 201: 已創建
- 202: 已接受
- 203: 成為,但未授權
- 204: 成功,無內容
- 205: 成功,重置內容
- 206: 成功,部分內容
- 301: 永久移動,重定向
- 302: 臨時移動,可使用原有URI
- 304: 資源未修改,可使用緩存
- 305: 需代理訪問
- 400: 請求語法錯誤
- 401: 要求身份認證
- 403: 拒絕請求
- 404: 資源不存在
- 500: 服務器錯誤
#3. get / post
- get: 緩存、請求長度受限、會被歷史保存記錄
- 無副作用(不修改資源),冪等(請求次數與資源無關)的場景
- post: 安全、大數據、更多編碼類型
#4. Websocket
Websocket?是一個 持久化的協議, 基于?http?, 服務端可以 主動?push
兼容:
- FLASH Socket
- 長輪詢: 定時發送?ajax
- long poll: 發送 --> 有消息時再?response
- new WebSocket(url)
- ws.onerror = fn
- ws.onclose = fn
- ws.onopen = fn
- ws.onmessage = fn
- ws.send()
#5. TCP三次握手
建立連接前,客戶端和服務端需要通過握手來確認對方:
- 客戶端發送?syn(同步序列編號) 請求,進入?syn_send?狀態,等待確認
- 服務端接收并確認?syn?包后發送?syn+ack?包,進入?syn_recv?狀態
- 客戶端接收?syn+ack?包后,發送?ack?包,雙方進入?established?狀態
#6. TCP四次揮手
- 客戶端 -- FIN --> 服務端, FIN—WAIT
- 服務端 -- ACK --> 客戶端, CLOSE-WAIT
- 服務端 -- ACK,FIN --> 客戶端, LAST-ACK
- 客戶端 -- ACK --> 服務端,CLOSED
#7. Node 的 Event Loop: 6個階段
- timer?階段: 執行到期的setTimeout / setInterval隊列回調
- I/O?階段: 執行上輪循環殘流的callback
- idle,?prepare
- poll: 等待回調
- 執行回調
- 執行定時器
- 如有到期的setTimeout / setInterval, 則返回?timer?階段
- 如有setImmediate,則前往?check?階段
- 執行setImmediate
#8. 跨域
- JSONP: 利用<script>標簽不受跨域限制的特點,缺點是只能支持?get?請求
- 設置?CORS: Access-Control-Allow-Origin:*
- postMessage
#9. 安全
- XSS攻擊: 注入惡意代碼
- cookie?設置?httpOnly
- 轉義頁面上的輸入內容和輸出內容
- CSRF: 跨站請求偽造,防護:
- get不修改數據
- 不被第三方網站訪問到用戶的?cookie
- 設置白名單,不被第三方網站請求
- 請求校驗
#五、框架:Vue
#1. nextTick
在下次dom更新循環結束之后執行延遲回調,可用于獲取更新后的dom狀態
- 新版本中默認是mincrotasks,?v-on中會使用macrotasks
- macrotasks任務的實現:
- setImmediate / MessageChannel / setTimeout
#2. 生命周期
init
- initLifecycle/Event,往vm上掛載各種屬性
- callHook: beforeCreated: 實例剛創建
- initInjection/initState: 初始化注入和?data?響應性
- created: 創建完成,屬性已經綁定, 但還未生成真實dom`
- 進行元素的掛載:?$el / vm.$mount()
- 是否有template: 解析成?render function
- *.vue文件:?vue-loader會將<template>編譯成render function
- beforeMount: 模板編譯/掛載之前
- 執行render function,生成真實的dom,并替換到dom tree中
- mounted: 組件已掛載
update
- 執行diff算法,比對改變是否需要觸發UI更新
- flushScheduleQueue
- watcher.before: 觸發beforeUpdate鉤子 -?watcher.run(): 執行watcher中的?notify,通知所有依賴項更新UI
- 觸發updated鉤子: 組件已更新
- actived / deactivated(keep-alive): 不銷毀,緩存,組件激活與失活
- destroy
- beforeDestroy: 銷毀開始
- 銷毀自身且遞歸銷毀子組件以及事件監聽
- remove(): 刪除節點
- watcher.teardown(): 清空依賴
- vm.$off(): 解綁監聽
- destroyed: 完成后觸發鉤子
上面是vue的聲明周期的簡單梳理,接下來我們直接以代碼的形式來完成vue的初始化
new Vue({})// 初始化Vue實例 function _init() {// 掛載屬性initLifeCycle(vm) // 初始化事件系統,鉤子函數等initEvent(vm) // 編譯slot、vnodeinitRender(vm) // 觸發鉤子callHook(vm, 'beforeCreate')// 添加inject功能initInjection(vm)// 完成數據響應性 props/data/watch/computed/methodsinitState(vm)// 添加 provide 功能initProvide(vm)// 觸發鉤子callHook(vm, 'created')// 掛載節點if (vm.$options.el) {vm.$mount(vm.$options.el)} }// 掛載節點實現 function mountComponent(vm) {// 獲取 render functionif (!this.options.render) {// template to render// Vue.compile = compileToFunctionslet { render } = compileToFunctions() this.options.render = render}// 觸發鉤子callHook('beforeMounte')// 初始化觀察者// render 渲染 vdom, vdom = vm.render()// update: 根據 diff 出的 patchs 掛載成真實的 dom vm._update(vdom)// 觸發鉤子 callHook(vm, 'mounted') }// 更新節點實現 funtion queueWatcher(watcher) {nextTick(flushScheduleQueue) }// 清空隊列 function flushScheduleQueue() {// 遍歷隊列中所有修改for(){// beforeUpdatewatcher.before()// 依賴局部更新節點watcher.update() callHook('updated')} }// 銷毀實例實現 Vue.prototype.$destory = function() {// 觸發鉤子callHook(vm, 'beforeDestory')// 自身及子節點remove() // 刪除依賴watcher.teardown() // 刪除監聽vm.$off() // 觸發鉤子callHook(vm, 'destoryed') }#3. Proxy 相比于 defineProperty 的優勢
- 數組變化也能監聽到
- 不需要深度遍歷監聽
#4. vue-router
mode
- hash
- history
跳轉
- this.$router.push()
- <router-link to=""></router-link>
占位
<router-view></router-view>#5. vuex
- state: 狀態中心
- mutations: 更改狀態
- actions: 異步更改狀態
- getters: 獲取狀態
- modules: 將state分成多個modules,便于管理
總結
- 上一篇: 前端面试-综合问题版
- 下一篇: 100个JavaScript代码片段