1. 程式人生 > >Object.defineProperty實現雙向繫結原理

Object.defineProperty實現雙向繫結原理

什麼是雙向繫結?

1.當一個物件(或變數)的屬性改變,那麼呼叫這個屬性的地方顯示也應該改變,模型到檢視(model => view)

2.當呼叫屬性的這個地方改變了這個屬性(通常是一個表單元素),那麼這個物件(或變數)的屬性也會改為最新的值 ,即檢視到模型(view => model)

我們怎麼知道物件的屬性變了?

上文說到,Object.defineProperty 設定物件屬性的描述欄位裡面有兩個屬性 set (設定屬性時被呼叫)和get(獲取屬性時被呼叫),只說不練,你再講什麼?眼見為實好嗎?OK ,上程式碼

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

var user = {};

var defaultName = "狂奔的蝸牛";

Object.defineProperty(user,"name",{

get:function(){

console.log("你是不是來獲取值啦");

return defaultName;

},

set:function(value){

console.log("你是不是來設定值啦"

);

defaultName = value;

}

})

console.log(user.name);

user.name = "狂奔的蘿蔔";

console.log(user.name);

get和set存取時被呼叫

如上圖所示 每當我獲取user.name屬性時,get方法被呼叫,get 方法對應的函式被執行,輸出 你是不是來獲取值啦;每當我設定user.name屬性時,set方法對應的函式被執行,輸出 你是不是來設定值啦 ; 是的,我們監控到了程式碼對user.name屬性的存取。

說明 假設id="model" 的元素的 value 是user.name的值,既然我們可以在改變屬性的執行日誌輸出(console.log("你是不是來設定值啦");),那麼,我們在設定值的時候給id="model" 的元素設定下新值,不就實現了從模型到檢視?!!,說幹就幹

模型到檢視(model => view)的同步

說明 假設id="model" 的元素的 value 是user.name的值,既然我們可以在改變屬性的執行日誌輸出(console.log("你是不是來設定值啦");),那麼,我們在設定值的時候給id="model" 的元素設定下新值,不就實現了從模型到檢視?!!,說幹就幹

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

<body>

手寫一個簡單雙向繫結<br/>

<input type="text" id="model"><br/>

<div id="modelText"></div>

</body>

<script>

var user = {};

var defaultName = "狂奔的蝸牛";

document.querySelector("#model").value = defaultName;

document.querySelector("#modelText").textContent = defaultName;

//定義屬性 監控改變

Object.defineProperty(user,"name",{

get:function(){

console.log("你是不是來獲取值啦");

return defaultName;

},

set:function(newValue){

console.log("設定新值");

defaultName = newValue;

console.log("實現 模型 => 檢視");

document.querySelector("#model").value = newValue;

document.querySelector("#modelText").textContent = newValue;

}

})

console.log("2s 後改變值");

setTimeout(() => {

//改變值

user.name = "狂奔的蘿蔔";

}, 2000);

</script>

模型到檢視(model => view)的同步

檢視到模型(view => model)的同步

問: 我們能捕捉到view對值更改嗎?

答:可以!! id="model" 的input元素的 value 是user.name的值,填充在這個文字框裡面,文字框有個“ keyup” 事件,當我們在文字框中輸入文字的時候,文字框的值會跟著改變,並且會連續觸發keyup事件,那麼我們只需要監聽這個事件,是不是就可以捕捉到view對值的更改了??既然文字框的值會跟著改變,我們獲取最新的值再把新值更新到user.name屬性,不就實現了檢視到模型(view => model)的同步?沒程式碼說個啥

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

<body>

手寫一個簡單雙向繫結<br/>

<input type="text" id="model"><br/>

<div id="modelText"></div>

</body>

<script>

var user = {};

var defaultName = "狂奔的蝸牛";

var model = document.querySelector("#model");

var modelText = document.querySelector("#modelText");

model.value = defaultName;

modelText.textContent = defaultName;

//定義屬性 監控改變

Object.defineProperty(user,"name",{

get:function(){

console.log("你是不是來獲取值啦");

return defaultName;

},

set:function(newValue){

console.log("設定新值");

defaultName = newValue;

model.value = newValue;

modelText.textContent = newValue;

}

})

model.addEventListener("keyup", function () {

user.name = this.value;

console.log("實現 檢視 => 模型");

}, false)

</script>

view2model.gif

【最終原始碼】

在上述程式碼的基礎上,加入了 使用者輸入中文的判斷(使用者輸入中文時,頻繁觸發 keyup事件,但實際上輸入並沒有結束。)

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>雙向繫結</title>

</head>

<body>

手寫一個簡單雙向繫結<br/>

<input type="text" id="model"><br/>

<div id="modelText"></div>

</body>

<script>

var model = document.querySelector("#model");

var modelText = document.querySelector("#modelText");

var defaultName = "defaultName";

var userInfo = {}

model.value = defaultName;

Object.defineProperty(userInfo, "name", {

get: function () {

return defaultName;

},

set: function (value) {

defaultName = value;

model.value = value;

console.log("-----value");

console.log(value);

modelText.textContent = value;

}

})

userInfo.name = "new value";

var isEnd = true;

model.addEventListener("keyup", function () {

if (isEnd) {

userInfo.name = this.value;

}

}, false)

//加入監聽中文輸入事件

model.addEventListener("compositionstart", function () {

console.log("開始輸入中文");

isEnd = false;

})

model.addEventListener("compositionend", function () {

isEnd = true;

console.log("結束輸入中文");

})

</script>

</html>

【完結】

Object.defineProperty 可以做很多好玩兒的,自己慢慢探索哈~