辣些經典的web前端面試題

前記

啊,快樂的時光總是很短暫。
2017年的第一場面試,比想的要更早些 :-D
好呢!總有辣么幾個問題,面試總要問!
嘛!想起幾個總結幾個,答案肯定不標準,隨時改,基本就是這樣。

談一談對性能優化的認識?

前端優化的途徑有很多,大致可以分為兩類,第一類是頁面級別的優化,例如 HTTP請求數、腳本的無阻塞加載、內聯腳本的位置優化等 ;第二類則是代碼級別的優化,例如 Javascript中的DOM 操作優化、CSS選擇符優化、圖片優化以及 HTML結構優化等等。

  • 頁面級優化
    1. 減少http請求次數。
      大部分響應時間花在下載網頁內容(images,stylesheets,javascript,script,flash等)。減少次數是縮短響應時間的關鍵!可以通過簡化頁面設計來減少請求次數,但頁面內容比較多的時候可以采用以下技巧:
    2. 從設計實現層面簡化頁面
      保持頁面簡潔、減少資源的使用是最直接的。
    3. 合理設置 HTTP 緩存
      原則很簡單,能緩存越多越好,能緩存越久越好。
      HTTP 緩存的四種風味與緩存策略
    4. 資源合并與壓縮
      如果可以的話,盡可能的將外部的腳本、樣式進行合并,多個合為一個。另外, CSS、 Javascript、Image 都可以用相應的工具進行壓縮,壓縮后往往能省下不少空間。
    5. css sprites
      合并 CSS圖片,減少請求數的又一個好辦法。
    6. inline images(不是很了解這個)
    7. Lazy Load Images
    8. 將外部腳本置底
    9. 異步執行 inline 腳本
    10. Lazy Load Javascript
    11. 將 css 放在 head 中
    12. 異步請求 callback
    13. 減少不必要的 HTTP 跳轉
    14. 避免重復的資源請求
  • 代碼級優化
    1. Javascript
        1. DOM
        DOM操作應該是腳本中最耗性能的一類操作,例如增加、修改、刪除 DOM元素或者對 DOM集合進行操作。如果腳本中包含了大量的 DOM 操作則需要注意以下幾點:
         1. HTML Collection(HTML收集器,返回的是一個數組內容信息)
         在腳本中 document.images、document.forms 、getElementsByTagName()返回的都是 HTMLCollection類型的集合,在平時使用的時候大多將它作為數組來使用,因為它有 length屬性,也可以使用索引訪問每一個元素。不過在訪問性能上則比數組要差很多,原因是這個集合并不是一個靜態的結果,它表示的僅僅是一個特定的查詢,每次訪問該集合時都會重新執行這個查詢從而更新查詢結果。所謂的 “訪問集合” 包括讀取集合的 length屬性、訪問集合中的元素。
         因此,當你需要遍歷 HTML Collection的時候,盡量將它轉為數組后再訪問,以提高性能。即使不轉換為數組,也請盡可能少的訪問它,例如在遍歷的時候可以將 length屬性、成員保存到局部變量后再使用局部變量。
         2. Reflow & Repaint(不是很了解)
         除了上面一點之外, DOM操作還需要考慮瀏覽器的 Reflow 和 Repaint ,因為這些都是需要消耗資源的。
        2. 慎用 with(不了解)
      with(obj){ p = 1};
      代碼塊的行為實際上是修改了代碼塊中的執行環境 ,將obj放在了其作用域鏈的最前端,在 with 代碼塊中訪問非局部變量是都是先從 obj 上開始查找,如果沒有再依次按作用域鏈向上查找,因此使用 with 相當于增加了作用域鏈長度。而每次查找作用域鏈都是要消耗時間的,過長的作用域鏈會導致查找性能下降。
         因此,除非你能肯定在 with 代碼中只訪問 obj 中的屬性,否則慎用 with,替代的可以使用局部變量緩存需要訪問的屬性。
        3. 避免使用 eval 和 Function
        每次 eval 或 Function 構造函數作用于字符串表示的源代碼時,腳本引擎都需要將源代碼轉換成可執行代碼。這是很消耗資源的操作 —— 通常比簡單的函數調用慢 100倍以上。
        eval 函數效率特別低,由于事先無法知曉傳給 eval 的字符串中的內容,eval 在其上下文中解釋要處理的代碼,也就是說編譯器無法優化上下文,因此只能有瀏覽器在運行時解釋代碼。這對性能影響很大。
        Function 構造函數比 eval 略好,因為使用此代碼不會影響周圍代碼;但其速度仍很慢。
        此外,使用 eval和 Function也不利于Javascript 壓縮工具執行壓縮。
        4. 減少作用域鏈查找
        這一點在循環中是尤其需要注意的問題。如果在循環中需要訪問非本作用域下的變量時請在遍歷之前用局部變量緩存該變量,并在遍歷結束后再重寫那個變量,這一點對全局變量尤其重要,因為全局變量處于作用域鏈的最頂端,訪問時的查找次數是最多的。
        此外,要減少作用域鏈查找還應該減少閉包的使用。
        5. 數據訪問
        Javascript中的數據訪問包括直接量 (字符串、正則表達式 )、變量、對象屬性以及數組,其中對直接量和局部變量的訪問是最快的,對對象屬性以及數組的訪問需要更大的開銷。當出現以下情況時,建議將數據放入局部變量:
         1. 對任何對象屬性的訪問超過 1次
         2. 對任何數組成員的訪問次數超過 1次
        另外,還應當盡可能的減少對對象以及數組深度查找。
        6. 字符串拼接
        在 Javascript中使用"+" 號來拼接字符串效率是比較低的,因為每次運行都會開辟新的內存并生成新的字符串變量,然后將拼接結果賦值給新變量。與之相比更為高效的做法是使用數組的 join方法,即將需要拼接的字符串放在數組中最后調用其 join 方法得到結果。不過由于使用數組也有一定的開銷,因此當需要拼接的字符串較多的時候可以考慮用此方法。
       2. CSS選擇符
       3. HTML
       4. Image壓縮

摘自:Web前端應該從哪些方面來優化網站?

另外:

  1. 減少http請求次數
    80%的響應時間花在下載網頁內容(images, stylesheets, javascripts, scripts, flash等)。減少請求次數是縮短響應時間的關鍵!可以通過簡化頁面設計來減少請求次數。
  2. 減少DNS查詢次數
    DNS查詢也消耗響應時間,如果我們的網頁內容來自各個不同的domain (比如嵌入了開放廣告,引用了外部圖片或腳本),那么客戶端首次解析這些domain也需要消耗一定的時間。DNS查詢結果緩存在本地系統和瀏覽器中一段時間,所以DNS查詢一般是對首次訪問響應速度有所影響。下面是我清空本地dns后訪問博客園主頁dns的查詢請求。
  3. 緩存Ajax
    Ajax可以幫助我們異步的下載網頁內容,但是有些網頁內容即使是異步的,用戶還是在等待它的返回結果,例如ajax的返回是用戶聯系人的下拉列表。所以我們還是要注意盡量應用以下規則提高ajax的響應速度。
  4. 延遲加載
    這里討論延遲加載需要我們知道我們的網頁最初加載需要的最小內容集是什么。剩下的內容就可以推到延遲加載的集合中。
    Javascript是典型的可以延遲加載內容。一個比較激進的做法是開發網頁時先確保網頁在沒有Javascript的時候也可以基本工作,然后通過延遲加載腳本來完成一些高級的功能。

另:
唯快不破:Web 應用的 13 個優化步驟

從輸入URL到瀏覽器顯示頁面發生了什么?

這個過程可以大致分為兩個部分:網絡通信和頁面渲染。

  1. 網絡通信
  • 在瀏覽器中輸入 url
    用戶輸入 url ,例如 http://www.baidu.com。其中 http 為協議, www.baidu.com 為網絡地址。一般網絡地址可以為域名或IP地址。
  • 應用層 DNS 解析域名。
    客戶端先檢查本地是否有對應的 IP 地址,若找到則返回響應的 IP 地址。若沒找到則請求上級 DNS 服務器,直至找到或到根節點。
  • 應用層客戶端發送 HTTP 請求
    HTTP 請求包括請求報頭和請求主體兩個部分.
  • 傳輸層 TCP 傳輸報文
    TCP協議通過“三次握手”等方法保證傳輸的安全可靠。
  • 網絡層IP協議查詢 MAC 地址
  • 數據到達數據鏈路層
  • 服務器接收數據
  • 服務器響應請求
    服務接收到客戶端發送的HTTP請求后,查找客戶端請求的資源,并返回響應報文,響應報文中包括一個重要的信息——狀態碼。其中比較常見的是200 OK表示請求成功。301表示永久重定向,即請求的資源已經永久轉移到新的位置。在返回301狀態碼的同時,響應報文也會附帶重定向的url,客戶端接收到后將http請求的url做相應的改變再重新發送。404 not found 表示客戶端請求的資源找不到。
    狀態碼由三位數字組成:
    • 1xx:指示信息–表示請求已接收,繼續處理。
    • 2xx:成功–表示請求已被成功接收、理解、接受。
    • 3xx:重定向–要完成請求必須進行更進一步的操作。
    • 4xx:客戶端錯誤–請求有語法錯誤或請求無法實現。
    • 5xx:服務器端錯誤–服務器未能實現合法的請求。
  • 服務器返回相應文件
  1. 頁面渲染
    瀏覽器是一個邊解析邊渲染的過程。首先瀏覽器解析HTML文件構建DOM樹,然后解析CSS文件構建渲染樹,等到渲染樹構建完成后,瀏覽器開始布局渲染樹并將其繪制到屏幕上。

摘自:從輸入URL到瀏覽器顯示頁面發生了什么
另:
一個頁面從輸入URL到頁面加載顯示完成,這個過程都發生什么?
從輸入URL到頁面加載發生了什么?

如何垂直居中一個浮動元素?

/* 已知寬高 */
#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;
  }

那么問題來了,如何垂直居中一個<img>?(用更簡便的方法。)

/*<img>的容器設置如下*/
#container 
{
    display:table-cell;
    text-align:center;
    vertical-align:middle;
}

jquery 常用的選擇器

  1. 基本選擇器
  2. ID選擇器 $(“#id”) 獲取指定ID的元素
  3. 類選擇器 $(“.class”) 獲取同一類class的元素
  4. 標簽選擇器 $(“div”) 獲取同一類標簽的所有元素
  5. 并集選擇器 $(“div,p,li”) 使用逗號分隔,只要符合條件之一就可。獲取所有的div、p、li元素
  6. 交集選擇器(標簽指定式選擇器) $(“div.redClass”) 注意選擇器1和選擇器2之間沒有空格,class為redClass的div元素,注意區分后代選擇器。
  7. 層級選擇器
  8. 子代選擇器 $(“ul>li”) 使用>號,獲取兒子層級的元素,注意,并不會獲取孫子層級的元素
  9. 后代選擇器 $(“ul li”) 使用空格,代表后代選擇器,獲取ul下的所有li元素,包括孫子等
  10. 過濾選擇器
  11. :eq(index) $(“li:eq(2)”).css(“color”, ”red”) 獲取到的li元素中,選擇索引號為2的元素,索引號index從0開始。
  12. :odd $(“li:odd”).css(“color”, ”red”) 獲取到的li元素中,選擇索引號為奇數的元素
  13. :even $(“li:even”).css(“color”, ”red”) 獲取到的li元素中,選擇索引號為偶數的元素
  14. 篩選選擇器(方法)
  15. children(selector) $(“ul”).children(“li”) 相當于$(“ul>li”),子類選擇器
  16. find(selector) $(“ul”).find(“li”) 相當于$(“ul li”),后代選擇器
  17. siblings(selector) $(“#first”).siblings(“li”) 查找兄弟節點,不包括自己本身。
  18. parent() $(“#first”).parent() 查找父親
  19. eq(index) $(“li”).eq(2) 相當于$(“li:eq(2)”),index從0開始

bind 算法

只要掌握核心幾點就沒問題:

  1. Function.bind返回的也是一個函數,所以注定發生了閉包,
  2. 在返回的這個函數中去調用一個其他的函數,這其實本質上就是函數鉤子(HOOK)

關于在JS里的函數鉤子,我認為只需要維護以下三點即可:

  1. 保持函數的this指向
  2. 保持函數的所有參數都傳遞到目標函數
  3. 保持函數的返回值

有了以上這幾點,這個函數就非常好寫了,下面是MSDN上的標準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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容