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