瀏覽器
1.1 cookie sessionStorage localStorage 區(qū)別
共同點(diǎn):
都是保存在瀏覽器端、且同源的
區(qū)別:
cookie數(shù)據(jù)始終在同源的http請(qǐng)求中攜帶(即使不需要)况增,即cookie在瀏覽器和服務(wù)器間來(lái)回傳遞劣纲,而sessionStorage和localStorage不會(huì)自動(dòng)把數(shù)據(jù)發(fā)送給服務(wù)器压真,僅在本地保存氮采。cookie數(shù)據(jù)還有路徑(path)的概念体捏,可以限制cookie只屬于某個(gè)路徑下
存儲(chǔ)大小限制也不同塔插,cookie數(shù)據(jù)不能超過(guò)4K梗摇,同時(shí)因?yàn)槊看蝖ttp請(qǐng)求都會(huì)攜帶cookie、所以cookie只適合保存很小的數(shù)據(jù)想许,如會(huì)話標(biāo)識(shí)伶授。sessionStorage和localStorage雖然也有存儲(chǔ)大小的限制,但比cookie大得多流纹,可以達(dá)到5M或更大
數(shù)據(jù)有效期不同糜烹,sessionStorage:僅在當(dāng)前瀏覽器窗口關(guān)閉之前有效;localStorage:始終有效捧颅,窗口或?yàn)g覽器關(guān)閉也一直保存景图,因此用作持久數(shù)cookie:只在設(shè)置的cookie過(guò)期時(shí)間之前有效,即使窗口關(guān)閉或?yàn)g覽器關(guān)閉
作用域不同碉哑,sessionStorage不在不同的瀏覽器窗口中共享挚币,即使是同一個(gè)頁(yè)面;localstorage在所有同源窗口中都是共享的扣典;cookie也是在所有同源窗口中都是共享的
web Storage支持事件通知機(jī)制妆毕,可以將數(shù)據(jù)更新的通知發(fā)送給監(jiān)聽(tīng)者
Storage的api接口使用更方便
1.2 如何寫(xiě)一個(gè)會(huì)過(guò)期的localStorage,說(shuō)說(shuō)想法
惰性刪除 和 定時(shí)刪除
惰性刪除
惰性刪除是指贮尖,某個(gè)鍵值過(guò)期后笛粘,該鍵值不會(huì)被馬上刪除,而是等到下次被使用的時(shí)候湿硝,才會(huì)被檢查到過(guò)期薪前,此時(shí)才能得到刪除。
var lsc = (function (self) {
var prefix = 'lsc_'
/**
* 增加一個(gè)鍵值對(duì)數(shù)據(jù)
* @param key 鍵
* @param val 值
* @param expires 過(guò)期時(shí)間关斜,單位為秒
*/
self.set = function(key, val, expires) {
key = prefix + key;
val = JSON.stringify({'val': val, 'expires': new Date().getTime() + expires * 1000});
localStorage.setItem(key, val);
};
/**
* 讀取對(duì)應(yīng)鍵的值數(shù)據(jù)
* @param key 鍵
* @returns {null|*} 對(duì)應(yīng)鍵的值
*/
self.get = function(key) {
key = prefix + key;
var val = localStorage.getItem(key);
if (!val) {
return null;
}
val = JSON.parse(val);
if (val.expires < new Date().getTime()) {
localStorage.removeItem(key);
return null;
}
return val.val;
};
return self;
}(lsc || {}));
定時(shí)刪除
定時(shí)刪除是指示括,每隔一段時(shí)間執(zhí)行一次刪除操作
- 隨機(jī)測(cè)試20個(gè)設(shè)置了過(guò)期時(shí)間的key。
- 刪除所有發(fā)現(xiàn)的已過(guò)期的key痢畜。
- 若刪除的key超過(guò)5個(gè)則重復(fù)步驟****1垛膝,直至重復(fù)500次鳍侣。
var lsc = (function (self) {
var prefix = 'lsc_'
var list = [];
//初始化
self.init = function () {
var keys = Object.keys(localStorage);
var reg = new RegExp('^' + prefix);
var temp = [];
//遍歷所有l(wèi)ocalStorage中的所有key
for (var i = 0; i < keys.length; i++) {
//找出可過(guò)期緩存的key
if (reg.test(keys[i])) {
temp.push(keys[i]);
}
}
list = temp;
};
self.init();
self.check = function () {
if (!list || list.length == 0) {
return;
}
var checkCount = 0;
while (checkCount < 500) {
var expireCount = 0;
// 隨機(jī)測(cè)試20個(gè)設(shè)置了過(guò)期時(shí)間的key
for (var i = 0; i < 20; i++) {
if (list.length == 0) {
break;
}
var index = Math.floor(Math.random() * list.length);
var key = list[index];
var val = localStorage.getItem(list[index]);
// 從list中刪除被惰性刪除的key
if (!val) {
list.splice(index, 1);
expireCount++;
continue;
}
val = JSON.parse(val);
// 刪除所有發(fā)現(xiàn)的已過(guò)期的key
if (val.expires < new Date().getTime()) {
list.splice(index, 1);
localStorage.removeItem(key);
expireCount++;
}
}
// 若刪除的key不超過(guò)5個(gè)則跳出循環(huán)
if (expireCount <= 5 || list.length == 0) {
break;
}
checkCount++;
}
}
//每隔一秒執(zhí)行一次定時(shí)刪除
window.setInterval(self.check, 1000);
return self;
}(lsc || {}));
1.3 localStorage 能跨域嗎
不能
解決辦法
- 通過(guò)postMessage來(lái)實(shí)現(xiàn)跨源通信
- 可以實(shí)現(xiàn)一個(gè)公共的iframe部署在某個(gè)域名中,作為共享域
- 將需要實(shí)現(xiàn)localStorage跨域通信的頁(yè)面嵌入這個(gè)iframe
1.4 memory cache 如何開(kāi)啟
memory cache 如何開(kāi)啟是一種比較特殊的緩存吼拥,他不受max-age倚聚、no-cache等配置的影響,即使我們不設(shè)置緩存凿可,如果當(dāng)前的內(nèi)存空間比較充裕的話惑折,一些資源還是會(huì)被緩存下來(lái)。但這種緩存是暫時(shí)的矿酵,一旦關(guān)閉了瀏覽器唬复,這一部分用于緩存的內(nèi)存空間就會(huì)被釋放掉。如果真的不想使用緩存全肮,可以設(shè)置no-store,這樣棘捣,即便是內(nèi)存緩存辜腺,也不會(huì)生效
1.5 localstorage的注意哪些問(wèn)題
- 兼容性問(wèn)題
- localStorage在瀏覽器的隱私模式下面是不可讀取的
- localStorage本質(zhì)上是對(duì)字符串的讀取,如果存儲(chǔ)內(nèi)容多的話會(huì)消耗內(nèi)存空間乍恐,會(huì)導(dǎo)致頁(yè)面變卡
- localStorage不能被爬蟲(chóng)抓取到
1.6 瀏覽器輸入U(xiǎn)RL發(fā)生了什么
- URL 解析
- DNS 查詢
- TCP 連接
- 處理請(qǐng)求
- 接受響應(yīng)
- 渲染頁(yè)面
1.7 瀏覽器是如何渲染頁(yè)面的评疗?
不同瀏覽器內(nèi)核渲染機(jī)制有所區(qū)別
- HTML 被 HTML 解析器解析成 DOM 樹(shù);
- CSS 被 CSS 解析器解析成 CSSOM 樹(shù)茵烈;
- 結(jié)合 DOM 樹(shù)和 CSSOM 樹(shù)百匆,生成一棵渲染樹(shù)(Render Tree),這一過(guò)程稱為 Attachment呜投;
- 生成布局(flow)加匈,瀏覽器在屏幕上“畫(huà)”出渲染樹(shù)中的所有節(jié)點(diǎn);
- 將布局繪制(paint)在屏幕上仑荐,顯示出整個(gè)頁(yè)面雕拼。
webkit
Gecko
1.8 重繪、重排
概念
- 重排(Reflow):當(dāng)渲染樹(shù)的一部分必須更新并且節(jié)點(diǎn)的尺寸發(fā)生了變化粘招,瀏覽器會(huì)使渲染樹(shù)中受到影響的部分失效啥寇,并重新構(gòu)造渲染樹(shù)
- 重繪(Repaint):是在一個(gè)元素的外觀被改變所觸發(fā)的瀏覽器行為,瀏覽器會(huì)根據(jù)元素的新屬性重新繪制洒扎,使元素呈現(xiàn)新的外觀辑甜。比如改變某個(gè)元素的背景色、文字顏色袍冷、邊框顏色等等
區(qū)別:
重繪不一定需要重排(比如顏色的改變)磷醋,重排必然導(dǎo)致重繪(比如改變網(wǎng)頁(yè)位置)
引發(fā)重排
- 添加、刪除可見(jiàn)的dom
- 元素的位置改變
- 元素的尺寸改變(外邊距难裆、內(nèi)邊距子檀、邊框厚度镊掖、寬高、等幾何屬性)
- 頁(yè)面渲染初始化
- 瀏覽器窗口尺寸改變
- 獲取某些屬性褂痰。當(dāng)獲取一些屬性時(shí)亩进,瀏覽器為取得正確的值也會(huì)觸發(fā)重排,它會(huì)導(dǎo)致隊(duì)列刷新,這些屬性包括:offsetTop缩歪、offsetLeft归薛、 offsetWidth、offsetHeight匪蝙、scrollTop主籍、scrollLeft、scrollWidth逛球、scrollHeight千元、clientTop、clientLeft颤绕、clientWidth幸海、clientHeight、getComputedStyle() (currentStyle in IE)奥务。所以物独,在多次使用這些值時(shí)應(yīng)進(jìn)行緩存
優(yōu)化方案
瀏覽器會(huì)維護(hù)1個(gè)隊(duì)列,把所有會(huì)引起重排氯葬,重繪的操作放入這個(gè)隊(duì)列挡篓,等隊(duì)列中的操作到一定數(shù)量或者到了一定時(shí)間間隔防楷,瀏覽器就會(huì)flush隊(duì)列寸五,進(jìn)行一批處理茶没,這樣多次重排坦报,重繪變成一次重排重繪
減少 reflow/repaint:
不要一條一條地修改 DOM 的樣式吝沫≈墒В可以先定義好 css 的 class鸣皂,然后修改 DOM 的className瞪讼。
不要把 DOM 結(jié)點(diǎn)的屬性值放在一個(gè)循環(huán)里當(dāng)成循環(huán)里的變量瞻坝。
為動(dòng)畫(huà)的 HTML 元件使用 fixed 或 absoult 的 position蛛壳,那么修改他們的 CSS 是不會(huì)reflow 的。
千萬(wàn)不要使用 table 布局所刀。因?yàn)榭赡芎苄〉囊粋€(gè)小改動(dòng)會(huì)造成整個(gè) table 的重新布局衙荐。(table及其內(nèi)部元素除外,它可能需要多次計(jì)算才能確定好其在渲染樹(shù)中節(jié)點(diǎn)的屬性浮创,通常要花3倍于同等元素的時(shí)間忧吟。這也是為什么我們要避免使用table做布局的一個(gè)原因。)
不要在布局信息改變的時(shí)候做查詢(會(huì)導(dǎo)致渲染隊(duì)列強(qiáng)制刷新)
1.9 事件循環(huán)(Event loop)
主線程從"任務(wù)隊(duì)列"中讀取執(zhí)行事件斩披,這個(gè)過(guò)程是循環(huán)不斷的溜族,這個(gè)機(jī)制被稱為事件循環(huán)
JavaScript 的事件分兩種
- 宏任務(wù):包括整體代碼 script讹俊,setTimeout,setInterval
- 微任務(wù):Promise.then(非 new Promise)煌抒,process.nextTick(node 中)
具體執(zhí)行:
事件的執(zhí)行順序——先執(zhí)行宏任務(wù)仍劈,然后執(zhí)行微任務(wù),任務(wù)有同步的任務(wù)和異步的任務(wù)寡壮,同步的進(jìn)入主線程贩疙,異步的進(jìn)入 Event Table 并注冊(cè)函數(shù),異步事件完成后况既,會(huì)將回調(diào)函數(shù)放在隊(duì)列中这溅,如果還有異步的宏任務(wù),那么就會(huì)進(jìn)行循環(huán)執(zhí)行上述的操作
主 線程會(huì)不斷從任務(wù)隊(duì)列中按順序取任務(wù)執(zhí)行棒仍,每執(zhí)行完一個(gè)任務(wù)都會(huì)檢查microtask隊(duì)列是否為空(執(zhí)行完一個(gè) 任務(wù)的具體標(biāo)志是函數(shù)執(zhí)行棧為空)悲靴,如果不為空則會(huì)一次性執(zhí)行完所有microtask。然后再進(jìn)入下一個(gè)循環(huán)去 任務(wù)隊(duì)列中取下一個(gè)任務(wù)執(zhí)行
詳細(xì)步驟:
選擇當(dāng)前要執(zhí)行的宏任務(wù)隊(duì)列莫其,選擇一個(gè)最先進(jìn)入任務(wù)隊(duì)列的宏任務(wù)对竣,如果沒(méi)有宏任務(wù)可以選擇,則會(huì) 跳轉(zhuǎn)至microtask的執(zhí)行步驟榜配。
將事件循環(huán)的當(dāng)前運(yùn)行宏任務(wù)設(shè)置為已選擇的宏任務(wù)。
運(yùn)行宏任務(wù)吕晌。
將事件循環(huán)的當(dāng)前運(yùn)行任務(wù)設(shè)置為null蛋褥。
將運(yùn)行完的宏任務(wù)從宏任務(wù)隊(duì)列中移除。
microtasks步驟:進(jìn)入microtask檢查點(diǎn)睛驳。
更新界面渲染烙心。
返回第一步。
執(zhí)行進(jìn)入microtask檢查的的具體步驟如下:
- 設(shè)置進(jìn)入microtask檢查點(diǎn)的標(biāo)志為true乏沸。
- 當(dāng)事件循環(huán)的微任務(wù)隊(duì)列不為空時(shí):選擇一個(gè)最先進(jìn)入microtask隊(duì)列的microtask淫茵;設(shè)置事
件循環(huán)的當(dāng) 前運(yùn)行任務(wù)為已選擇的microtask;運(yùn)行microtask蹬跃;設(shè)置事件循環(huán)的當(dāng)前運(yùn)行任務(wù)
為null匙瘪;將運(yùn)行結(jié)束 的microtask從microtask隊(duì)列中移除。
- 對(duì)于相應(yīng)事件循環(huán)的每個(gè)環(huán)境設(shè)置對(duì)象(environment settings object),通知它們哪些
promise為 rejected蝶缀。
- 清理indexedDB的事務(wù)丹喻。
- 設(shè)置進(jìn)入microtask檢查點(diǎn)的標(biāo)志為false。
注意
當(dāng)前執(zhí)行棧執(zhí)行完畢時(shí)會(huì)立刻先處理所有微任務(wù)隊(duì)列中的事件,然后再去宏任務(wù)隊(duì)列中取出一個(gè)事件翁都。同一次事件循環(huán)中,微任務(wù)永遠(yuǎn)在宏任務(wù)之前執(zhí)行
1.10 let a = 1 掛載在哪里碍论?
var a 掛載在window下。而let是掛載在 全局函數(shù)下面