1. 程式人生 > >11-撩課大前端-面試寶典-第十一篇

11-撩課大前端-面試寶典-第十一篇

一、演算法題部分

1. 如何獲取瀏覽器URL中查詢字串中的引數

function getParamsWithUrl(url) {    
   var args = url.split('?');   
   if (args[0] === url) {        
       return "";
    }    
    var arr = args[1].split('&');    
    var obj = {};    
    for ( var i = 0;
     i < arr.length; i++) 
     {        
       var arg = arr[i].split('=');
        obj[arg[0]] = arg[1];
    }    
    return obj;
}
 
var href = getParamsWithUrl
('http://www.itlike.com?id=1022&name=撩課&age=1');
console.log(href['name']); // 撩課

2. 寫一個深度克隆方法(es5)?

/**
 *  深拷貝
 * @param {object}fromObj 拷貝的物件
 * @param {object}toObj  目標物件
 */
function deepCopyObj2NewObj(fromObj, toObj) {   
   for(var key in fromObj){       
     // 1. 取出鍵值對
     var fromValue = fromObj[key];       
     // 2. 檢查當前的屬性值是什麼型別
     // 如果是值型別,那麼就直接拷貝賦值
     if(!isObj(fromValue)){
       toObj[key] = fromValue;
     }else {           
        // 如果是引用型別,
        // 那麼就再呼叫一次這個方法,
        // 去內部拷貝這個物件的所有屬性
        var tempObj = 
        new fromValue.constructor;
        console.log(fromValue.constructor);
        deepCopyObj2NewObj(fromValue, tempObj);
        toObj[key] = tempObj;
     }
   }
}

/**
 * 輔助函式, 判斷是否是物件
 * @param {object}obj
 * @returns {boolean}
 */
function isObj(obj) {  
    return obj instanceof Object;
}

3. 對陣列[1,2,3,8,2,8]進行去重,es5或者es6方法?

es5四種方式:
方式一:
Array.prototype.unique1 = function() {    
    // 1. 定義陣列
    var temp = [];    
    // 2. 遍歷當前陣列
    for(var i = 0; 
       i < this.length; i++) {        
        // 3.如果當前陣列的第i
        // 已經儲存進了臨時陣列,
        // 那麼跳過,否則把當前項
        // push到臨時數組裡面
        if (-1 === temp.indexOf(this[i])) 
        {
           temp.push(this[i]);
        }
    }    
    return temp;
};

方式二:
Array.prototype.unique2 = function() {    
    //1. hash為hash表,r為臨時陣列
    var hash = {}, temp=[];    
    // 2.遍歷當前陣列
    for(var i = 0; i < this.length; i++)
    {        
        // 3. 如果hash表中沒有當前項
        if (!hash[this[i]])
        {            
            // 4.存入hash表
            hash[this[i]] = true;            
            // 5.把當前陣列的當前項
            // push到臨時數組裡面
            temp.push(this[i]);
        }
    }    
    return temp;
};

方式三:
Array.prototype.unique3 = function() {    
    var n = [this[0]];    
    for(var i = 1; 
        i < this.length; i++){        
       if (this.indexOf(this[i]) === i) {
            n.push(this[i]);
        }
    }    
    return n;
};

方式四:
Array.prototype.unique4 = function() {    
    this.sort();    
    var re=[this[0]];    
    for(var i = 1; 
        i < this.length; i++) {        
     if( this[i] !== re[re.length-1]){
        re.push(this[i]);
     }
    }    
    return re;
};

es6實現方式:
Array.prototype.unique =
Array.prototype.unique || 
() =>{    
    return [...new Set(this)];
};

4. 如何判斷一個物件是否為陣列?

function isArray(arg) {    
   if (typeof arg === 'object') {        
       return 
       Object.prototype.toString.call(arg)
       === '[object Array]';
    }   
    return false;
}

5. 氣泡排序?

思路:
每次比較相鄰的兩個數,
如果後一個比前一個小,換位置;

var arr = [2, 0, 1, 9, 8, 7, 3];
function bubbleSort(arr) {   
   for (var i = 0;
        i < arr.length - 1; i++) {
      for(var j = 0; 
          j < arr.l    i - 1; j++) {
         if(arr[j + 1] < arr[j]) {
            var temp;
            temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
         }
       }
   }
  return arr;
}
console.log(bubbleSort(arr));

6. 快速排序?

思路: 採用二分法,取出中間數,
陣列每次和中間數比較,
小的放到左邊,大的放到右邊;

var arr = [2, 0, 1, 9, 8, 7, 3];
function quickSort(arr) {
    if(arr.length == 0) {
       // 返回空陣列
       return [];    
    }
    var cIndex = Math.floor(arr.length / 2);    
    var c = arr.splice(cIndex, 1);    
    var l = [];    
    var r = [];
    for (var i = 0; 
         i < arr.length; 
         i++) {  
      if(arr[i] < c) {
         l.push(arr[i]);
      } else {
         r.push(arr[i]);
      }
    }    
    return quickSort(l).concat(c, quickSort(r));
}
console.log(quickSort(arr));

7. 正則表示式驗證郵箱格式?

var reg = /^(\w)+(\.\w+)*@(\w)+((\.\w{2,3}){1,3})$/;
var email = "[email protected]";
console.log(reg.test(email));  // true  

8. 正則表示式清除字串前後的空格?

function trim(str) {
    if (str && typeof str === "string") 
    {        
        // 去除前後空白符
        return 
        str.replace(/(^\s*)|(\s*)$/g,""); 
    }
}

------------------------------------------------------------------華麗分割線--------------------------------------------------------------------

二、JS系列部分

1. var的變數提升的底層原理是什麼?

JS引擎的工作方式是:
1) 先解析程式碼,獲取所有被宣告的變數;
2) 然後在執行。也就是說分為預處理和執行兩個階段。
補充:
變數提升:所有變數的宣告語句都會被提升到程式碼頭部。
但是變數提升只對var命令宣告的變數有效,如果一個變數不是
用var命令宣告的,就不會發生變數提升。
js裡的function也可看做變數,也存在變數提升情況。

2. JS如何計算瀏覽器的渲染時間?

瀏覽器的渲染過程主要包括以下幾步:
1) 解析HTML生成DOM樹。
2) 解析CSS生成CSSOM規則樹。
3) 將DOM樹與CSSOM規則樹合併在一起生成渲染樹。
4) 遍歷渲染樹開始佈局,計算每個節點的位置大小資訊。
5) 將渲染樹每個節點繪製到螢幕。

優化考慮:
CSS 優先:引入順序上,CSS 資源先於 JavaScript 資源。
JS置後:通常把JS程式碼放到頁面底部,且JavaScript 應儘量少影響 DOM 的構建。

3. JS的回收機制?

垃圾回收機制就是間歇的不定期的尋找到不再使用的變數,
並釋放掉它們所指向的記憶體; 主要為了以防記憶體洩漏,
(記憶體洩漏: 當已經不需要某塊記憶體時這塊記憶體還存在著), 

JS有兩種變數: 全域性變數和在函式中產生的區域性變數。
區域性變數的生命週期在函式執行過後就結束了,
此時便可將它引用的記憶體釋放(即垃圾回收); 
但全域性變數生命週期會持續到瀏覽器關閉頁面。

JS執行環境中的垃圾回收器有兩種方式:
標記清除(mark and sweep)、
引用計數(reference counting)。

標記清除:  垃圾收集器給記憶體中的所有變數都加上標記,
然後去掉環境中的變數以及被環境中的變數引用的變數的標記。
在此之後再被加上的標記的變數即為需要回收的變數,
因為環境中的變數已經無法訪問到這些變數。

引用計數(reference counting):  這種方式常常會引起記憶體洩漏,
低版本的IE使用這種方式。機制就是跟蹤一個值的引用次數,
當宣告一個變數並將一個引用型別賦值給該變數時該值引用次數加1,
當這個變數指向其他一個時該值的引用次數便減一。
當該值引用次數為0時就會被回收。

4. 寫一下節點增刪改?

//  注意:動態建立元素不會直接顯示在頁面當中,
// 前面必須是document,不能是其他
1)document.createElement(標籤名); 

// 在指定父級子節點最後一個後面追加子元素
2)父級.appendChild(要追加的元素) ;   

// 在父級的指定子元素前面插入一個新元素
(注意:先判斷如果第二個引數的節點是否存在)
3)父級.insertBefore(新的元素,指定的已有子元素); 

// 深克隆(負值標籤、標籤屬性、標籤裡面內容); 
// 淺克隆(負值標籤、標籤屬性不復制標籤裡面內容)
4)元素.cloneNode(true) 或者元素.cloneNode(false);
5)父級.removeChild(已有子元素);
6)父級.replaceChild(新的元素節點,原有元素節點);

5. 如何獲取元素的父節點和兄弟節點,寫一下?

獲取父節點:
// 1. parentNode獲取父節點
// 獲取的是當前元素的直接父元素。
var p = document.getElementById("test").parentNode;
 
// 2. parentElement獲取父節點
// parentElement和parentNode一樣,
只是parentElement是ie的標準。
var p1 = document.getElementById("test").parentElement;

// 3. offsetParent獲取所有父節點
var p2 = document.getElementById("test").offsetParent;

獲取兄弟節點:
// 1. 通過獲取父親節點再獲取子節點來獲取兄弟節點
var brother1 = document.getElementById("test").parentNode.children[1];

// 2. 獲取上一個兄弟節點
// 在獲取前一個兄弟節點的時候可以使用previousSibling
// 和previousElementSibling。
// 他們的區別是previousSibling會匹配字元,
// 包括換行和空格,而不是節點。
// previousElementSibling則直接匹配節點。
var brother2 = document.getElementById("test").previousElementSibling;
var brother3 = document.getElementById("test").previousSibling;

// 3. 獲取下一個兄弟節點
var brother4 = document.getElementById("test").nextElementSibling;
var brother5 = document.getElementById("test").nextSibling;

6. 給你一個亂序陣列,你怎麼排序?

撩課小編:  sort, 冒泡, 選擇, 二分法....

7. 原生JS都有哪些方式可以實現兩個頁面間的通訊?


1)  通過url位址列傳遞引數;
    例如:點選列表頁中的每一條資料,
    我們跳轉到同一個詳細頁面,
    但是根據點選的不一樣可以看到
    不同的內容,這樣的話我們就可以
    在URL中傳遞不同的值來區分了;

2)  通過本地儲存 cookie、localeStorage、
    sessionStroage...,例如:京東的登入,
    我們在登入頁登入完成後,
    把使用者資訊儲存到本地,
    然後在其它頁面中如果需要使用的話,
    我們直接從本地的儲存資料中拿
    出來用即可;

3)  使用iframe在A頁面中嵌入B頁面,
    這樣的話,在A中可以通過一些屬性
    和方法實現和B頁面的通訊;

4)  利用postMessage實現頁面間通訊, 
    父視窗往子視窗傳遞資訊, 
    子視窗往父視窗傳遞資訊。

8. 原生JS動態向一個div中插入1000個div標籤,如何實現?

此題主要考效能!

1)  可以用JS中的createElement建立div,
    每當建立一個就把它新增到div中, 
    但會造成引發迴流的次數太多;

2)  使用字串拼接的方式,
    把1000個div都拼接完成後,
    統一的新增到頁面中, 
    但會對div原有的元素標籤產生影響:
    原來標籤繫結的事件都消失了

3)  綜合1和2可以使用文件碎片方式來處理。

追問:如果是建立1000萬個呢?

可採用的方案: 資料分批非同步載入
1)  首先把前兩螢幕的資料量
    (例如:300條)先獲取到,
    然後使用字串拼接或者文件碎片
    的方式繫結到頁面中; 

2)  當瀏覽器滾動到指定的區域的
    時候在載入300條...以此類推。

9. 程式出現bug了,你是如何除錯的?

1)  在控制檯加斷點,
    F10->逐過程  F11->逐語句;
2) 在程式碼重點的位置加入console.log輸出對應的值來進行除錯;
3) debugger除錯;
4) 程式碼分割還原除錯;
5) 異常捕獲機制, 記錄執行日誌;
6) 單元測試。

10. 開發中是如何進行效能優化的?

現在框架(vue, react,...)、構建工具(webpack, ...)
已經給我們解決掉大部分的效能優化問題, 
面試時, 可以就你瞭解的框架來深入剖析, 
但此題應該是考原生JS的範疇,
參考答案如下:
1) 雅虎35條效能優化黃金定律;
2) JS程式碼優化:
   a. 專案中的JS/CSS檔案最好一個頁面只用一個, 
      需要把JS/CSS進行合併壓縮, 
      並且減少頁面中的垃圾冗餘程式碼。
      專案的資原始檔在伺服器上最好
      做一下GZIP壓縮。
   
  b. 解除檔案快取; 
     我們修改程式碼並上傳, 
     如果之前頁面訪問過該網站,
     很有可能不能立即見效; 
     我們在引入CSS/JS檔案的時候, 
     在檔名的後面加上版本號(加時間戳), 
     比如:     
     <script src='itlike.com.js?_=202001...'></script>; 
     當我們上傳新的檔案後
     把時間戳改一下就可以清除快取了。

  c. 移動端儘量少用圖片:
      icon能用svg畫的不用圖片;
     靜態資源圖:做佈局的時候就能確定下來的圖片,
      比如:     
      1)css sprite圖片合併(針對於小圖片)
      2)做圖片延遲載入
         (針對於大圖片 頭部的長條圖片、背景大圖...),
         開始給一張預設的小的圖片
         (最好維持在10kb以內)
      3)base64
        (存在問題: 頁面的程式碼太臃腫了,以後維護不好操作);  
        如果專案中由於圖片太大實在解決不了, 
        改成base64就解決了
   
  d. 動態資料圖:
     通過ajax從後臺讀取回來的圖片 , 圖片懶載入;

  e. 音視訊檔案的優化: 
     載入頁面的時候,儘量不要載入音視訊檔案,
     當頁面中的其他資源載入完成後,
     再開始載入音視訊檔案; 
     目前移動端經常給音視訊做的優化是:
     走直播流檔案(音訊字尾名是m3u8格式);

  f. 減少頁面資源請求的次數:
     如果當前只是一個宣傳頁,
     或者是一個簡單的頁面, 
     使用的css和js可以採用內嵌式開發;

  g. ajax資料請求分批請求, 
     例如:一次要請求10000條資料的話,
     我們每一次只請求100條,第一螢幕肯定能看全了,
     當頁面滾動到對應的其它螢幕的時候,
     在載入下一個100條...

  h. 做資料的二次快取,  
     能用CSS3做動畫的絕對不用JS,
     能使用transform儘量使用,
     能用animation的進行不用transition...
     儘量減少同步操作,多用非同步操作;
     能使用原生JS自己編寫的,
     絕對不用外掛或者框架;  

在這裡插入圖片描述
11. 請描述下JS中事件冒泡機制?

冒泡型事件:事件按照從最特定的
事件目標到最不特定的事件目標
(document物件)的順序觸發。

捕獲型事件:事件從最不精確的對
象(document 物件)開始觸發,然後
到最精確。(也可以在視窗級別捕獲
事件,不過必須由開發人員特別指定)。

支援W3C標準的瀏覽器在新增事件時
用addEventListener(event,fn,useCapture)方法,
基中第3個引數useCapture是一個Boolean值,
用來設定事件是在事件捕獲時執行,
還是事件冒泡時執行。

而不相容W3C的瀏覽器(IE)用attachEvent()方法,
此方法沒有相關設定,
不過IE的事件模型預設是在事件冒泡時執行的,
也就是在useCapture等於false的時候執行,
所以把在處理事件時把useCapture
設定為false是比較安全,
也實現相容瀏覽器的效果。

如下圖所示:

在這裡插入圖片描述
12. 為什麼利用多個域名來提供網站資源會更有效?

1.CDN快取更方便

2.突破瀏覽器併發限制
(一般每個域名建立的連結不超過6個)

3.Cookieless,節省頻寬,
尤其是上行頻寬一般比下行要慢;

4.對於UGC的內容和主站隔離,
防止不必要的安全問題
(上傳js竊取主站cookie之類的)。
正是這個原因要求使用者內容的域名
必須不是自己主站的子域名,
而是一個完全獨立的第三方域名。

5.資料做了劃分,
甚至切到了不同的物理叢集,
通過子域名來分流比較省事。

補充: 關於Cookie的問題,
頻寬是次要的,安全隔離才是主要的。
關於多域名,也不是越多越好,
雖然伺服器端可以做泛解釋,
瀏覽器做dns解釋也是耗時間的,
而且太多域名,如果要走https的話,
還有要多買證書和部署的問題。

13. 請說出三種減少頁面載入時間的方法?

1.優化圖片;
精靈圖片, 字型圖示
SVG, GIF, WEBP
(可用在一些對顏色要求不高的地方)

2. 優化CSS
(壓縮合並css,
如margin-top,margin-left...)

3. 網址後加斜槓
(如www.itlike.com/目錄,
會判斷這個“目錄是什麼檔案型別,
或者是目錄。)

4. 標籤標明高度和寬度
(如果瀏覽器沒有找到這兩個引數,
它需要一邊下載圖片一邊計算大小,
如果圖片很多,
瀏覽器需要不斷地調整頁面。
這不但影響速度,也影響瀏覽體驗。
當瀏覽器知道了高度和寬度引數後,
即使圖片暫時無法顯示,
頁面上也會騰出圖片的空位,
然後繼續載入後面的內容。
從而載入時間快了,
瀏覽體驗也更好了。)

5.減少http請求
(合併檔案,合併圖片)。

14. cookie 和session 的區別?

1. cookie資料存放在客戶的瀏覽器上,
session資料放在伺服器上。

2. cookie不是很安全,
別人可以分析存放在本地的COOKIE,
並進行COOKIE欺騙,
考慮到安全應當使用session。

3. session會在一定時間內儲存在伺服器上。
當訪問增多,會比較佔用你伺服器的效能
考慮到減輕伺服器效能方面,
應當使用COOKIE。

4. 單個cookie儲存的資料不能超過4K,
很多瀏覽器都限制一個站點最多儲存20個cookie。

5. 開發建議:將登入,使用者等重要資訊存放為session,
其他資訊如果需要保留,可以放在cookie中。

PS: 額外閱讀

應用場景

經常登入一個網站,
今天輸入使用者名稱密碼登入了,
第二天再開啟很多情況下就直接打開了
。這個時候用到的一個機制就是cookie。

session的一個場景是購物車,
添加了商品之後客戶端處
可以知道添加了哪些商品,
而伺服器端如何判別呢,
所以也需要儲存一些資訊,
這裡就用到了session。

Cookie
Cookie是訪問某些網站以後
在本地儲存的一些網站相關的資訊,
下次再訪問的時候減少一些步驟。

另外一個更準確的說法是:
Cookies是伺服器在本地機器上
儲存的小段文字並隨每一個請求
傳送至同一個伺服器,
是一種在客戶端保持狀態的方案。

Cookie的主要內容包括:
名字,值,過期時間,路徑和域。

Session

Session是存在伺服器的
一種用來存放使用者資料的類
HashTable結構。

當瀏覽器 第一次傳送請求時,
伺服器自動生成了一個HashTable
和一個Session ID用來唯一標識這個HashTable,
並將其通過響應傳送到瀏覽器。

當瀏覽器第二次傳送請求,
會將前一次伺服器響應中的Session ID
放在請求中一併傳送到伺服器上,
伺服器從請求中提取出Session ID,
並和儲存的所有Session ID進行對比,
找到這個使用者對應的HashTable。

一般這個值會有一個時間限制,
超時後毀掉這個值,預設是20分鐘。

Session的實現方式和Cookie有一定關係。
試想一下,建立一個連線就生成一個session id,
那麼開啟幾個頁面就好幾個了,
這顯然不是我們想要的,

那麼該怎麼區分呢?

這裡就用到了Cookie,
我們可以把session id存在Cookie中,
然後每次訪問的時候將
Session id帶過去就可以識別了
15. 如何理解閉包?
為什麼要使用閉包:
因為JS中變數的作用域分類:
全域性變數和區域性變數。

函式內部可以讀取函式外部的全域性變數;
在函式外部無法讀取函式內的區域性變數。

為了讓函式執行完成後,內部的函式、變數還
能被呼叫,可以採用閉包延長
區域性變數/函式的生命週期。

定義和用法:
當一個函式的返回值是
另外一個函式,而返回的那個函式
如果呼叫了其父函式內部的其它變數,
如果返回的這個函式在外部被執行,
就產生了閉包。

表現形式:
使函式外部能夠呼叫
函式內部定義的變數。

使用場景:
排他、函式節流、網路...

使用閉包的注意點:

濫用閉包,會造成記憶體洩漏;
由於閉包會使得函式中的變數
都被儲存在記憶體中,記憶體消耗很大,
所以不能濫用閉包,
否則會造成網頁的效能問題,
在IE中可能導致記憶體洩露。
解決方法是,在退出函式之前,
將不使用的區域性變數指向null。

16.JS有哪些手段可以實現繼承?

1. 原型鏈繼承
將父類的例項作為子類的原型;

2. 藉助建構函式繼承
使用父類的建構函式來增強子類例項,
等於是複製父類的例項屬性給子類;
(沒用到原型)

3. 寄生組合繼承(完美)
通過寄生方式,
砍掉父類的例項屬性,
這樣,在呼叫兩次父類的構造的時候,
就不會初始化兩次例項方法/屬性,
避免的組合繼承的缺點

4. 組合繼承
通過呼叫父類構造,
繼承父類的屬性並保留傳參的優點,
然後通過將父類例項作為子類原型,
實現函式複用

5. 拷貝繼承
支援多繼承,無法獲取父類
不可列舉的方法

6. 例項繼承
為父類例項新增新特性,
作為子類例項返回

17. 用純JS實現,點選一個列表時,輸出對應的索引?

方式一:
for(let i = 0, 
    len = lis.length; 
    i < len; i++){
    lis[i].addEventListener('click',
      function () {        
        console.log(i);
      }, false);
}

方式二:
for(var i = 0, 
    len = lis.length; 
    i < len; i++) {
    (function (i) {
        lis[i].addEventListener
        ('click', function () {            
             console.log(i);
        }, false);
    })(i)
}

方式三:
let ul = document.querySelector('ul');
let lis = document.querySelectorAll('ul li');
ul.addEventListener('click', 
function (e) {    
  let target = e.target;    
  if(target.nodeName.toLowerCase() === 'li') {        
      console.log([].indexOf.call(lis, target));
    }
}, false);

18. 以下程式碼有記憶體洩漏嗎?

var user = {    
    name: '撩課', 
    age: 12, 
    gender: '女'
};

var box = document.getElementById('box');
box.onclick = function() {
    box.innerHTML = user.name;
};
// ...其他操作
user = null; // 釋放物件

答:存在記憶體洩漏,
這是js中垃圾回收的引用計數造成的。
完全去除記憶體洩漏是不現實的,但是,
如果採用下面的方法可以減少記憶體洩漏:

var user = {    
   name: '撩課',    
   age: 12,    
   gender: '女'
};

var box = document.getElementById('box');
(function (name) {
    box.onclick = function() {
        box.innerHTML = name;
    };
})(user.name);
// ...其他操作
user = null; // 釋放物件

19. es6中let,const,var的區別是什麼?

var :宣告全域性變數;
let :宣告塊級變數,即區域性變數, 定義後可以修改;
const :用於宣告常量,就是定義後不能再修改值或者引用值的常量,也具有塊級作用域4

20. 說說對es6中=>的理解?

箭頭函式相當於匿名函式,
並且簡化了函式定義,
箭頭左邊是引數,
右邊是返回值。

箭頭函式看上去
是匿名函式的一種簡寫,
但實際上,箭頭函式和
匿名函式有個明顯的區別:

箭頭函式內部的this是詞法作用域,
由上下文確定。

21. 點選按鈕發出ajax請求,如何防止使用者在此請求方式返回之前再次點選?

// 點選提交按鈕的時候,
// 把這個提交這個處理函式給解綁掉,
// 請求完成的時候在繫結回來
function clickHandler(){
  $(this).unbind('click', clickHandler);
    $.ajax({        
       url : 'url',        
       dataType : 'json',
       type : 'post',
       success : function (data) {
         if (data.success) { 
          //提交成功做跳轉處理
         } else { 
          //處理失敗,重新繫結點選事件
          $(self).click(clickHandler);
         }
        }
  });
}
                
$('#itlike').click(clickHandler);
                
// 可以點選後讓按鈕不可用,
// 如果提交失敗可以再次設定為可用
// 1.讓按鈕不可用
$("#itlike").attr("disabled","disabled");
$.ajax({
    url : 'url',
    dataType : 'json',    
    type : 'post',    
    success : function (data) {
       if (data.success) {
       // 提交成功做跳轉處理
       } else {            
       // 處理失敗,重新繫結點選事件
       // 2. 讓按鈕可用
       $("#itlike").removeAttr("disabled");
      }
    }
});

22. 請說明ECMAScript, JavaScript, Jscript之間的關係?

ECMAScript提供指令碼語言必須遵守的規則、
細節和準則,是指令碼語言的規範。
比如:ES5,ES6就是具體的一js版本。

JavaScript是ECMAScript的一個分支版本, 
JavaScript 實現了多數 ECMA-262 中
描述的 ECMAScript 規範,但存在少數差異。 

JScript是微軟公司對ECMA-262語言規範的
一種實現,除了少數例外(這是為了保持向後相容 ),
微軟公司宣稱JScript完全實現了ECMA標準.

關係:
JavaScript和JScript都是ECMAScript的版本分支, 
二者在語法上沒有多大的區別; 
只不過一個是NetScape公司的, 一個是微軟的; 
IE系列預設是JScript, 其它的則反之用JavaScript。

23. 頁面載入過程中可能觸發哪些事件? 它們的順序是?

頁面載入時,大致可以分為以下幾個步驟:
1)  開始解析HTML文件結構;
2)  載入外部樣式表及JavaScript指令碼;
3)  解析執行JavaScript指令碼;
4)  DOM樹渲染完成;
5)  載入未完成的外部資源(如 圖片);
6)  頁面載入成功

執行順序:
1)  document readystatechange事件
2)  document DOMContentLoaded事件 
3)  window load事件

24. 函式中在宣告變數a前使用a會產生錯誤嗎? 為什麼?

不會, JS中能夠進行變數作用域提升, 
把所有變數、函式的宣告提升到當前
作用域的最前面, 但不進行賦值操作;

所以可能造成獲取的值是undefined。

25. 什麼是hash, 以及hashchange事件?

先了解下什麼是hash:hash即URL中"#"字元後面的部分:
a) 使用瀏覽器訪問網頁時,
如果網頁URL中帶有hash,
頁面就會定位到id(或name)
與hash值一樣的元素的位置;
b) hash還有另一個特點,
它的改變不會導致頁面重新載入;
c) hash值瀏覽器是不會隨請求傳送到伺服器端的;
d) 通過window.location.hash屬性獲取和設定hash值。

window.location.hash值的變化會直接
反應到瀏覽器位址列(#後面的部分會發生變化),
同時,瀏覽器位址列hash值的變化也會觸發
window.location.hash值的變化,
從而觸發onhashchange事件。

再來了解下什麼是hashchange事件:
a) 當URL的片段識別符號更改時,
將觸發hashchange事件
(跟在#符號後面的URL部分,包括#符號)
b) hashchange事件觸發時,
事件物件會有hash改變前的URL
(oldURL)和hash改變後的URL
(newURL)兩個屬性。

26. 什麼是CDN, CDN對於網站有什麼意義, 它有什麼樣的缺點?

CDN又稱為內容分發網路;  本意在於
儘可能避開網際網路上有可能影響資料
傳輸速度和穩定性的瓶頸和環節,
使內容傳輸的更快、更穩定。

主要目的:

解決因分佈、頻寬、伺服器效能帶來的訪問延遲問題,
適用於站點加速、點播、直播等場景。

使使用者可就近取得所需內容,
解決 Internet網路擁擠的狀況,
提高使用者訪問網站的響應速度和成功率。

缺點:
a) 實施複雜 , 投資大;
b) 目前大部分的CDN還只是對靜態內容加速,
對動態加速效果不好;
而雙線對動態加速的效果跟靜態是一樣的。

27. 說說你對作用域鏈的理解?

作用域鏈的作用是保證執行環境裡
有權訪問的變數和函式是有序的,
作用域鏈的變數只能向上訪問,
變數訪問到window物件即被終止,
作用域鏈向下訪問變數是不被允許的;

作用域就是變數與函式的可訪問範圍,
即作用域控制著變數與函式的可見性
和生命週期。

28. 請說說JavaScript原型,原型鏈 ?

原型: 
當我們訪問一個物件的屬性時, 
每個物件都會在其內部初始化一個屬性,
就是prototype(原型);

原型鏈:
如果這個物件內部不存在這個屬性,
那麼他就會去prototype裡找這個屬性,
這個prototype又會有自己的prototype,
於是就這樣一直找下去,
也就是我們平時所說的原型鏈;

兩者關係:
instance.constructor.prototype = instance.__proto__

29. 請解釋什麼是事件代理?

事件代理(Event Delegation),
又稱之為事件委託。
是 JavaScript 中常用繫結事件
的常用技巧。

“事件代理”即是把原本需要繫結
的事件委託給父元素,讓父元素
擔當事件監聽的角色。

事件代理的原理是DOM元素的事件冒泡。
使用事件代理的好處是可以提高效能, 
可以大量節省記憶體佔用,減少事件註冊,
比如在ul上代理所有li的click事件;

此外, 還可以實現動態新增子物件時無需
再次對其繫結事件。

30. new操作符具體完成了哪幾個操作?

1) 建立一個空物件, 定義this 變數引用該物件,同時還繼承了該函式的原型;
2) 屬性和方法被加入到 this 引用的物件中;
3) 新建立的物件由 this 所引用, 並且最後隱式的返回 this
31. 說幾條寫JavaScript的基本規範?
1) 不要在同一行宣告多個變數;
2) 請使用===/!==來比較true/false或者數值;
3) 使用物件字面量替代new Object這種形式;
4) 減少使用全域性函式, 全域性變數;
5) switch語句必須帶有default分支;
6) if語句必須使用大括號;
7) for-in迴圈中的變數;
應該使用var關鍵字明確限定作用域;
從而避免作用域全域性汙染。

32. 如何解決跨域問題?

1) jsonp
2) iframe
3) window.name 
4) window.postMessage 
5) 伺服器上設定代理頁面

33. XML和JSON的區別?

1) 資料體積方面JSON相對於XML來講,
資料的體積小,傳遞的速度更快些。
2) 資料互動方面JSON與JavaScript的互動更加方便,
更容易解析處理,更好的資料互動。
3) 資料描述方面;JSON對資料的描述性比XML較差。
4) 傳輸速度方面:JSON的速度要遠遠快於XML。

34. 在Javascript中什麼是偽陣列?如何將偽陣列轉化為標準陣列?

偽陣列(類陣列):
無法直接呼叫陣列方法, 
length屬性有什麼特殊的行為,
但仍可以對真正陣列遍歷方法來遍歷它們。

典型的是函式的argument引數,
還有像調getElementsByTagName,document.childNodes之類的,
它們都返回NodeList物件,  這些都屬於偽陣列。

可以使用Array.prototype.slice.call(fArray)將陣列
轉化為真正的Array物件。

35. 一次完整的HTTP事務是怎樣的一個過程?

基本流程:
a. 域名解析;
b. 發起TCP的3次握手;
c. 建立TCP連線後發起http請求;
d. 伺服器端響應http請求,瀏覽器得到html程式碼;
e. 瀏覽器解析html程式碼,並請求html程式碼中的資源;
f. 瀏覽器對頁面進行渲染呈現給使用者

36. 開發中有哪些常見的Web攻擊技術?

a) XSS
(Cross-Site Scripting,跨站指令碼攻擊)
指通過存在安全漏洞的Web網站註冊使用者的瀏覽器
內執行非法的HTML標籤或者JavaScript進行的一種攻擊。
b)SQL注入
c) CSRF
(Cross-Site Request Forgeries,跨站點請求偽造)
指攻擊者通過設定好的陷阱,強制對已完成的認證使用者進行
非預期的個人資訊或設定資訊等某些狀態更新。

----------------------------------------------------------------華麗分割線----------------------------------------------------------------------

三、H5+C3部分

1. 垂直水平居中的方式?

/**
方式一:  定位
父元素設定為:position: relative; 
子元素設定為:position: absolute; 
距上50%,據左50%,然後減去元素自身寬度的距離就可以實現 
*/

width: 100px;
height: 100px;
position: absolute;
left: 50%;
top: 50%;
margin: -50px 0 0 -50px;

/** 方式二:  flex佈局 */
display: flex;  //flex佈局
justify-content: center; // 使子專案水平居中
align-items: center; // 使子專案垂直居中

/** 方式三:  table-cell  (不推薦) */
display: table-cell;
vertical-align: middle;//使子元素垂直居中
text-align: center;//使子元素水平居中

2. 實現一個三欄佈局,中間版塊自適應方法有哪些?

// 方式一: 浮動和定位
<div class="content">
   <div class="left">left</div>
   <div class="right">right</div>
   <div class="center">center</div>
</div>

.left{
    float: left;
    width: 100px;
    height: 200px; 
}
.right{    float: right;
    padding: 0;
    width: 100px;
    height: 200px;
}
.center{
    margin: 0 100px 0 200px;
}

方式二:  將父容器的position設定為relative,兩個邊欄的position設定成absolute。

3. margin坍塌?

當兩個盒子在垂直方向上設定margin值時,會出現塌陷現象。

解決方案主要包括:
1. 給父盒子新增border;
2. 給父盒子新增padding-top;
3. 給父盒子新增overflow:hidden;
4. 父盒子position:fixed;
5. 父盒子display:table;
6. 給子元素的前面新增一個兄弟元素
屬性為content:"";overflow:hidden;

4. 說說BFC原理?

BFC就是頁面上的一個隔離的獨立容器,
容器裡面的子元素不會影響到外面的元素。

因為BFC內部的元素和外部的元素絕對不會互相影響,
因此,當BFC外部存在浮動時,
它不會影響BFC內部Box的佈局,
BFC會通過變窄,而不與浮動有重疊。

同樣的,當BFC內部有浮動時,
為了不影響外部元素的佈局,
BFC計算高度時會包括浮動的高度。
避免margin重疊也是這樣的一個道理。

5. 為什麼要清除浮動?舉個實際場景?

父元素的高度是由子元素撐開的,
且子元素設定了浮動,
父元素沒有設定浮動,
子元素脫離了標準的文件流,
那麼父元素的高度會將其忽略,
如果不清除浮動,
父元素會出現高度不夠,
那樣如果設定border或者background都得不到正確的解析。

方式:
.clearfix::after,
.clearfix::before{   
   content:"";  
   display:table;   
   clear:both;
}

6. 你能描述一下漸進增強和優雅降級之間的不同嗎?

定義:
優雅降級(graceful degradation):
一開始就構建站點的完整功能,
然後針對瀏覽器測試和修復

漸進增強(progressive enhancement): 
一開始只構建站點的最少特性,
然後不斷針對各瀏覽器追加功能。

優雅降級和漸進增強都關注於同一網站
在不同裝置裡不同瀏覽器下的表現程度。

區別:
“優雅降級”觀點認為應該針對那些最高階、
最完善的瀏覽器來設計網站。

而將那些被認為“過時”或有功能缺失的瀏覽器下
的測試工作安排在開發週期的最後階段,並把測試
物件限定為主流瀏覽器(如 IE、Mozilla 等)的
前一個版本。

“漸進增強”觀點則認為應關注於內容本身。

總結:
"優雅降級"就是首先完整地實現整個網站,
包括其中的功能和效果. 然後再為那些無
法支援所有功能的瀏覽器增加候選方案, 
使之在舊式瀏覽器上以某種形式降級體驗
卻不至於完全失效。

"漸進增強"則是從瀏覽器支援的基本功能開始,
首先為所有裝置準備好清晰且語義化的html及
完整內容, 然後再以無侵入的方法向頁面增加無
害於基礎瀏覽器的額外樣式和功能。
當瀏覽器升級時, 它們會自動呈現併發揮作用。

7. 請說說瀏覽器核心的組成?

瀏覽器的結構:

1)使用者介面(UI)
包括選單欄、工具欄、位址列、後退/前進按鈕、書籤目錄等,
也就是能看到的除了顯示頁面的主視窗之外的部分;

2)瀏覽器引擎(Rendering engine)
也被稱為瀏覽器核心、渲染引擎,主要負責取得頁面內容、
整理資訊(應用CSS)、計算頁面的顯示方式,然後會輸出到
顯示器或者印表機;

3)JS直譯器
也可以稱為JS核心,主要負責處理javascript指令碼程式,
一般都會附帶在瀏覽器之中,例如chrome的V8引擎;

4)網路部分
主要用於網路呼叫,例如:HTTP請求,
其介面與平臺無關,併為所有的平臺提供底層實現;

5)UI後端
用於繪製基本的視窗部件,比如組合框和視窗等。

6)資料儲存
儲存類似於cookie、storage等資料部分,
HTML5新增了web database技術,一種完整的輕量級客
戶端儲存技術。

主要瀏覽器:
IE、Firefox、Safari、Chrome、Opera。

它們的瀏覽器核心(渲染引擎):
IE--Trident
FF(Mozilla)--Gecko
Safari--Webkit
Chrome--Blink(WebKit的分支)
Opera--原為Presto,現為Blink

8. 為什麼利用多個域名來請求網路資源會更有效?

動靜分離需求,使用不同的伺服器處理請求。
處理動態內容的只處理動態內容,不處理別的,
提高效率。

突破瀏覽器併發限制, 同一時間針對同一域名
下的請求有一定數量限制。超過限制數目的請
求會被阻止。不同瀏覽器這個限制的數目不一樣。

Cookieless, 節省頻寬,尤其是上行頻寬一般比下
行要慢。使用者的每次訪問,都會帶上自己的cookie
,久而久之耗費的頻寬還是挺大的。

假如weibo 的圖片放在主站域名下,那麼使用者
每次訪問圖片時,request header 裡就會帶有
自己的cookie ,header 裡的cookie 還不能壓縮,
而圖片是不需要知道使用者的cookie 的,所以這部分帶
寬就白白浪費了。

避免不必要的安全問題(比如: 上傳js竊取主站cookie之類的)

節約主域名的連線數,從而提高客戶端網路頻寬的利用率,
優化頁面響應。

9. 說說前端開發中, 如何進行效能優化?

1) 減少http請求次數:css spirit,data uri;
2) JS,CSS原始碼壓縮;
3) 前端模板 JS+資料,減少由於HTML標籤導致
    的頻寬浪費,前端用變數儲存AJAX請求結果,每
    次操作本地變數,不用請求,減少請求次數;
4) 用innerHTML代替DOM操作,減少DOM操作次數;
5) 用setTimeout來避免頁面失去響應;
6) 用hash-table來優化查詢;
7) 當需要設定的樣式很多時設定className而不
    是直接操作style; 
8) 少用全域性變數;
9) 快取DOM節點查詢的結果;
10) 避免使用CSS Expression;
11) 圖片預載;

12) 避免在頁面的主體佈局中使用table,
table要等其中的內容完全下載之後才會顯示出來,
顯示比div+css佈局慢;

13) 控制網頁在網路傳輸過程中的資料量; 
比如: 啟用GZIP壓縮或者保持良好的程式設計習慣,
避免重複的CSS,JavaScript程式碼,
多餘的HTML標籤和屬性。   

10. 從前端角度出發, 談談做好網站seo需要考慮什麼?

1) 語義化html標籤;
2) 合理的title, description, keywords;
3) 重要的html程式碼放前面;
4) 少用iframe, 搜尋引擎不會抓取iframe中的內容
5) 圖片加上alt

11. HTTP狀態碼及其含義?

1XX:資訊狀態碼
100 Continue 繼續,一般在傳送post請求時,
已傳送了http header之後服務端將返回此資訊,
表示確認,之後傳送具體引數資訊;

2XX:成功狀態碼
200 OK 正常返回資訊
201 Created 請求成功並且伺服器建立了新的資源
202 Accepted 伺服器已接受請求,但尚未處理

3XX:重定向
301 Moved Permanently 請求的網頁已永久移動到新位置。
302 Found 臨時性重定向。
303 See Other 臨時性重定向,且總是使用 GET 請求新的 URI。
304 Not Modified 自從上次請求後,請求的網頁未修改過。

4XX:客戶端錯誤
400 Bad Request 伺服器無法理解請求的格式,
客戶端不應當嘗試再次使用相同的內容發起請求。
401 Unauthorized 請求未授權。
403 Forbidden 禁止訪問。
404 Not Found 找不到如何與 URI 相匹配的資源。

5XX: 伺服器錯誤
500 Internal Server Error 最常見的伺服器端錯誤。
503 Service Unavailable 伺服器端暫時無法處理請求
(可能是過載或維護)。

12. html5有哪些新特性、移除了那些元素?

HTML5 現在已經不是 SGML 的子集,
主要是關於影象,位置,儲存,多工等功能的增加:
1) 繪畫標籤canvas;
2) 用於媒介回放的 video 和 audio 元素;
3) 本地離線儲存 localStorage 長期儲存資料,
瀏覽器關閉後資料不丟失;
4) sessionStorage的資料在瀏覽器關閉後自動刪除;
5) 語意化更好的內容元素,
比如article、footer、header、nav、section;
6) 表單控制元件,calendar、date、time、email、url、search;
7) webworker, websocket, Geolocation;

移除的元素:
1) 純表現的元素:basefont,big,center,font, s,strike,tt,...
2) 對可用性產生負面影響的元素:frame,frameset,noframes

13. display: none;與visibility: hidden;的區別?

相同點:它們都能讓元素不可見

不同點:
display:none;會讓元素完全從渲染樹中消失,
渲染的時候不佔據任何空間;

visibility: hidden;不會讓元素從渲染樹消失,
渲染師元素繼續佔據空間,只是內容不可見;

display: none;是非繼承屬性,
子孫節點消失由於元素從渲染樹消失造成,
通過修改子孫節點屬性無法顯示;

visibility: hidden;是繼承屬性,
子孫節點消失由於繼承了hidden,
通過設定visibility: visible;可以讓子孫節點顯示;

修改常規流中元素的display通常會造成文件重排。
修改visibility屬性只會造成本元素的重繪。

讀屏器不會讀取display: none;元素內容;
會讀取visibility: hidden;元素內容

14. px和em的區別?

px和em都是長度單位;

區別是: 
px的值是固定的,指定是多少就是多少,
計算比較容易。

em得值不是固定的,並且em會繼承父級元素的字型大小。

瀏覽器的預設字型高都是16px;
所以未經調整的瀏覽器都符合: 1em=16px;
那麼12px=0.75em, 10px=0.625em

15. CSS 去掉inline-block元素間隙的幾種方法?

間隙是怎麼來的:

間隙是由換行或者回車導致的;
只要把標籤寫成一行或者
標籤直接沒有空格,就不會出現間隙;

怎麼去除?

方法1:
元素間的間隙出現的原因
是元素標籤之間的空格,
把空格去掉間隙自然就會消失。
<div class="itlike">
  <span>撩課itlike</span><span>撩課itlike</span>
</div>

方法2:
利用HTML註釋標籤
<div class="demo">
    <span>撩課itlike</span>
    <!-- -->
    <span>撩課itlike</span>
</div>

方法3:
取消標籤閉合
<div class="demo">
    <span>撩課itlike
    <span>撩課itlike
    <span>撩課itlike
    <span>撩課itlike
</div>

方法4:
在父容器上使用font-size:0;可以消除間隙
<div class="demo">
    <span>撩課itlike</span>
    <span>撩課itlike</span>
    <span>撩課itlike</span>
    <span>撩課itlike</span>
</div>
.demo {font-size: 0;}