javascript擼來擼去(1)-事件監聽與物件屬性

weixin_33866037發表於2017-12-28

本坑開始新的系列記錄,該記錄是回顧這麼多年以來學習到javescript知識以及通過這種讓自己更加獨立的審視相關基礎能力和方式方法,系列文章記錄的內容都會有以下特點:

> 非常之基礎
> 非常之實用
> 稍微帶點趣味

這篇文章圍繞事件監聽與物件屬性來記錄,你將瞭解到:

1.`addEvenListener `第三個引數`passvie`是用來幹嘛的.
2.es2015中物件屬性中一幫隱藏的`getter`和`setter`方法。

從以下這段程式碼開始,一步步來解釋說明:

var passiveSupported = false;

try {
  var options = Object.defineProperty({}, "passive", {
    get: function() {
      passiveSupported = true;
      console.log("11111")
    }
  });

  window.addEventListener("test", null, options);
} catch(err) {}

如果javascript學的很隨便的同學,可能對這段程式碼的功能很疑惑。因為像Object.defineProperty比較少用到。我們從這個方法開始解釋說明,最終獲知這段程式碼是有什麼功能作用。

Object.defineProperty

在MDN裡我們知道它有三個引數物件:obj, prop, descriptor。顯然obj 就是要改變的那個物件,而prop是需要改變或者新增的屬性,descriptor是js已經規定好的屬性的屬性,其中enumerable就是其中之一。在es2016的版本中,這個屬性得到比較上一個版本更多使用。有了這個屬性我們可以用map,keys等等遍歷函式對有著該屬性的資料集合進行處理,可以解放不小的生產力。
回到程式碼中來,Object定義了一個{}設定了一個passive,並且提供了一個getter 方法。我們不妨在addEvenListener前把options列印出來,如下圖:

4455053-86baf6f7bbe160ae.png
點選紅色方框,getter將執行

什麼時候回getter被呼叫了,執行完window.addEventListener("test", null, options);這時程式就會查詢passvie的屬性值,雖然這是一個沒有效果的監聽test,永遠不會觸發該事件。最終的效果就是 passiveSupported = true。那麼這有什麼意義呢?

我們先回顧下物件getter方法是什麼。
JavaScript物件的屬性是由名字、值和一組特性(可寫、可列舉、可配置等)構成的。在ECMAScript 5中,屬性值可以用一個或兩個方法代替,這兩個方法就是gettersetter

var options = {
    a: 2,
    get b(){
        return 3;
    }   
};

console.log(options.a);//2
console.log(options.b);//3

上面的程式碼中,屬性a稱為“資料屬性”,它只有一個簡單的值;像屬性b這種用gettersetter方法定義的屬性稱為“存取器屬性”。如果屬性同時具有gettersetter,那麼它是一個讀/寫屬性。如果它只有getter方法,那麼它是一個只讀屬性。如果它只有setter方法,那麼它是一個只寫屬性(資料屬性中有一些例外),讀取只寫屬性總是返回undefined.存取器屬性定義為一個或兩個與屬性同名的函式,這個函式定義沒有使用function關鍵字,而是使用get或set,也沒有使用冒號將屬性名和函式體分開,但函式體的結束和下一個方法之間有逗號隔開。當程式查詢存取器的屬性值時,JavaScript代用getter方法(無引數),這個方法的返回值就是該屬性存取表示式的值。當程式設定一個存取器屬性值時,JavaScript呼叫setter方法,將賦值表示式右側的值當作引數傳入setter。從某種意義上來說,這個方法負責設定屬性值,可以忽略該方法的返回值。
開頭的程式碼段裡的getter方法是一個匿名函式,而這個例子中不是,但是本質是一樣的。

addEvenListener

在過去經驗中,我們往往在使用addEvenListener的第三個引數時,是使用布林值獲取直接不行,在MDN裡,它是一個options物件。我們一般會用到的是這幾個屬性:capture,once,passivecapture就是我們經常實際使用的布林值設定useCapture,如果是true捕獲階段觸發事件,相反則冒泡階段觸發。預設情況,事件是在繫結元素冒泡階段觸發。MDN對passive這個屬性的解釋是:當為true時,表示 listener 永遠不會呼叫 preventDefault(),如果 listener仍然呼叫了這個函式,客戶端將會忽略它並丟擲一個控制檯警告。
preventDefault() 是指阻止預設事件行為,比如 a標籤的連結,點選會跳轉;還有觸發touchstart事件,手機端滾螢幕效果也是一種預設行為。如果在這些事件處理函式出現preventDefault,比如滾屏效果就不會滾動,顯然不是我們要的。所以就需要一個引數來告訴瀏覽器,這個函式裡沒有preventDefault方法。這個屬性最初在谷歌瀏覽器裡使用,它的目的是為解決滾動和觸控事件的卡頓。目前火狐也支援,其他瀏覽器相對舊版連option物件都不支援。兼於這個問題就有了開始的那段程式碼用於相容性問題解決。
如果相容性不支援option ,執行window.addEventListener('test', null, options)options會自動被瀏覽器轉為true,getter方法也不會執行。

捕獲階段:首先window會獲捕獲到事件,之後documentdocumentElementbody會捕獲到,再之後就是在bodyDOM元素一層一層的捕獲到事件。
目標階段:對目標元素在捕獲和冒泡都繫結觸發事件,則捕獲觸發事件一次,冒泡觸發事件一次,兩者之間可視為目標階段。
冒泡階段:會和捕獲階段相反的步驟將事件一步一步的冒泡到window。

參考文件:
1.監聽三個階段
2.移動Web滾動效能優化: Passive event listeners

相關文章