面試題錦(大廠面試前夕的掙扎)

煙雨空靈發表於2019-03-31

歡迎評論區糾錯完善

css部分

實現三欄佈局

(兩側定寬,中間自適應)

  1. 採用了 absolute,導致父元素脫離了文件流,那所有的子元素也需要脫離文件流。如果頁面複雜,那開發的難度可想而知
  2. 利用浮動 當中間內容高於兩側時,兩側高度不會隨中間內容變高而變高
  3. 彈性盒子佈局
  4. 利用負邊距和浮動,實現起來比較複雜
  5. 利用網格佈局
.container {
    display: grid;
    grid-template-columns: 100px auto 200px;
}
複製程式碼

BFC(塊級格式化上下文)

BFC 的原理 其實也就是 BFC 的渲染規則(能說出以下四點就夠了)。包括:

  1. BFC 內部的子元素,在垂直方向,邊距會發生重疊。
  2. BFC在頁面中是獨立的容器,外面的元素不會影響裡面的元素,反之亦然。(稍後看舉例1)
  3. BFC區域不與旁邊的float box區域重疊。(可以用來清除浮動帶來的影響)。(稍後看舉例2)
  4. 計算BFC的高度時,浮動的子元素也參與計算。 如何生成BFC
  • 方法1:overflow: 不為vidible,可以讓屬性是 hidden、auto。【最常用】
  • 方法2:浮動中:float的屬性值不為none。意思是,只要設定了浮動,當前元素就建立了BFC。
  • 方法3:定位中:只要posiiton的值不是 static或者是relative即可,可以是absolute或fixed,也就生成了一個BFC。
  • 方法4:display為inline-block, table-cell, table-caption, flex, inline-flex

flex(略)

js部分

陣列去重

[...new Set(arr]
複製程式碼
var arr = [1,2,1,2,3,5,4,5,3,4,4,4,4],
    init=[]
var result = arr.sort().reduce((init, current)=>{
    console.log(init,current)
    if(init.length===0 || init[init.length-1]!==current){
        init.push(current);
    }
    return init;
}, []);
console.log(result);//1,2,3,4,5
複製程式碼

防抖節流

var deBounce=function(fn,wait=300){
    let timer
    return function(){
      if(timer){
          clearTimeOut(timer)
      }
      timer=setTimeOut(()=>{
          fn.apply(this,arguments)
      },wait)
    } 
}
var throttle=function(fn,wait=300){
    let prev=+new Date();
    return function(){
        const args=argument,
            now=+new Date();
            if(now>last+wait){
                last=now;
                fn.apply(this,args)
            } 
    }
}
複製程式碼

實現Promise思路

 function Promise(fn) {
        ...
        this._state = 0 // 狀態標記
        doResolve(fn, this)
    }
    
    function doResolve(fn, self) {
        var done = false // 保證只執行一個監聽
        try {
            fn(function(value) {
                if (done) return
                done = true
                resolve(self, value)
            },
            function(reason) {
                if (done) return;
                done = true
                reject(self, value)
            })
        } catch(err) {
            if (done) return
            done = true
            reject(self, err)
        }
    }
    
    function resolve(self, newValue) {
        try {
            self._state = 1;
            ...
        }
        catch(err) {
            reject(self, err)
        }
    }
    
    function reject(self, newValue) {
        self._state = 2;
        ...
        if (!self._handled) {
            Promise._unhandledRejectionFn(self._value);
        }
    }
複製程式碼

正則實現千位分隔符

function commafy(num) {
        return num && num
            .toString()
            .replace(/(\d)(?=(\d{3})+\.)/g, function($0, $1) {
                return $1 + ",";
            });
    }
 console.log(commafy(1312567.903000))
複製程式碼

js事件迴圈

javascript是單執行緒語言,任務設計成了兩類,同步任務和非同步任務 同步和非同步任務分別進入不同的執行“場所”,同步進入主執行緒,非同步進入Event Table並註冊函式。當指定的事情完成時,Event Table會將這個函式移入Event Queue。主執行緒內的任務執行完畢為空,回去了Event Queue讀取對應的函式,進入主執行緒。 上述過程會不斷重複,也就是常說的Event Loop(事件迴圈)。 但是,JS非同步還有一個機制,就是遇到巨集任務,先執行巨集任務,將巨集任務放入event queue,然後再執行微任務,將微任務放入eventqueue,但是,這兩個queue不是一個queue。當你往外拿的時候先從微任務裡拿這個回撥函式,然後再從巨集任務的queue拿巨集任務的回撥函式 巨集任務一般包括:整體程式碼script,setTimeout,setInterval。 微任務:Promise,process.nextTick

前端路由的兩種實現原理

  1. Hash模式

window物件提供了onhashchange事件來監聽hash值的改變,一旦url中的hash值發生改變,便會觸發該事件。 2. History 模式

  • popstate監聽歷史棧資訊變化,變化時重新渲染
  • 使用pushState方法實現新增功能
  • 使用replaceState實現替換功能

封裝ajax

/* 封裝ajax函式
 * @param {string}opt.type http連線的方式,包括POST和GET兩種方式
 * @param {string}opt.url 傳送請求的url
 * @param {boolean}opt.async 是否為非同步請求,true為非同步的,false為同步的
 * @param {object}opt.data 傳送的引數,格式為物件型別
 * @param {function}opt.success ajax傳送並接收成功呼叫的回撥函式
 */
function myAjax(opt){
     opt = opt || {};
     opt.method = opt.method.toUpperCase() || 'POST';
     opt.url = opt.url || '';
     opt.async = opt.async || true;
     opt.data = opt.data || null;
     opt.success = opt.success || function () {}
     let xmlHttp = null;
     if (XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest();
     }else{
         xmlHttp =new ActiveXObject('Microsoft.XMLHTTP')
     }
     let params;
    for (var key in opt.data){
        params.push(key + '=' + opt.data[key]);
    }
    let postData = params.join('&');
    if (opt.method.toUpperCase() === 'POST') {
        xmlHttp.open(opt.method, opt.url, opt.async);
        xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
        xmlHttp.send(postData);
    }else if (opt.method.toUpperCase() === 'GET') {
        xmlHttp.open(opt.method, opt.url + '?' + postData, opt.async);
        xmlHttp.send(null);
    } 
     xmlHttp.onreadystatechange= function () {
            if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
                opt.success(xmlHttp.responseText);//如果是json資料可以在這使用opt.success(JSON.parse( xmlHttp.responseText))
            }
     };
}
複製程式碼

url拿引數

var url = "http://www.taobao.com/index.php?key0=0&key1=1&key2=2";
function parseQueryString(url){
    var str = url.split("?")[1],    //通過?得到一個陣列,取?後面的引數
        items = str.split("&");    //分割成陣列
    var arr,name,value;

    for(var i=0; i<items.length; i++){
        arr = items[i].split("=");    //["key0", "0"]
        name = arr[0];
        value = arr[1];
        this[name] = value;
    }
}

var obj = new parseQueryString(url);
alert(obj.key2)
複製程式碼

HTTP部分

http協議

HTTP協議(超文字傳輸協議) 主要特點

  1. 簡單快速:客戶向伺服器請求服務時,只需傳送請求方法和路徑。請求方法常用的有GET、HEAD、POST。每種方法規定了客戶與伺服器聯絡的型別不同。由於HTTP協議簡單,使得HTTP伺服器的程式規模小,因而通訊速度很快。
  2. 靈活:HTTP允許傳輸任意型別的資料物件。正在傳輸的型別由Content-Type加以標記。
  3. 無連線:無連線的含義是限制每次連線只處理一個請求。伺服器處理完客戶的請求,並收到客戶的應答後,即斷開連線。採用這種方式可以節省傳輸時間。
  4. 無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺少狀態意味著如果後續處理需要前面的資訊,則它必須重傳,這樣可能導致每次連線傳送的資料量增大。另一方面,在伺服器不需要先前資訊時它的應答就較快。
  5. 支援B/S及C/S模式。

HTTP之請求訊息Request

  1. 請求行(request line)、請求頭部(header)、空行和請求資料四個部分組成。
  2. 請求行,用來說明請求型別,要訪問的資源以及所使用的HTTP版本.
  3. 請求頭部,緊接著請求行(即第一行)之後的部分,用來說明伺服器要使用的附加資訊
  4. 空行,請求頭部後面的空行是必須的
  5. 請求資料也叫主體,可以新增任意的其他資料。

HTTP之響應訊息Response

HTTP響應也由四個部分組成,分別是:狀態行、訊息報頭、空行和響應正文。

  1. 狀態行,由HTTP協議版本號, 狀態碼, 狀態訊息 三部分組成。
  2. 訊息報頭,用來說明客戶端要使用的一些附加資訊
  3. 第三部分:空行,訊息報頭後面的空行是必須的
  4. 第四部分:響應正文,伺服器返回給客戶端的文字資訊。

在瀏覽器位址列鍵入URL,按下回車之後會經歷以下流程:

  1. 瀏覽器向 DNS 伺服器請求解析該 URL 中的域名所對應的 IP 地址;
  2. 建立TCP連線;
  3. 瀏覽器發出讀取檔案(URL 中域名後面部分對應的檔案)的HTTP 請求,該請求報文作為 TCP 三次握手的第三個報文的資料傳送給伺服器;
  4. 伺服器對瀏覽器請求作出響應,並把對應的 html 文字傳送給瀏覽器;
  5. 釋放 TCP連線(四次揮手);
  6. 瀏覽器將該 html 文字並顯示內容;

三次握手

SYN (同步序列編號)ACK(確認字元)

  1. 第一次握手:Client將標誌位SYN置為1,隨機產生一個值seq=J,並將該資料包傳送給Server,Client進入SYN_SENT狀態,等待Server確認。
  2. 第二次握手:Server收到資料包後由標誌位SYN=1知道Client請求建立連線,Server將標誌位SYN和ACK都置為1,ack=J+1,隨機產生一個值seq=K,並將該資料包傳送給Client以確認連線請求,Server進入SYN_RCVD狀態。
  3. 第三次握手:Client收到確認後,檢查ack是否為J+1,ACK是否為1,如果正確則將標誌位ACK置為1,ack=K+1,並將該資料包傳送給Server,Server檢查ack是否為K+1,ACK是否為1,如果正確則連線建立成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client與Server之間可以開始傳輸資料了。

四次揮手

  1. 第一次揮手:Client傳送一個FIN,用來關閉Client到Server的資料傳送,Client進入FIN_WAIT_1狀態。
  2. 第二次揮手:Server收到FIN後,傳送一個ACK給Client,確認序號為收到序號+1(與SYN相同,一個FIN佔用一個序號),Server進入CLOSE_WAIT狀態。
  3. 第三次揮手:Server傳送一個FIN,用來關閉Server到Client的資料傳送,Server進入LAST_ACK狀態。
  4. 第四次揮手:Client收到FIN後,Client進入TIME_WAIT狀態,接著傳送一個ACK給Server,確認序號為收到序號+1,Server進入CLOSED狀態,完成四次揮手。

為什麼建立連線是三次握手,而關閉連線卻是四次揮手呢?

這是因為服務端在LISTEN狀態下,收到建立連線請求的SYN報文後,把ACK和SYN放在一個報文裡傳送給客戶端。而關閉連線時,當收到對方的FIN報文時,僅僅表示對方不再傳送資料了但是還能接收資料,己方也未必全部資料都傳送給對方了,所以己方可以立即close,也可以傳送一些資料給對方後,再傳送FIN報文給對方來表示同意現在關閉連線,因此,己方ACK和FIN一般都會分開傳送。

網頁生成的過程,大致可以分為五步:

  1. html程式碼轉化為dom
  2. css程式碼轉化為cssom
  3. 結合dom和cssom,生成一顆渲染樹
  4. 生成佈局layout,即將所有的渲染樹的節點進行平面合成
  5. 將佈局繪製paint在螢幕上(可以擴充講一下減少瀏覽器渲染的重排和重繪)

瀏覽器快取

當瀏覽器再次訪問一個已經訪問過的資源時,它會這樣做:

  1. 看看是否命中強快取,如果命中,就直接使用快取了。
  2. 如果沒有命中強快取,就發請求到伺服器檢查是否命中協商快取。
  3. 如果命中協商快取,伺服器會返回 304 告訴瀏覽器使用本地快取。
  4. 否則,返回最新的資源。

vue

vue Virtual DOM

其實 VNode 是對真實 DOM 的一種抽象描述,它的核心定義無非就幾個關鍵屬性,標籤名、資料、子節點、鍵值等,其它屬性都是都是用來擴充套件 VNode 的靈活性以及實現一些特殊 feature 的。由於 VNode 只是用來對映到真實 DOM 的渲染,不需要包含操作 DOM 的方法,因此它是非常輕量和簡單的。 Virtual DOM 除了它的資料結構的定義,對映到真實的 DOM 實際上要經歷 VNode 的 create、diff、patch 等過程。那麼在 Vue.js 中,VNode 的 create 是通過之前提到的 createElement 方法建立的,我們接下來分析這部分的實現。

vue的響應式原理

  • Object.defineProperty(obj, prop, descriptor)
  • obj 是要在其上定義屬性的物件;prop 是要定義或修改的屬性的名稱;descriptor 是將被定義或修改的屬性描述符。 比較核心的是 descriptor,它有很多可選鍵值,具體的可以去參閱它的文件。這裡我們最關心的是 get 和 set,get 是一個給屬性提供的 getter 方法,當我們訪問了該屬性的時候會觸發 getter 方法;set 是一個給屬性提供的 setter 方法,當我們對該屬性做修改的時候會觸發 setter 方法。一旦物件擁有了 getter 和 setter,我們可以簡單地把這個物件稱為響應式物件
  • observe
  • observe 方法的作用就是給非 VNode 的物件型別資料新增一個 Observer,如果已經新增過則直接返回,否則在滿足一定條件下去例項化一個 Observer 物件例項。
  • observe 的功能就是用來監測資料的變化.
  • Observer 是一個類,它的作用是給物件的屬性新增 getter 和 setter,用於依賴收集和派發更新:
  • 依賴收集和派發更新
  • 收集依賴的目的是為了當這些響應式資料發生變化,觸發它們的 setter 的時候,能知道應該通知哪些訂閱者去做相應的邏輯處理,我們把這個過程叫派發更新,其實 Watcher 和 Dep 就是一個非常經典的觀察者設計模式的實現
  • 派發更新就是資料發生變化的時候,觸發 setter 邏輯,把在依賴過程中訂閱的的所有觀察者,也就是 watcher,都觸發它們的 update 過程,這個過程又利用了佇列做了進一步優化,在 nextTick 後執行所有 watcher 的 run,最後執行它們的回撥函式

vue編譯的過程主要分以下幾步

// 解析模板字串生成 AST
const ast = parse(template.trim(), options)
//優化語法樹
optimize(ast, options)
//生成程式碼
const code = generate(ast, options)
複製程式碼

對vuex的理解,單向資料流

前端安全

XSS和CSRF

  • XSS:跨站指令碼攻擊,是一種網站應用程式的安全漏洞攻擊,是程式碼注入的一種。常見方式是將惡意程式碼注入合法程式碼裡隱藏起來,再誘發惡意程式碼,從而進行各種各樣的非法活動。

預防:

  • 使用XSS Filter

    1. 輸入過濾,對使用者提交的資料進行有效性驗證,僅接受指定長度範圍內並符合我們期望格式的的內容提交,阻止或者忽略除此外的其他任何資料。
    2. 輸出轉義,當需要將一個字串輸出到Web網頁時,同時又不確定這個字串中是否包括XSS特殊字元,為了確保輸出內容的完整性和正確性,輸出HTML屬性時可以使用HTML轉義編碼(HTMLEncode)進行處理,輸出到<script>中,可以進行JS編碼。
  • 使用 HttpOnly Cookie 將重要的cookie標記為httponly,這樣的話當瀏覽器向Web伺服器發起請求的時就會帶上cookie欄位,但是在js指令碼中卻不能訪問這個cookie,這樣就避免了XSS攻擊利用JavaScript的document.cookie獲取cookie。

  • CSRF:跨站請求偽造,也稱 XSRF,是一種挾制使用者在當前已登入的Web應用程式上執行非本意的操作的攻擊方法。與 XSS 相比,XSS利用的是使用者對指定網站的信任,CSRF利用的是網站對使用者網頁瀏覽器的信任。

  1. 預防:使用者操作限制——驗證碼機制
  • 方法:新增驗證碼來識別是不是使用者主動去發起這個請求,由於一定強度的驗證碼機器無法識別,因此危險網站不能偽造一個完整的請求。
  • 優點:簡單粗暴,低成本,可靠,能防範99.99%的攻擊者。
  • 缺點:對使用者不友好。
  1. 請求來源限制——驗證 HTTP Referer 欄位
  • 方法:在HTTP請求頭中有一個欄位叫Referer,它記錄了請求的來源地址。 伺服器需要做的是驗證這個來源地址是否合法,如果是來自一些不受信任的網站,則拒絕響應。
  • 優點:零成本,簡單易實現。
  • 缺點:由於這個方法嚴重依賴瀏覽器自身,因此安全性全看瀏覽器。
  • 額外驗證機制——token的使用
  1. 方法:使用token來代替驗證碼驗證。由於黑客並不能拿到和看到cookie裡的內容,所以無法偽造一個完整的請求。基本思路如下:
  2. 伺服器隨機產生token(比如把cookie hash化生成),存在session中,放在cookie中或者以ajax的形式交給前端。
    • 前端發請求的時候,解析cookie中的token,放到請求url裡或者請求頭中。
    • 伺服器驗證token,由於黑客無法得到或者偽造token,所以能防範csrf

正在整理中的問題

寫過webpack loader嗎

vue怎麼監聽陣列

call, apply, bind區別? 怎麼實現call方法(不能使用apply,bind方法);

手寫實現json

實現一個釋出訂閱模式;

手寫快排,時間複雜度,優化

vue裡面的虛擬dom

HTTPS的工作原理

那通過物件.方法呼叫箭頭函式,裡面的this指向什麼

事件委託,target 和 currentTarget 區別;

js繼承,建構函式,原型鏈,建構函式、原型鏈組合式繼承,寄生式組合繼承,Object.create polyfill;

webpack用過嗎?搖樹是什麼,什麼場景下用過?

你遇到過最難的問題是什麼

react 虛擬 dom實現,diff演算法;

手寫快排,怎麼優化;說下sort實現原理;

解釋一個你最近遇到的技術挑戰

相關文章