混子前端彙總面試常問知識點

混子前端分享一些面試常問乾貨,同時也幫助你們複習和鞏固一波基礎

ps:文章所有由我的總結,知識點分析不全面請自行查漏補缺javascript

頁面佈局

你必須知道的經典問題:

假設高度已知,寫出三欄佈局,其中左欄/右欄寬度各爲300px;中間自適應。css

(ps:此題做爲一個小白也要至少答出3種方法,如下分享連混子前端都知道的5種方法)html

  • 浮動float  [優勢:兼容性好;缺點:脫離文檔流]

HTML部分:
<section class="layout float">
    <article class='left-right-center'>
        <div class="left"></div>
        <div class="right"></div>
        <div class="center"></div>
    </article>
</section>

CSS部分:
.layout article div{
    height: 100px;
}
.layout.float .left{
    float: left;
    width: 300px;
    background: red;
}
.layout.float .right{
    float: right;
    width: 300px;
    background: blue;
}
.layout.float .center{
    background: yellow;
}
複製代碼

  • 定位absolute  [優勢:快捷配合JS使用;缺點:子元素脫離文檔流,可以使用性差]

HTML部分:
<section class="layout absolute">
    <article class='left-right-center'>
        <div class="left"></div>
        <div class="center"></div>
        <div class="right"></div>
    </article>
</section>

CSS部分:.layout article div{
    height: 100px;
}
.layout.absolute .left,.layout.absolute .right,.layout.absolute .center{
    position: absolute;
}
.layout.absolute .left{
    left: 0px;
    width: 300px;
    background: red;
}
.layout.absolute .right{
    right: 0px;
    width: 300px;
    background: blue;
}
.layout.absolute .center{
    left: 300px;
    right: 300px;
    background: yellow;
}
複製代碼

  • 彈性盒flex  [完美解決上面2個佈局的缺點]

HTML部分:
<section class="layout flexbox">
    <article class='left-right-center'>
        <div class="left"></div>
        <div class="center"></div>
        <div class="right"></div>
    </article>
</section>

CSS部分:
.layout article div{
    height: 100px;
}
.layout.flexbox .left-right-center{
    display: flex;
}
.layout.flexbox .left{
    width: 300px;
    background: red;
}
.layout.flexbox .center{
    flex: 1;
    background: yellow;
}
.layout.flexbox .right{
    width: 300px;
    background: blue;
}
複製代碼

  • 表格table  [優勢:兼容性好;缺點:其中某個單元格高度超出,其它兩欄也超出]

HTML部分:
<section class="layout table">
    <article class='left-right-center'>
        <div class="left"></div>
        <div class="center"></div>
        <div class="right"></div>
    </article>
</section>

CSS部分:
.layout article div{
    height: 100px;
}
.layout.table .left-right-center{
    width: 100%;
    display: table;
    height: 100px;
}
.layout.table .left-right-center>div{
    display: table-cell;
}
.layout.table .left{
    width: 300px;
    background: red;
}
.layout.table .center{
    background: yellow;
}
.layout.table .right{
    width: 300px;
    background: blue;
}
複製代碼

  • 網格grid  [不用柵格來模擬網格佈局 / 代碼量簡化不少]

HTML部分:
<section class="layout grid">
    <article class='left-right-center'>
        <div class="left"></div>
        <div class="center"></div>
        <div class="right"></div>
    </article>
</section>

CSS部分:
.layout article div{
    height: 100px;
}
.layout.grid .left-right-center{
    display: grid;
    width: 100%;
    grid-template-rows: 100px;
    grid-template-columns: 300px auto 300px;
}
.layout.grid .left{
    background: red;
}
.layout.grid .center{
    background: yellow;
}
.layout.grid .right{
    background: blue;
}複製代碼

補充:未知高度時,彈性盒flex / 表格table很適用,不修改代碼前端

思考:上下高度固定,中間自適應(ps:混子前端都會的,你也必須會!)html5

CSS盒模型

你必須知道的盒子模型:
java

標準盒模型 + IE盒模型

標準模型:不包含padding和border值,content即盒子width / heightweb

IE盒模型:盒子width / height包含padding和border值面試


CSS如何設置這兩種模型:

box-sizing: content-box;    // 標準盒模型(默認)
box-sizing: border-box;    // IE盒模型複製代碼

JS如何設置盒模型對應寬和高

一、dom.style.width/height;    // 獲取內聯樣式寬/高[侷限性]
二、dom.currentStyle.width/height;    // 獲取是渲染後的寬/高 [只有ie支持]三、window.getComputedStyle(dom).width/height;    // 支持chrome、firefox
四、dom.getBoundingClientRect().width/height[left/right/top/bottom];    // 不多人知道,獲取渲染後width/height,計算一個元素絕對位置,根據視窗(viewport),經常使用獲取left/right/top/bottom複製代碼

邊距重疊

常見問題:子元素高度100px,margin-top: 10px,此時父元素高度 ?ajax

解:父元素是100px;若是父元素設置overflow: hidden,就是110px;算法

[就是給父元素建立BFC(塊級格式化)] 

BFC:邊距重疊解決方案

基本概念:塊級格式化上下文

原理(BFC渲染規則):

  1. BFC垂直方向邊距不發生重疊
  2. BFC區域不會與浮動元素重疊(清除浮動)
  3. BFC在頁面是獨立容器,外面元素不會影響裏面元素,裏面元素不會影響外面元素
  4. 計算BFC高度時候,浮動元素也會參與計算

怎樣建立BFC,除了給父元素添加overflow: hidden建立BFC,還有那些?

  1. float值除none之外
  2. position值不是static或relative就建立了BFC
  3. display: inline-block,table-cell和table相關的幾個屬性均可以建立
  4. overflow不是visiable也能夠建立

DOM事件

你必須知道幾個關於DOM的知識點

DOM事件級別(標準級別)

一代:DOM 0級

element.onClick = function(){
    // TODO
}複製代碼

爲何從DOM 0級直接到DOM 2級了?DOM 1級標準沒有設定與事件相關的東西

二代:DOM 2級

element.addEventListener('click', function(){
    // TODO
}, false);複製代碼

三代:DOM 3級 

新增了鼠標,鍵盤等事件

element.addEventListener('keyup', function(){
    // TODO
}, false);複製代碼

事件模型

DOM事件流

瀏覽器在爲當前頁面與用戶交互過程當中,如:點擊鼠標是怎樣傳到頁面上(如何響應的)?

混子前端認爲分爲三個階段:

  1. 捕獲[事件經過捕獲到達目標元素]
  2. 目標階段[點擊按鈕]
  3. 冒泡到window對象[目標階段上傳到window對象]

DOM事件捕獲的具體流程

捕獲:window > document > HTML > body  > HTML結構 > 一層層傳遞到目標元素

冒泡:捕獲反過來就是冒泡,從目標元素傳遞到window

Event對象

  1. event.perventDefault()阻止默認事件,如:<a>標籤綁定click事件,點擊會阻止跳轉默認行爲
  2. event.stopPropagation()阻止冒泡行爲
  3. event.stopImmediatePropagation(),如:button綁定兩個click事件(a,b),如今想a執行後不執行b[在a響應函數添加event.stopImmediatePropagation(),會阻止b的執行],用來阻斷
  4. event.currentTarget當前綁定事件元素
  5. event.target當前被點擊元素
注意:4和5在沒冒泡是同樣的值,但在用了事件委託的狀況就不同了。

如:ul > li 點擊觸發,給ul綁定onclick方法,此時:
target:<li>

currentTarget:<ul>

自定義事件

var eve = new Event('custome');    // 聲明自定義事件
ev.addEventListener('custome', function(){
    console.log('custome');
});
ev.dispatchEvent(eve);    // dispatchEvent api觸發複製代碼

HTTP協議

協議主要特色

  1. 簡單快速
  2. 靈活
  3. 無鏈接
  4. 無狀態
ps:你覺得這樣就完了?如下是你必須知道的擴展說明:
  1. uri是固定的,圖片等(簡單快速)
  2. http頭部分有類型,經過http協議完成不一樣數據類型傳輸(靈活)
  3. 連接一次會斷掉,不會保持連接(無鏈接)
  4. 客戶端和服務端請求圖片,http協議幫助創建連接和傳輸,完成後斷開,下次客戶端在過來,服務端是沒辦法區分上次和此次是否是同一我的,即服務端沒記錄(無鏈接)

補充:網站如今記住的是身份,不是http協議作到的,而是經過服務端加session作到的

HTTP報文

客戶端輸入url,服務端響應;

報文分爲2部分,即:請求報文 響應報文,如圖所示:

HTTP方法

GET:獲取資源

POST:傳輸資源

PUT:更新資源
DELETE:刪除資源

HEAD:得到報文首部

POST和GET的區別

  1. GET在瀏覽器回退時是無害的,而POST會再次提交請求
  2. GET產生的URL地址能夠被收藏,而POST不能夠
  3. GET請求會被瀏覽器主動緩存,而POST不會,除非手動設置
  4. GET請求只能進行URL編碼,而POST支持多種編碼方式
  5. GET請求參數會被完整保留在瀏覽器歷史記錄裏,而POST中的參數不會被保留 
  6. GET請求在URL中傳遞的參數是有長度限制的(2kb),而POST沒有限制
  7. 對參數的數據類型,GET只接受ASCII字符,而POST沒有限制
  8. GET比POST更不安全,由於參數直接暴露在URL上,因此不能用來傳遞敏感信息
  9. GET參數經過URL傳遞,而POST放在Request body中

HTTP狀態碼

大概要知道的:
  1. 1xx:指示信息
  2. 2xx:成功
  3. 3xx:重定向
  4. 4xx:客戶端錯誤
  5. 5xx:服務器錯誤

若是你對本身有追求,那如下你也要知道:

200:客戶端請求成功

206:客戶發送了一個帶有Range頭的GET請求,服務器完成;如:客戶端發起0-10000字節請求,服務器返回206,服務端會根據Range頭部截一部分,響應回客戶端(客戶端要求的Range範圍)

301:所請求頁面已經轉移至新的url(永久重定向)

302:所請求頁面已經臨時轉移至新的url(臨時重定向)

304:客戶端口有緩衝的文檔併發出了一個條件性的請求,服務器告訴客戶端,原來緩衝的文檔還能夠繼續使用 [ 服務器告訴客戶端有緩存,能夠取緩存用,不用在服務器取 ]

400:客戶端有語法錯誤,不能被服務器所理解

401:請求未經受權

403:對被請求頁面訪問禁止

404:資源不存在

500:服務器錯誤

503:服務器臨時過載或宕機

HTTP持久鏈接

HTTP協議採用「請求 -- 應答」模式,當使用普通模式,即非keep-alive(持久鏈接)時,每一個請求/應答客戶端和服務器都要新建一個鏈接,完成以後當即斷開鏈接(HTTP無鏈接協議)

當使用keep-alive模式(持久鏈接)時,keep-alive功能使客戶端到服務端的鏈接持久有效,當出現對服務器的後續請求時,keep-alive功能避免了創建或者從新創建鏈接

注意:持久鏈接1.1版本才支持,1.0版本不支持

管線化

當使用持久鏈接的狀況下,某個鏈接上消息的傳遞相似於:

請求1 > 響應1 > 請求2 > 響應2 > 請求3 > 響應3

某個鏈接上的消息變成了相似如下這樣(管線化):

[請求1 > 請求2 > 請求3 ] > [響應1 > 響應2 > 響應3]

注意:

  1. 只有GET和HEAD請求能夠管線化,POST有所限制
  2. 服務端支持才能夠,chrome和firefox默認並未開啓管線化

原型鏈

建立對象的幾種方法

  1. 字面量

    var o1 = {name: 'o1'};
    var o11 = new Object({name: 'o11'});複製代碼

  2. 顯示構造函數

    var M = function(){this.name='o2'};
    var o2 = new M();複製代碼

  3. 借用Object.create()

    var P = {name: 'o3'};
    var o3 = Object.create(P)複製代碼

Object.create()建立對象是用原型鏈鏈接的;o3的__proto__指向P對象,下文會說o3.__proto__指向構造函數的原型對象,也就是說原型就是P

o3.__proto__ === P;    // true複製代碼

ps:Object.create()方法是把參數中對象做爲一個新對象原型對象賦給o3的,o3自己不具有name屬性,是經過原型鏈鏈接它的原型對象;

原型 / 構造函數 / 實例 / 原型鏈


ps:經過prototype 和 __proto__完成原型鏈向上找的過程,Object.prototype是整個原型鏈的頂端,到這裏未知

問題來了:原型對象和原型鏈到底起什麼做用?

解答:構造函數中增長不少屬性和方法,實例就能夠共用,當有不少個實例想去公用方法時,不可能每一個都拷貝一份,如今考慮應該有個東西存,這個東西就是原型對象

注意:

  1. 構造函數 > 函數纔會有prototype,對象是沒有的
  2. 只有實例對象有__proto__ [ 爲何函數也有,函數也是對象 ]
  3. 構造函數prototype屬性使實例和原型對象產生關聯,修改了prototype就是修改實例上一級原型對象

instanceof原理


ps:主要在這條原型鏈上的構造函數都是實例對象構造函數

instanceof原理是 實例對象__proto__屬性 和 構造函數 prototype判斷是否是同一個引用,返回true / false

JS code:
function M (){ this.name = name };
var o = new M()
o instanceof M;    // true
o instanceof Object;    // true複製代碼

注意爲何 o instanceof Object 也返回true ?

JS code:
o.__proto__ === M.prototype;    // true
M.prototype.__proto__ === Object.prototype;    // true複製代碼

爲了區分此現象,建議你們平常使用constructor

JS code:
o.__proto__.constructor === M;    // true
o.__proto__.constructor === Object;    // false複製代碼

因此說使用constructor判斷比instanceof更嚴謹

new運算符(原理)

  1. 一個對象被建立(理解成字面量),它繼承構造函數prototype [ 構造函數被執行,執行時候,相同的傳參被傳入 ]
  2. 同時上下文this會被指定爲這個新實例 
  3. 構造函數若是返回了一個新對象,那這個對象會取代new出來結果 [ 若是構造函數沒有返回對象,那new出來的結果爲步驟1建立的對象 ]

JS code:

var newF = function(func){
    var o = Object.create(func.prototype);    // 1.建立對象關聯構造函數原型對象
    var k = func.call(o);    // 2.執行構造函數
    if(typeof k === 'object'){    // 3.判斷構造函數運行結果,是否爲對象類型
        return k;
    }else{
        return o;
    }
}複製代碼

通訊類

同源策略及限制

源:協議 / 域名 / 端口 80(默認),這三個有一項不一樣就跨域了

限制:不是同一個源文檔沒有權利修改另外一個源文檔

  1. cookie、localStorage 和 indexDB沒法讀取
  2. DOM沒法得到
  3. Ajax請求沒法得到(只適合同源策略)

先後端如何通訊

  1. Ajax [ 同源策略 ]
  2. WebSocket [ 不限制同源策略 ]
  3. CORS [ 支持同源和跨域 ]
  4. ..

如何建立Ajax

var xhr = XMLHttpRequest ? new XMLHttpRequest() : new Window.ActiveXObject("Microsoft.XMLHTTP");    // 兼容寫法
xhr.setRequestHeader();    // header頭部信息
xhr.open("GET","/try/ajax/ajax_info.txt", true);    
xhr.send();    // 發送
// xhr.onload判斷xhr.status = 200 || 206 || 304 複製代碼

跨域通訊幾種方式

Ajax是不能發送跨域通訊的,瀏覽器在識別用Ajax發送一個跨域請求時,它會在http頭部加origin來容許跨域通訊,若是不加就被攔截,這也是前端混子所理解CORS原理

JSONP

原理:在出現postMessage和CORS前,用script標籤的異步加載實現;

JS code:
// 發送
<script src="http://www.abc.com/?data=name&callback=jsonp"></script>

// 返回
<script>
    jsonp({
        data:{}
    })   </script>

// 發送
document.getElementByTagName('head')[0].appendChild('script');複製代碼

       實現:

      1.告訴服務端callback名稱,未來是做爲函數名返回的

      2.本地必須有jsonp全局函數,後面才能把數據執行出來

Hash

應用場景:頁面A經過iframe潛入跨域頁面B併發送消息

var B = document.getElementByTagName('iframe');
B.src = B.src + '#' + 'data';

window.onhashchange = function(){    // 寫在B頁面中, 監聽hash有沒有改變
    var data = window.location.hash;
}複製代碼

postMessage

應用場景同上

// A頁面發送到B
B.window.postMessage('data', 'http://B.com');

// B頁面接收A
window.addEventListener('message',function(event){
    console.log(event.origin);    // 拿到A的源
    console.log(event.source);    // Awindow的對象
    console.log(event.data);    // 發送的消息拿到了
}, false);複製代碼

Websocket

var ws = new Websocket('wss: echo.websocket.org');    // 指向服務器地址, ws不加密, wss加密
ws.onopen = function(){ ws.send(); };    // 發送出去
ws.onmessage = function(ev){ 
    ev.data;
    ws.close;    // close斷開
}複製代碼

CORS

新型的Ajax通訊api:fetch 實現CORS通訊

fetch('/some/url',{
    method: 'get'
}).then(function(response){
    
}).catch(function(error){

});複製代碼

安全類

CSRF

CSRF:稱爲跨站請求僞造


ps:引誘點擊後訪問網站A後,瀏覽器自動上傳cookie,A網站對身份從新確認(合法),就執行了動做,完成CSRF攻擊

實現CSRF不可缺乏的條件:

  1. 訪問A網站是登錄的
  2. 接口存在漏洞

CSRF防護措施:

  1. token驗證:剛纔瀏覽器只自動上傳cookie,沒有自動上傳token,token是註冊成功後服務器會往本地存儲token,在訪問接口時攜帶token,若是是上圖場景,只會自動上傳cookie,不會上傳token
  2. Refer驗證(refer是頁面來源):服務器判斷頁面來源是否是站點下頁面
  3. 隱藏令牌(相似token):隱藏才http header頭中,不會放在鏈接上,本質與token無區別,只是使用方式

XSS

XSS稱爲跨域腳本攻擊

攻擊原理:不須要作登錄驗證,核心原理是向頁面注入腳本 [ 評論區是最好最入css攻擊的,在提交區裏面寫script標籤,img標籤等 ],xss是利用合法渠道注入JS

防護措施:宗旨是不讓頁面執行插入的JS腳本

XSS與CSRF對比區別

  1. XSS是向頁面注入JS運行,JS函數體作事情
  2. CSRF是利用自己漏洞幫我執行接口
  3. 方式不同,CSRF依賴用戶登錄網站

渲染機制類

DOCTYPE的做用

你要知道的:DTD(document type definition)告訴瀏覽器我是什麼類型,瀏覽器根據類型來判斷用什麼引擎優化它;如:XML / HTML

這裏是做用:DOCTYPE是聲明文檔類型和DTD規範的,直接告訴瀏覽器當前文檔包含哪些DTD(文檔類型)

<!DOCTYPE HTML>    // html5

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//en" "http://www.w3. org/TR/html4/strict.dtd">    // HTML4嚴格:不包含展現性和棄用元素(front)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//en" "http://www.w3. org/TR/html4/loose.dtd">    // HTML4傳統:包含展現性和棄用元素(front)
複製代碼

瀏覽器渲染過程


重排Reflow

瀏覽器根據各類樣式計算放在它該出現的問題,如:JS修改div中內容(即觸發Reflow)

常見觸發Reflow

  1. 增長/刪除/修改 致使Reflow或Repaint
  2. 移動DOM未知(CSS3動畫)
  3. 修改CSS樣式(寬高/display)
  4. Resize窗口有可能(移動端沒有這個問題)或滾動時
  5. 修改字體

重繪Repaint

屏幕上頁面的呈現內容

常見觸發Repaint

  1. DOM移動
  2. CSS改動
  3. ... ( 太多了,只要判斷如今呈現內容有沒有改變 )

JS運行機制

JS是單線程的(同一事件只能作一件事);這裏混子前端拿道面爛了的面試題來舉例:

JS代碼:

for (var i = 1;i <= 5;i ++) {
    setTimeout(function timer() {
        console.log(i)
    },i * 1000)
}複製代碼

對JS做用域、閉包以及事件循環等概念不瞭解的夥伴會想固然的回答:

第一次循環,隔一秒輸出1;

第二次循環,隔一秒輸出2;

第三次循環,隔一秒輸出3;

第四次循環,隔一秒輸出4;

第五次循環,隔一秒輸出5;

或者還有夥伴預期結果是分別輸出數字1~5,每秒依次,每次一個。

但實際結果你們粘貼到控制檯就都知道,以一秒的頻率輸出五個6。

下面來過一下上面提到的知識點:

做用域:這裏我引用《你不知道的javascript》中的一個比喻,能夠把做用域鏈想象成一座高樓,第一層表明當前執行做用域,樓的頂層表明全局做用域。咱們在查找變量時會先在當前樓層進行查找,若是沒有找到,就會坐電梯前往上一層樓,若是仍是沒有找到就繼續向上找,以此類推。到達頂層後(全局做用域),可能找到了你所需的變量,也可能沒找到,但不管如何查找過程都將中止。

閉包:個人理解是在傳遞函數類型的變量時,該函數會保留定義它的所在函數的做用域。讀起來可能比較繞,或者能夠簡單的這麼理解,A函數中定義了B函數而且它返回了B函數,那麼無論B函數在哪裏被調用或如何被調用,它都會保留A函數的做用域。

事件循環:這個概念深刻起來很複雜,下面新開一個段落詳談:

提及事件循環,不得不提起任務隊列。事件循環只有一個,但任務隊列可能有多個,任務隊列可分爲宏任務(macro-task)微任務(micro-task)
XHR回調、事件回調(鼠標鍵盤事件)、setImmediate、setTimeout、setInterval、indexedDB數據庫操做等I/O以及UI rendering都屬於宏任務(也有文章說UI render不屬於宏任務,目前尚未定論)

process.nextTick、Promise.then、Object.observer(已經被廢棄)、MutationObserver(html5新特性)屬於微任務。注意進入到任務隊列的是具體的執行任務的函數。

好比上述例子setTimeout()中的timer函數。另外不一樣類型的任務會分別進入到他們所屬類型的任務隊列,好比全部setTimeout()的回調都會進入到setTimeout任務隊列,全部then()回調都會進入到then隊列。當前的總體代碼咱們能夠認爲是宏任務。事件循環從當前總體代碼開始第一次事件循環,而後再執行隊列中全部的微任務,當微任務執行完畢以後,事件循環再找到其中一個宏任務隊列並執行其中的全部任務,而後再找到一個微任務隊列並執行裏面的全部任務,就這樣一直循環下去。這就是我所理解的事件循環。

繼續看個栗子:

JS代碼:

console.log('global')

setTimeout(function () {
   console.log('timeout1')
   new Promise(function (resolve) {
     console.log('timeout1_promise')
       resolve()
   }).then(function () {
     console.log('timeout1_then')
  })
},2000)

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

new Promise(function (resolve) {
  console.log('promise1')
  resolve()
 }).then(function () {
  console.log('then1')
})

setTimeout(function () {
  console.log('timeout2')
  new Promise(function (resolve) {
    console.log('timeout2_promise')
    resolve()
  }).then(function () {
    console.log('timeout2_then')
  })
}, 1000)

new Promise(function (resolve) {
  console.log('promise2')
  resolve()
}).then(function () {
  console.log('then2')
})
複製代碼

來一步一步分析以上代碼:

  1. 首先執行總體代碼,「global」會被第一個打印出來。這是第一個輸出
  2. 執行到第一個setTimeout時,發現它是宏任務,此時會新建一個setTimeout類型的宏任務隊列並派發當前這個setTimeout的回調函數到剛建好的這個宏任務隊列中去,而且輪到它執行時要延遲2秒後再執行。 
  3. 代碼繼續執行走到for循環,發現是循環5次setTimeout(),那就把這5個setTimeout中的回調函數依次派發到上面新建的setTimeout類型的宏任務隊列中去,注意,這5個setTimeout的延遲分別是1到5秒。此時這個setTimeout類型的宏任務隊列中應該有6個任務了。再執行for循環裏的console.log(i),很簡單,直接輸出1,2,3,4,5,這是第二個輸出
  4. 再執行到new Promise,Promise構造函數中的第一個參數在new的時候會直接執行,所以不會進入任何隊列,因此第三個輸出是"promise1",上面有說到Promise.then是微任務,那麼這裏會生成一個Promise.then類型的微任務隊列,這裏的then回調會被push進這個隊列中。
  5. 再繼續走,執行到第二個setTimeout,發現是宏任務,派發它的回調到上面setTimeout類型的宏任務隊列中去。
  6. 再走到最後一個new Promise,很明顯,這裏會有第四個輸出:"promise2",而後它的then中的回調也會被派發到上面的Promise.then類型的微任務隊列中去。
  7. 第一輪事件循環的宏任務執行完成(總體代碼能夠看作宏任務)。此時微任務隊列中只有一個Promise.then類型微任務隊列,它裏面有兩個任務。宏任務隊列中也只有一個setTimeout類型的宏任務隊列。 
  8. 下面執行第一輪事件循環的微任務,很明顯,會分別打印出"then1",和"then2"。分別是第五和第六個輸出。此時第一輪事件循環完成。
  9. 開始第二輪事件循環:執行setTimeout類型隊列(宏任務隊列)中的全部任務。發現都有延時,但延時最短的是for循環中第一次循環push進來的那個setTimeout和上面第5個步驟中的第二個setTimeout,它們都只延時1s。它們會被同時執行,但前者先被push進來,因此先執行它!它的做用就是打印變量i,在當前做用域找變量i,木有!去它上層做用域(這裏是全局做用域)找,找到了,但此時的i早已經是6了。(爲啥不是5,那你得去補補for循環的執行流程了)因此這裏第七個輸出是延時1s後打印出6。
  10. 緊接着執行第二個setTimeout,它會前後打印出"timeout2"和"timeout2_promise",這分別是第八和第九個輸出。但這裏發現了then,又把它push到上面已經被執行完的then隊列中去。
  11. 這裏要注意,由於出現了微任務then隊列,因此這裏會執行該隊列中的全部任務(此時只有一個任務),即打印出"timeout2_then"。這是第十個輸出
  12. 繼續回過頭來執行宏任務隊列,此時是執行延時爲2s的第一個setTimeout和for循環中第二次循環的那個setTimeout,跟上面同樣,前者是第一個被push進來的,因此它先執行。這裏會延時1秒(緣由下面會解釋)分別輸出「timeout1」和「timeout1_promise」,但發現了裏面也有一個then,因而push到then微任務隊列並當即執行,輸出了"timeout1_then"。緊接着執行for中第二次循環的setTimeout,輸出6。注意這三個幾乎是同時被打印出來的。他們分別是第十一到十三個輸出
  13. 再就很簡單了,把省下的for循環中後面三次循環被push進來的setTimeout依次執行,因而每隔1s輸出一個6,連續輸出3次。
  14. 第二輪事件循環結束,所有代碼執行完畢。
這裏解釋下爲何上面第12步不是延遲2秒再輸出「timeout1」和「timeout1_promise」,這時須要理解setTimeout()延時參數的意思,這個延遲時間始終是相對主程序執行完畢的那個時間算的 ,而且多個setTimeout執行的前後順序也是由這個延遲時間決定的。   

再回過頭來看上面那個問題,理解了事件循環的機制,問題就很簡單了。for循環時setTimeout()不是當即執行的,它們的回調被push到了宏任務隊列當中,而在執行任務隊列裏的回調函數時,變量i早已變成了6。那如何獲得想要的結果呢?很簡單,原理就是須要給循環中的setTimeout()建立一個閉包做用域,讓它執行的時候找到的變量i是正確的。

混子前端給出以下5種解決方案:

  1. 引入IIFE

    JS代碼
    
    for(var i = 0;i < 5; i++){
        (function(i){
            setTimeout(function timer(){
                console.log(i);
            }, i * 1000)
        })(i);
    }
    複製代碼

  2. 利用ES 6引入的let關鍵字

    JS代碼
    
    for(let i = 0;i<5;i++) {
        setTimeout(function timer(){
            console.log(i);
        }, i * 1000);
    }
    複製代碼

  3. 利用ES 5引入的bind函數

    JS代碼
    
    for (var i=1; i<=5; i++) {
        setTimeout( function timer(i) {
            console.log(i);
        }.bind(null,i), i*1000 );
    }
    複製代碼

  4. 利用setTimeout第三個參數

    JS代碼
    
    for (var i=1; i<=5; i++) {
        setTimeout(function timer(i) {
            console.log(i);    
        }, i*1000,i );
    }
    複製代碼

  5. 把setTimeout用一個方法單獨出來造成閉包

    JS代碼
    
    var loop = function (i) {
        setTimeout(function timer() {
            console.log(i);  
        }, i*1000);
    };
    
    for (var i = 1;i <= 5; i++) {
        loop(i);
    }複製代碼

頁面性能

  • 資源壓縮合並,減小HTTP請求(把文件變小)
  • 非核心代碼異步加載
    異步加載方式:
    一、動態腳本加載:document.creatElement建立標籤/節點標籤加到head/body
    二、defer:script上添加
    三、async:script上添加

    對比defer/async區別:
    defer是在HTML解析完纔會執行,若是是多個按加載順序執行
    async是在加載完以後當即執行,多個async的話,執行順序和加載順序無關
  • 利用瀏覽器緩存
    強緩存 / 協商緩存
  • 利用CDN[內容分發]讓網絡快速到服務端把文件下載下來
  • 預解析DNS
    高級瀏覽器在頁面裏a標籤默認打開了DNS預解析,不加這句話,a標籤也作了DNS預解析,但頁面若是是HTTPS協議,瀏覽器是關閉預解析的,經過meta標籤強制打開預解析:

    <meta http-equiv="x-dns-prefetch-control" content="on">
    
    <link rel="dns-prefetch" href="//host_name_to_prefetch.com">複製代碼

錯誤監控

首先咱們要知道錯誤分類

  1. 即時運行錯誤:代碼錯誤
  2. 資源加載錯誤:img / js / css 加載失敗

捕獲方式:

(一)若是是即時性錯誤

  1. try...catch
  2. window.onerror 
  3. addEventListener

(二)若是是資源加載性錯誤

  1. Object.onerror  對 img / js 添加 onerror事件
  2. performance.getEntries() 獲取全部已加載的資源,返回一個數組
  3. Error事件捕獲window上[冒泡不會阻止捕獲]

    JS代碼
    
    window.addEventListener('error', function(){
        // ****
    }, true);複製代碼

ps:若是JS錯誤是跨域的,能夠在script標籤添加crossorigin屬性,同時在響應JS資源時,設置JS資源頭Access-Control-Allow-Origin就能夠拿到錯誤信息了

這裏上報錯誤的原理:利用ajax通信上報 / 也能夠利用image對象上報

JS代碼

(new Image()).src = "http://baidu.com/tesjk?r=tksjk";複製代碼

利用這種方式比ajax簡單,也能實現資源上報

算法類

因爲本人也是混子前端,不一一列舉,但做爲一名合格的前端,算法是必不可少的。

ps:請隨意吐槽本混子,會虛心接受


好了,混子前端就總結到這裏,還請留意混子前端不定時的更新....

相關文章
相關標籤/搜索