js與html DOM實現雙向數據綁定的原理:Object.defineProperty()

在Angular或Vue中,視圖(view)和模型(model)之間是能夠實現雙向數據綁定的,這也是Angular和Vue的一大特點。就是說,當用戶修改視圖時,和視圖相關的那個變量值也會發生改變;當那個變量值自己發生變化時,視圖也會相應作出改變。

JavaScript中的對象(Object)是由一個個鍵值對(key-value)組成的,類似於Ruby中的哈希表或者Python中的字典。這些鍵值對在JavaScript的語境下被稱作屬性(property),其中(key)被稱作屬性名(property name),(value)被稱作屬性值(property value)。

在這裏講這些是爲了引入一個函數,它叫做Object.defineProperty()。顧名思義,它的功能就是給某個JS對象定義屬性。因此它有三個參數:

  1. 給哪個對象定義屬性
  2. 屬性名
  3. 屬性值

前兩個參數沒什麼可說的,第一個是個JS對象,第二個是字符串。有趣的地方在於第三個參數。一般來講,屬性值可以是任何類型,但是這裏的第三個參數不僅僅可以設置屬性值本身,還能夠進行屬性被訪問(get)和被修改(set)時的監聽。比如如下的例子:

Object.defineProperty(a, "title", {
  set: () => {
    console.log("set!");
  },
  get: () => {
    console.log("get!");
  }
});

當a.title被修改時,如執行a.title = "xxx"這樣的語句,就會自動觸發set,控制檯會顯示"set!"的字樣;

當a.title被訪問時,如執行let s = a.title這樣的語句,就會自動觸發get,控制檯會顯示"get!"的字樣。

實際上,這第三個參數是個屬性描述符(property descriptor)。屬性描述符有兩種類型,分別是數據描述符和存取描述符,二者必須選其一且互有異同。這裏爲避免跑題不多加解釋,詳情可見:Object.defineProperty() - JavaScript | MDN

因此,實現雙向數據綁定就有了如下例子:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>defineProperty</title>
  </head>
  <body>
    <input type="text" id="textInput" />
    <p id="textDisplay"></p>
    <script>
      const textInput = document.getElementById("textInput");
      const textDisplay = document.getElementById("textDisplay");
      let a = {};

      Object.defineProperty(a, "title", {
        set: val => {
          textInput.value = val;
          textDisplay.innerText = val;
        }
      });

      textInput.onkeyup = e => {
        a.title = e.target.value;
      };
    </script>
  </body>
</html>

 這樣,輸入框的值、段落的值和a.title就全都綁定在一起了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章