前記
啊侥猩,快樂的時(shí)光總是很短暫。
2017年的第一場面試抵赢,比想的要更早些 :-D
好呢欺劳!總有辣么幾個(gè)問題,面試總要問铅鲤!
嘛划提!想起幾個(gè)總結(jié)幾個(gè),答案肯定不標(biāo)準(zhǔn)邢享,隨時(shí)改鹏往,基本就是這樣。
談一談對性能優(yōu)化的認(rèn)識(shí)骇塘?
前端優(yōu)化的途徑有很多,大致可以分為兩類伊履,第一類是頁面級別的優(yōu)化,例如 HTTP請求數(shù)款违、腳本的無阻塞加載唐瀑、內(nèi)聯(lián)腳本的位置優(yōu)化等 ;第二類則是代碼級別的優(yōu)化,例如 Javascript中的DOM 操作優(yōu)化奠货、CSS選擇符優(yōu)化介褥、圖片優(yōu)化以及 HTML結(jié)構(gòu)優(yōu)化等等座掘。
- 頁面級優(yōu)化
- 減少http請求次數(shù)递惋。
大部分響應(yīng)時(shí)間花在下載網(wǎng)頁內(nèi)容(images柔滔,stylesheets,javascript萍虽,script睛廊,flash等)。減少次數(shù)是縮短響應(yīng)時(shí)間的關(guān)鍵杉编!可以通過簡化頁面設(shè)計(jì)來減少請求次數(shù)超全,但頁面內(nèi)容比較多的時(shí)候可以采用以下技巧: - 從設(shè)計(jì)實(shí)現(xiàn)層面簡化頁面
保持頁面簡潔、減少資源的使用是最直接的邓馒。 - 合理設(shè)置 HTTP 緩存
原則很簡單嘶朱,能緩存越多越好,能緩存越久越好光酣。
HTTP 緩存的四種風(fēng)味與緩存策略 - 資源合并與壓縮
如果可以的話疏遏,盡可能的將外部的腳本、樣式進(jìn)行合并救军,多個(gè)合為一個(gè)财异。另外, CSS唱遭、 Javascript戳寸、Image 都可以用相應(yīng)的工具進(jìn)行壓縮,壓縮后往往能省下不少空間拷泽。 - css sprites
合并 CSS圖片疫鹊,減少請求數(shù)的又一個(gè)好辦法。 - inline images(不是很了解這個(gè))
- Lazy Load Images
- 將外部腳本置底
- 異步執(zhí)行 inline 腳本
- Lazy Load Javascript
- 將 css 放在 head 中
- 異步請求 callback
- 減少不必要的 HTTP 跳轉(zhuǎn)
- 避免重復(fù)的資源請求
- 減少http請求次數(shù)递惋。
- 代碼級優(yōu)化
- Javascript
1. DOM
DOM操作應(yīng)該是腳本中最耗性能的一類操作跌穗,例如增加订晌、修改、刪除 DOM元素或者對 DOM集合進(jìn)行操作蚌吸。如果腳本中包含了大量的 DOM 操作則需要注意以下幾點(diǎn):
1. HTML Collection(HTML收集器锈拨,返回的是一個(gè)數(shù)組內(nèi)容信息)
在腳本中 document.images、document.forms 羹唠、getElementsByTagName()返回的都是 HTMLCollection類型的集合奕枢,在平時(shí)使用的時(shí)候大多將它作為數(shù)組來使用,因?yàn)樗?length屬性佩微,也可以使用索引訪問每一個(gè)元素缝彬。不過在訪問性能上則比數(shù)組要差很多,原因是這個(gè)集合并不是一個(gè)靜態(tài)的結(jié)果哺眯,它表示的僅僅是一個(gè)特定的查詢谷浅,每次訪問該集合時(shí)都會(huì)重新執(zhí)行這個(gè)查詢從而更新查詢結(jié)果。所謂的 “訪問集合” 包括讀取集合的 length屬性、訪問集合中的元素一疯。
因此撼玄,當(dāng)你需要遍歷 HTML Collection的時(shí)候,盡量將它轉(zhuǎn)為數(shù)組后再訪問墩邀,以提高性能掌猛。即使不轉(zhuǎn)換為數(shù)組,也請盡可能少的訪問它眉睹,例如在遍歷的時(shí)候可以將 length屬性荔茬、成員保存到局部變量后再使用局部變量。
2. Reflow & Repaint(不是很了解)
除了上面一點(diǎn)之外竹海, DOM操作還需要考慮瀏覽器的 Reflow 和 Repaint 慕蔚,因?yàn)檫@些都是需要消耗資源的。
2. 慎用 with(不了解)
with(obj){ p = 1};
代碼塊的行為實(shí)際上是修改了代碼塊中的執(zhí)行環(huán)境 斋配,將obj放在了其作用域鏈的最前端坊萝,在 with 代碼塊中訪問非局部變量是都是先從 obj 上開始查找,如果沒有再依次按作用域鏈向上查找许起,因此使用 with 相當(dāng)于增加了作用域鏈長度十偶。而每次查找作用域鏈都是要消耗時(shí)間的,過長的作用域鏈會(huì)導(dǎo)致查找性能下降园细。
因此惦积,除非你能肯定在 with 代碼中只訪問 obj 中的屬性,否則慎用 with猛频,替代的可以使用局部變量緩存需要訪問的屬性狮崩。
3. 避免使用 eval 和 Function
每次 eval 或 Function 構(gòu)造函數(shù)作用于字符串表示的源代碼時(shí),腳本引擎都需要將源代碼轉(zhuǎn)換成可執(zhí)行代碼鹿寻。這是很消耗資源的操作 —— 通常比簡單的函數(shù)調(diào)用慢 100倍以上睦柴。
eval 函數(shù)效率特別低,由于事先無法知曉傳給 eval 的字符串中的內(nèi)容毡熏,eval 在其上下文中解釋要處理的代碼坦敌,也就是說編譯器無法優(yōu)化上下文,因此只能有瀏覽器在運(yùn)行時(shí)解釋代碼痢法。這對性能影響很大狱窘。
Function 構(gòu)造函數(shù)比 eval 略好,因?yàn)槭褂么舜a不會(huì)影響周圍代碼;但其速度仍很慢财搁。
此外蘸炸,使用 eval和 Function也不利于Javascript 壓縮工具執(zhí)行壓縮。
4. 減少作用域鏈查找
這一點(diǎn)在循環(huán)中是尤其需要注意的問題尖奔。如果在循環(huán)中需要訪問非本作用域下的變量時(shí)請?jiān)诒闅v之前用局部變量緩存該變量搭儒,并在遍歷結(jié)束后再重寫那個(gè)變量穷当,這一點(diǎn)對全局變量尤其重要,因?yàn)槿肿兞刻幱谧饔糜蜴湹淖铐敹搜秃蹋L問時(shí)的查找次數(shù)是最多的膘滨。
此外,要減少作用域鏈查找還應(yīng)該減少閉包的使用稀拐。
5. 數(shù)據(jù)訪問
Javascript中的數(shù)據(jù)訪問包括直接量 (字符串、正則表達(dá)式 )丹弱、變量德撬、對象屬性以及數(shù)組,其中對直接量和局部變量的訪問是最快的躲胳,對對象屬性以及數(shù)組的訪問需要更大的開銷蜓洪。當(dāng)出現(xiàn)以下情況時(shí),建議將數(shù)據(jù)放入局部變量:
1. 對任何對象屬性的訪問超過 1次
2. 對任何數(shù)組成員的訪問次數(shù)超過 1次
另外坯苹,還應(yīng)當(dāng)盡可能的減少對對象以及數(shù)組深度查找隆檀。
6. 字符串拼接
在 Javascript中使用"+" 號來拼接字符串效率是比較低的,因?yàn)槊看芜\(yùn)行都會(huì)開辟新的內(nèi)存并生成新的字符串變量粹湃,然后將拼接結(jié)果賦值給新變量恐仑。與之相比更為高效的做法是使用數(shù)組的 join方法,即將需要拼接的字符串放在數(shù)組中最后調(diào)用其 join 方法得到結(jié)果为鳄。不過由于使用數(shù)組也有一定的開銷裳仆,因此當(dāng)需要拼接的字符串較多的時(shí)候可以考慮用此方法。
2. CSS選擇符
3. HTML
4. Image壓縮
- Javascript
摘自:Web前端應(yīng)該從哪些方面來優(yōu)化網(wǎng)站?
另外:
- 減少http請求次數(shù)
80%的響應(yīng)時(shí)間花在下載網(wǎng)頁內(nèi)容(images, stylesheets, javascripts, scripts, flash等)孤钦。減少請求次數(shù)是縮短響應(yīng)時(shí)間的關(guān)鍵歧斟!可以通過簡化頁面設(shè)計(jì)來減少請求次數(shù)。 - 減少DNS查詢次數(shù)
DNS查詢也消耗響應(yīng)時(shí)間偏形,如果我們的網(wǎng)頁內(nèi)容來自各個(gè)不同的domain (比如嵌入了開放廣告静袖,引用了外部圖片或腳本),那么客戶端首次解析這些domain也需要消耗一定的時(shí)間俊扭。DNS查詢結(jié)果緩存在本地系統(tǒng)和瀏覽器中一段時(shí)間队橙,所以DNS查詢一般是對首次訪問響應(yīng)速度有所影響。下面是我清空本地dns后訪問博客園主頁dns的查詢請求萨惑。 - 緩存Ajax
Ajax可以幫助我們異步的下載網(wǎng)頁內(nèi)容喘帚,但是有些網(wǎng)頁內(nèi)容即使是異步的,用戶還是在等待它的返回結(jié)果咒钟,例如ajax的返回是用戶聯(lián)系人的下拉列表吹由。所以我們還是要注意盡量應(yīng)用以下規(guī)則提高ajax的響應(yīng)速度。 - 延遲加載
這里討論延遲加載需要我們知道我們的網(wǎng)頁最初加載需要的最小內(nèi)容集是什么朱嘴。剩下的內(nèi)容就可以推到延遲加載的集合中倾鲫。
Javascript是典型的可以延遲加載內(nèi)容粗合。一個(gè)比較激進(jìn)的做法是開發(fā)網(wǎng)頁時(shí)先確保網(wǎng)頁在沒有Javascript的時(shí)候也可以基本工作,然后通過延遲加載腳本來完成一些高級的功能乌昔。
另:
唯快不破:Web 應(yīng)用的 13 個(gè)優(yōu)化步驟
從輸入U(xiǎn)RL到瀏覽器顯示頁面發(fā)生了什么?
這個(gè)過程可以大致分為兩個(gè)部分:網(wǎng)絡(luò)通信和頁面渲染隙疚。
- 網(wǎng)絡(luò)通信
- 在瀏覽器中輸入 url
用戶輸入 url ,例如http://www.baidu.com
磕道。其中http
為協(xié)議供屉,www.baidu.com
為網(wǎng)絡(luò)地址。一般網(wǎng)絡(luò)地址可以為域名或IP地址溺蕉。 - 應(yīng)用層 DNS 解析域名伶丐。
客戶端先檢查本地是否有對應(yīng)的 IP 地址,若找到則返回響應(yīng)的 IP 地址疯特。若沒找到則請求上級 DNS 服務(wù)器哗魂,直至找到或到根節(jié)點(diǎn)。 - 應(yīng)用層客戶端發(fā)送 HTTP 請求
HTTP 請求包括請求報(bào)頭和請求主體兩個(gè)部分. - 傳輸層 TCP 傳輸報(bào)文
TCP協(xié)議通過“三次握手”等方法保證傳輸?shù)陌踩煽俊?/li> - 網(wǎng)絡(luò)層IP協(xié)議查詢 MAC 地址
- 數(shù)據(jù)到達(dá)數(shù)據(jù)鏈路層
- 服務(wù)器接收數(shù)據(jù)
- 服務(wù)器響應(yīng)請求
服務(wù)接收到客戶端發(fā)送的HTTP請求后漓雅,查找客戶端請求的資源录别,并返回響應(yīng)報(bào)文,響應(yīng)報(bào)文中包括一個(gè)重要的信息——狀態(tài)碼邻吞。其中比較常見的是200 OK表示請求成功组题。301表示永久重定向,即請求的資源已經(jīng)永久轉(zhuǎn)移到新的位置抱冷。在返回301狀態(tài)碼的同時(shí)往踢,響應(yīng)報(bào)文也會(huì)附帶重定向的url,客戶端接收到后將http請求的url做相應(yīng)的改變再重新發(fā)送徘层。404 not found 表示客戶端請求的資源找不到峻呕。
狀態(tài)碼由三位數(shù)字組成:- 1xx:指示信息–表示請求已接收,繼續(xù)處理趣效。
- 2xx:成功–表示請求已被成功接收瘦癌、理解、接受跷敬。
- 3xx:重定向–要完成請求必須進(jìn)行更進(jìn)一步的操作讯私。
- 4xx:客戶端錯(cuò)誤–請求有語法錯(cuò)誤或請求無法實(shí)現(xiàn)。
- 5xx:服務(wù)器端錯(cuò)誤–服務(wù)器未能實(shí)現(xiàn)合法的請求西傀。
- 服務(wù)器返回相應(yīng)文件
- 頁面渲染
瀏覽器是一個(gè)邊解析邊渲染的過程斤寇。首先瀏覽器解析HTML文件構(gòu)建DOM樹,然后解析CSS文件構(gòu)建渲染樹拥褂,等到渲染樹構(gòu)建完成后娘锁,瀏覽器開始布局渲染樹并將其繪制到屏幕上。
摘自:從輸入U(xiǎn)RL到瀏覽器顯示頁面發(fā)生了什么
另:
一個(gè)頁面從輸入U(xiǎn)RL到頁面加載顯示完成饺鹃,這個(gè)過程都發(fā)生什么莫秆?
從輸入U(xiǎn)RL到頁面加載發(fā)生了什么?
如何垂直居中一個(gè)浮動(dòng)元素间雀?
/* 已知寬高 */
#div1{
background-color:#6699FF;
width:200px;
height:200px;
position: absolute; /*父元素需要相對定位*/
top: 50%;
left: 50%;
margin-top:-100px ;
margin-left: -100px;
}
/* 未知寬高 */
#div1{
width: 200px;
height: 200px;
background-color: #6699FF;
margin:auto;
position: absolute;/*父元素需要相對定位*/
left: 0;
top: 0;
right: 0;
bottom: 0;
}
那么問題來了,如何垂直居中一個(gè)<img>?(用更簡便的方法镊屎。)
/*<img>的容器設(shè)置如下*/
#container
{
display:table-cell;
text-align:center;
vertical-align:middle;
}
jquery 常用的選擇器
- 基本選擇器
- ID選擇器 $(“#id”) 獲取指定ID的元素
- 類選擇器 $(“.class”) 獲取同一類class的元素
- 標(biāo)簽選擇器 $(“div”) 獲取同一類標(biāo)簽的所有元素
- 并集選擇器 $(“div,p,li”) 使用逗號分隔惹挟,只要符合條件之一就可。獲取所有的div缝驳、p连锯、li元素
- 交集選擇器(標(biāo)簽指定式選擇器) $(“div.redClass”) 注意選擇器1和選擇器2之間沒有空格,class為redClass的div元素用狱,注意區(qū)分后代選擇器运怖。
- 層級選擇器
- 子代選擇器 $(“ul>li”) 使用>號,獲取兒子層級的元素齿拂,注意,并不會(huì)獲取孫子層級的元素
- 后代選擇器 $(“ul li”) 使用空格肴敛,代表后代選擇器署海,獲取ul下的所有l(wèi)i元素,包括孫子等
- 過濾選擇器
- :eq(index) $(“l(fā)i:eq(2)”).css(“color”, ”red”) 獲取到的li元素中医男,選擇索引號為2的元素砸狞,索引號index從0開始。
- :odd $(“l(fā)i:odd”).css(“color”, ”red”) 獲取到的li元素中镀梭,選擇索引號為奇數(shù)的元素
- :even $(“l(fā)i:even”).css(“color”, ”red”) 獲取到的li元素中刀森,選擇索引號為偶數(shù)的元素
- 篩選選擇器(方法)
- children(selector) $(“ul”).children(“l(fā)i”) 相當(dāng)于$(“ul>li”),子類選擇器
- find(selector) $(“ul”).find(“l(fā)i”) 相當(dāng)于$(“ul li”),后代選擇器
- siblings(selector) $(“#first”).siblings(“l(fā)i”) 查找兄弟節(jié)點(diǎn)报账,不包括自己本身研底。
- parent() $(“#first”).parent() 查找父親
- eq(index) $(“l(fā)i”).eq(2) 相當(dāng)于$(“l(fā)i:eq(2)”),index從0開始
bind 算法
只要掌握核心幾點(diǎn)就沒問題:
- Function.bind返回的也是一個(gè)函數(shù),所以注定發(fā)生了閉包透罢,
- 在返回的這個(gè)函數(shù)中去調(diào)用一個(gè)其他的函數(shù)榜晦,這其實(shí)本質(zhì)上就是函數(shù)鉤子(HOOK)
關(guān)于在JS里的函數(shù)鉤子,我認(rèn)為只需要維護(hù)以下三點(diǎn)即可:
- 保持函數(shù)的this指向
- 保持函數(shù)的所有參數(shù)都傳遞到目標(biāo)函數(shù)
- 保持函數(shù)的返回值
有了以上這幾點(diǎn)羽圃,這個(gè)函數(shù)就非常好寫了乾胶,下面是MSDN上的標(biāo)準(zhǔn)Polyfill:
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP
? this
: oThis || this,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
摘自: Web前端面試小記
最后
嘛!來啊朽寞,互相傷害笆读!面試什么的脑融,傷的多惹喻频,并不算什么【傲嬌臉】!
:-P