在Angular或Vue中,視圖(view)和模型(model)之間是能夠實現雙向數據綁定的,這也是Angular和Vue的一大特點。就是說,當用戶修改視圖時,和視圖相關的那個變量值也會發生改變;當那個變量值自己發生變化時,視圖也會相應作出改變。
JavaScript中的對象(Object)是由一個個鍵值對(key-value)組成的,類似於Ruby中的哈希表或者Python中的字典。這些鍵值對在JavaScript的語境下被稱作屬性(property),其中鍵(key)被稱作屬性名(property name),值(value)被稱作屬性值(property value)。
在這裏講這些是爲了引入一個函數,它叫做Object.defineProperty()。顧名思義,它的功能就是給某個JS對象定義屬性。因此它有三個參數:
- 給哪個對象定義屬性
- 屬性名
- 屬性值
前兩個參數沒什麼可說的,第一個是個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就全都綁定在一起了。