前端常見JS問題總結

風暴阿呆發表於2017-12-14

20. Call 和 Apply 的區別

語法:
function.call(thisObj [, arg1[, arg2[, [, ...argN]]]]);
function.apply(thisObj [, argArray] );

定義: call 和 apply 可以讓我們手動設定 this 指向
兩個引數: 第一個引數是 繫結 this 指向;第二個引數是 向將要執行的函式傳遞的引數
區別: 第二個引數, call 以一個一個的形式傳遞引數;apply 以陣列的形式傳遞引數

使用示例
var a = 10;
function sum(num1, num2) {
    console.log(this.a + num1 + num2);
}
var obj = {
    a: 20
}

sum(10, 10);    //30
sum.call(obj, 10, 10);       // 40
sum.apply(obj, [10, 10]);    // 40
複製程式碼

19. 鍵盤事件屬性

event.keyCode;    // 獲取按下的鍵盤按鍵的鍵碼值(Unicode值)
event.ctrlKey;    // 獲取是否按下了ctrl鍵
event.shiftKey;   // 獲取是否按下了shift鍵
event.altKey;     // 獲取是否按下了alt鍵
event.metaKey;    // 獲取是否按下了meta鍵
複製程式碼

18. 滑鼠事件屬性

event.screenX/event.screenY     // 獲取滑鼠基於螢幕的X軸/Y軸座標
event.clientX/event.clientY     // 獲取滑鼠基於瀏覽器視窗的X軸/Y軸座標
event.pageX/event.pageY         // 獲取滑鼠基於文件的X軸/Y軸座標

event.button   // 獲取滑鼠按下的鍵。非IE瀏覽器中0為滑鼠左鍵,1為滑鼠中鍵,2為滑鼠右鍵
event.which    // 獲取指定事件上哪個鍵盤鍵或滑鼠按鈕被按下
複製程式碼

17. addEventListener 和 attachEvent 區別

attachEvent方法適用於IE
attachEvent中的事件帶on, 而addEventListener中的事件不帶on
attachEvent 方法有兩個引數:第一個引數為事件名稱,第二個引數為接收事件處理的函式; addEventListener 方法有三個引數:第一個引數為事件名稱(不含 on,如 "click"),第二個引數為要接收事件處理的函式,第三個引數為一個bool值,預設為false

1. 新增多個事件處理程式執行的順序不同

js程式碼(addEventListener): 
var btn=document.getElementById("myBtn");  
btn.addEventListener("click",function(){  
    alert(1);     
    },false);  
  
btn.addEventListener("click",function(){  
      alert(2);     
    },false); 

//執行結果 1 ,2
複製程式碼
js程式碼(attachEvent): 
var btn=document.getElementById("myBtn");  
 btn.attachEvent("onclick",function(){  
     alert(1);     
   });  
 btn.attachEvent("onclick",function(){  
     alert(2);     
   });  

//執行結果 2 ,1
複製程式碼

2. 事件處理程式的作用域不同

DOM2級事件新增的事件處理程式,它的作用域是所屬的元素,而IE的事件處理程式會在全域性作用域中執行。

js程式碼(addEventListener): 
var btn=document.getElementById("myBtn");  
btn.addEventListener("click",function(){  
    console.log(this.id);    // myBtn   
    },false);  
複製程式碼
js程式碼(attachEvent): 
var btn=document.getElementById("myBtn");  
btn.attachEvent("onclick",function(){  
  alert(this===window);    // true   
  });  
複製程式碼

3. 移除繫結事件 removeEventListener() 和 detachEvent()

移除 addEventListener 事件:
element.removeEventListene(event, function, useCapture)
event: 事件名,注意不使用“on”字首,如 click
function: 指定事件觸發時執行的函式
useCapture: 指定事件是否在捕獲或冒泡階段執行
true: 在捕獲階段執行
false: 在冒泡階段進行,預設值為false
如果新增時用的捕獲階段,那麼在移除時也要用捕獲階段,否則無法移除它們
如果是同一個元素同一個呼叫函式同一個useCapture值繫結多次,在移除時只需要執行一次移除
複製程式碼
移除 attachEvent 事件:
element.detachEvent(event, function)
event: 事件名,注意要使用“on”字首,如 onclick
function: 指定事件觸發時執行的函式
複製程式碼

16. addEventListener 和 on 區別

html程式碼:
<div id="box">addEventListener 和 on 區別</div>
複製程式碼
js程式碼:
window.onload = function(){
     var box = document.getElementById("box");
     box.onclick = function(){
         console.log("我是box1");
     }
     box.onclick = function(){
         console.log("我是box2");
     }
}

//執行結果:“我是box2”
複製程式碼
js程式碼:
 window.onload = function(){
     var box = document.getElementById("box");
     box.addEventListener("click", function(){
         console.log("我是box1");
     })
     box.addEventListener("click", function(){
         console.log("我是box2");
     })
}
執行結果:我是box1
     我是box2
複製程式碼

第二個onclick會把第一個onclick給覆蓋了,雖然大部分情況我們用on就可以完成我們想要的結果,但是有時我們又需要執行多個相同的事件,很明顯如果用on完成不了我們想要的,而addEventListener可以多次繫結同一個事件並且不會覆蓋上一個事件

15. HTML5 新增的事件

contextmenu事件
這個事件是當滑鼠右擊的時候觸發的,但是觸發這個屬性的時候預設的行為也會被觸發,所以需要通過preventDefault()方法來阻止。

beforeunload事件
beforeunload在頁面解除安裝之前觸發,該事件會彈出一個對話方塊,詢問是否確定離開。

hashchange事件
該事件當URL中的hash值改變時觸發,通常用於Ajax應用中利用URL引數儲存導航資訊;這個在前端路由的製作中是非常有用得。

14. 阻止事件預設行為和阻止事件冒泡

html程式碼:
<div id="wrap" style="width: 200px; height: 200px; background: gray;">
    <div id="btn" style="width: 100px; height: 100px; background: orangered;"></div>
    <a id="prevent" target="_blank" href="http://www.baidu.com">preventDefault</a>
</div>
複製程式碼
標準瀏覽器的使用方法

preventDefault(): 用於阻止事件的預設行為;
比如: a 連結的跳轉行為和表單自動提交行為

js程式碼: 
var prevent = document.getElementById("prevent");
    prevent.addEventListener("click", function(event){
        event.preventDefault();
    }, false);

//使用preventDefault()方法就阻止了a標籤開啟新視窗的預設行為 
複製程式碼

stopPropagation(): 用於阻止事件的進一步獲取和傳播;
比如:阻止事件繼續向上層冒泡

js程式碼:
var btn = document.getElementById("btn"),
    wrap= document.getElementById("wrap");
    btn.addEventListener("click",function(event){
        alert("btn");
        event.stopPropagation();
    },false);
    wrap.addEventListener("click",function(){
        alert("wrap");
    },false);

//點選btn時,這樣就阻止了id="btn"向上級id="wrap"冒泡,列印出來的結果是:彈窗僅彈出btn。否則,將會先彈出btn,然後彈出wrap。
複製程式碼
低版本IE瀏覽器的使用方法
event.returnValue = false;   //阻止事件的預設行為;
event.cancelBubble = true;   //阻止事件的進一步獲取或者冒泡;
複製程式碼

使用示例

js程式碼: 
function prevent(event) {
    event = event || window.event;
    if(event.preventDefault) {
        event.preventDefault();
    } else {
        event.returnValue = false; 
    }
}
//使用 if else 去判斷
複製程式碼

13. 事件捕獲和事件冒泡

事件冒泡執行過程:從最具體的的元素(你單擊的那個元素)開始向上開始冒泡,下面的案例的順序是:content > wrap
事件捕獲執行過程:從最不具體的元素(最外面的那個盒子)開始向裡面冒泡,下面的案例的順序是:wrap > content

html程式碼:
<div id="wrap">
    <div id="content"></div>
</div>
複製程式碼
js程式碼: (addEventListener第三個引數寫的是false, 預設為false)
 window.onload = function(){
     var wrap= document.getElementById("wrap");
     var content= document.getElementById("content");
     wrap.addEventListener("click", function(){
         console.log("我是wrap");
     }, false)
     content.addEventListener("click", function(){
         console.log("我是content");
     })
}
執行結果:我是content
     我是wrap
複製程式碼
js程式碼: (addEventListener第三個引數寫的是true, 預設為false)
  window.onload = function(){
     var wrap= document.getElementById("wrap");
     var content= document.getElementById("content");
     wrap.addEventListener("click", function(){
         console.log("我是wrap");
     }, true)
     content.addEventListener("click", function(){
         console.log("我是content");
     })
}
執行結果:我是wrap
     我是content
複製程式碼

第三個引數寫的是true,則按照事件捕獲的執行順序進行。

12. 給 select 標籤 option 內容加連結

<select onchange="window.open(options[selectedIndex].value, '_self')">
    <option value="http://www.bj-hmk.com/">中文</option>
    <option value="http://en.bj-hmk.com/">English</option>
</select>
複製程式碼

11. Null 和 Undefined

undefined 表示根本不存在定義
null 表示一個值被定義了,定義為“空值”

(1)變數被宣告瞭,但沒有賦值時,就等於undefined。
(2)呼叫函式時,應該提供的引數沒有提供,該引數等於undefined。
(3)物件沒有賦值的屬性,該屬性的值為undefined。
(4)函式沒有返回值時,預設返回undefined。

所以設定一個值為 null 是合理的,如
objA.valueA = null;

但設定一個值為 undefined 是不合理的,如
objA.valueA = undefined; // 應該直接使用 delete objA.valueA; 任何一個存在引用的變數值為undefined都是一件錯誤的事情。
這樣判斷一個值是否存在,就可以用
objA.valueA === undefined // 不應使用 null 因為 undefined == null,而 null 表示該值定義為空值。

10. 解決slideDown()和slideUp()滑鼠快速移入移出,出現反覆執行的問題

方法一:
$(".orderDivMain").hover(function () {
    if (!$(".orderDivId").is(":animated")) {
        $(this).find('.orderDivId').slideDown(500);
    }
}, function () {
    if (!$(".orderDivId").is(":animated")) {
        $(this).find('.orderDivId').slideUp(500);
    }
});

方法二:
$(".orderDivMain").hover(function () {
    $(".orderDivId").slideDown(500);
});
$(".orderDivId").mouseleave(function () {
    $(".orderDivId").slideUp(500);
});
複製程式碼

9. web應用整體效能的考慮

8 9兩點 參考於 《JavaScript DOM程式設計藝術(第2版)》

儘量少訪問DOM和儘量減少標記

if(document.getElementsByTagName("a").length > 0){
    var links = document.getElementsByTagName("a");
    for(var i = 0; i < links.length; i++){
        something...
    }
}
複製程式碼

上面這段程式碼使用了兩次getElementsByTagName方法去執行相同的操作,浪費了一次搜尋。更好的辦法是把第一次搜尋的結果儲存在一個變數中,然後重用該結果:

var links = document.getElementsByTagName("a");
if(links.length > 0){
    for(var i = 0; i < links.length; i++){
        something...
    }
}
複製程式碼

合併和放置指令碼

<script src="js/functionA.js"></script>
<script src="js/functionB.js"></script>
<script src="js/functionC.js"></script>
複製程式碼
<script src="js/function.js"></script>
複製程式碼

上面兩種做法,推薦的做法是把第一種functionA.js、functionB.js、functionC.js合併到一個指令碼檔案中。這樣就可以減少載入頁面時傳送的請求數量,而減少請求數量通常都是效能優化時首先要考慮的。

位於<head>塊中的指令碼會導致瀏覽器無法並行載入其他檔案,把<script>標籤放在</body>標記之前,就可以讓頁面變的更快。

8. 使你所寫的頁面能夠向後相容、平穩退化

針對這一問題的最簡單的解決方案是,檢測瀏覽器對javascript的支援程度,即物件檢測。幾乎所有的東西(包括各種方法在內)都可以被當作物件來對待,這意味著我們可以很容易的把不支援某個特定的DOM方法的瀏覽器檢測出來:

if(method){
    statements
}
複製程式碼

例如,檢測瀏覽器是否支援getElementById方法:

if(document.getElementById){
    statements using getElementById
}
複製程式碼

但是如果需要檢測多個DOM方法或者屬性是否存在,那麼最重要的語句可能會被深埋在一層又一層的花括號裡,使得程式碼將會很難閱讀和理解。

那麼就可以把測試的條件改為“如果你不理解這個方法,請離開”則會變得簡單明瞭。

if(!document.getElementById){
    return false;
}
複製程式碼

若需要測試多個方法或屬性是否支援,可以使用“邏輯或”操作符將其合併:

if(!document.getElementById || !document.getElementsByTagName){
    return false;
}
複製程式碼

7. jQuery中attr和prop的區別

處理HTML元素本身就帶有的固有屬性時使用prop方法

處理HTML元素我們自己定義的Dom屬性時使用attr方法

<input class='check1' type='checkbox'>選擇1
<input class='check2' type='checkbox' checked>選擇2
複製程式碼

checked屬於checkbox元素的固有屬性,讓我們來看看prop和attr的結果有什麼不同:

prop方法:
$('.check1').prop('checked') -- false
$('.check2').prop('checked') -- true 
複製程式碼
attr方法:
$('.check1').attr('checked') -- undefined 
$('.check2').attr('checked') -- 'checked'
複製程式碼

6. 在選中的元素上繫結事件和通過代理繫結事件的區別

在選中的元素上繫結click事件:
$('a').on('click', function(){});
複製程式碼
通過代理繫結click事件:
$(document).on('click', 'a', function(){});
複製程式碼

在選中的元素上繫結事件只能為頁面現有的a元素繫結點選事件,如果是動態生成的新的a元素是沒有事件的。

通過代理繫結事件是將指定的事件繫結在document上,新新增的a元素也能觸發此事件。

5. reload 方法,強迫瀏覽器重新整理當前頁面。

語法:location.reload([falseOrTrue]) ;
引數: falseOrTrue, 可選引數。 預設為 false,從客戶端快取裡取當前頁。true,則以 GET 方式,從服務端取最新的頁面,相當於客戶端點選 F5(“重新整理”)。

html程式碼:
    <a href="javascript:location.reload();">點選重新整理頁面</a>
    <span onclick="location.reload(true);">點選重新整理頁面</span>
複製程式碼

4. 禁止頁面選取內容

適用於IE、Chrome瀏覽器,在 head 的 <script> 標籤裡面新增 js 程式碼

js程式碼:
document.onselectstart = function (e) { return false; }
或者
document.onselectstart = new Function('event.returnValue = false;');
複製程式碼

在firefox火狐瀏覽器中,禁止元素被選取可以採用 CSS 樣式在來控制

CSS程式碼:
body{
    -moz-user-focus: ignore;  
    -moz-user-input: disabled; 
    -moz-user-select: none;
}
複製程式碼

禁止滑鼠右鍵

js程式碼:
document.oncontextmenu = function(e){return false;}
複製程式碼

3. 正規表示式驗證

//  驗證是否為手機號
function phone(num) {
    return /^1[34578]\d{9}$/.test(num);
}

//  驗證是否為郵箱
function email(e) {
    return /^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/.test(e);
}
複製程式碼

2. 返回頂部

js程式碼:
window.onscroll = function () {
    if (document.body.scrollTop || document.documentElement.scrollTop > 0) {
        document.getElementById('back_top').style.display = "block";
    } else {
        document.getElementById('back_top').style.display = "none";
    }
}
$('.back_top').click(function () {
    $("html,body").animate({ scrollTop: 0 }, 500);
    return false;
});
複製程式碼

1. js 中 Error 錯誤

ReferenceError:作用域判別錯誤,通過作用域鏈的搜尋找不到相應的變數。

TypeError:可以通過作用域搜尋到變數,但是對變數的操作不合法。

SyntaxError:語法錯誤。


相關文章