Object.defineProperty屬性實現雙向綁定

什麼是雙向綁定?html

1.當一個對象(或變量)的屬性改變,那麼調用這個屬性的地方顯示也應該改變,模型到視圖(model => view)vue

2.當調用屬性的這個地方改變了這個屬性(一般是一個表單元素),那麼這個對象(或變量)的屬性也會改成最新的值 ,即視圖到模型(view => model)數組

那麼vue利用es5的defineProperty特性實現雙向綁定的原理是什麼呢?ide

例如:函數

var person= {};
Object.defineProperty(person, "name", {
  value: '張三'
})
console.log(person.name); // 張三

傳參this

  • 第一個參數:要設置的目標對象(必填)es5

  • 第二個參數:須要定義的屬性或方法的名稱(必填)spa

  • 第三個參數:目標屬性所擁有的特性。(descriptor)(必填)prototype

      三個參數都是必填項,重點介紹第三個參數 descriptor翻譯

descriptor

  • value:屬性的值

  • writable:若是爲false,屬性的值就不能被重寫, 只能爲只讀了

  • configurable:總開關,一旦爲false,就不能再設置他的(value,writable,configurable)

  • enumerable:是否可枚舉(是否能在for...in循環中遍歷出來或在Object.keys中列舉出來)

  • get:後面會重點講解

  • set:後面會重點講解

 

descriptor 默認值

var person = {};
Object.defineProperty(person ,"name",{
  value: '張三',
  writable :false,
  enumerable: false,
  configurable: false
});
console.log(person.name); // 張三

這裏須要注意的是!!!!

configurable

總開關,第一次設置 false 以後,,第二次什麼設置也不行了。

writable

若是設置爲fasle,就變成只讀了。

enumerable

屬性特性 enumerable 定義了對象的屬性是否能夠在 for...in 循環和 Object.keys() 中被枚舉。

set 和 get

在 descriptor 中不能同時設置訪問器(get 和 set)和 wriable 或 value,不然會錯,就是說想用 get 和 set,就不能用 writable 或 value 中的任何一個。

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);

每當我獲取user.name屬性時,get方法被調用,get 方法對應的函數被執行,輸出 你是否是來獲取值啦;每當我設置user.name屬性時,set方法對應的函數被執行,輸出 你是否是來設置值啦 ; 是的,咱們監控到了代碼對user.name屬性的存取。

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

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

<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>

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

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

答:能夠!! id="model" 的input元素的 value 是user.name的值,填充在這個文本框裏面,文本框有個「 keyup」 事件,當咱們在文本框中輸入文字的時候,文本框的值會跟着改變,而且會連續觸發keyup事件,那麼咱們只須要監聽這個事件,是否是就能夠捕捉到view對值的更改了??既然文本框的值會跟着改變,咱們獲取最新的值再把新值更新到user.name屬性,不就實現了視圖到模型(view => model)的同步

<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>

【最終源碼】

在上述代碼的基礎上,加入了 用戶輸入中文的判斷(用戶輸入中文時,頻繁觸發 keyup事件,但實際上輸入並無結束。)

<!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.definePropety特性就完成了雙向綁定。

下面咱們再來看一下object.definePropety的用法及其餘屬性

Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。

 

語法

 

Object.defineProperty(obj, prop, descriptor)

 

參數

 

obj      要在其上定義屬性的對象。
prop      要定義或修改的屬性的名稱。
descriptor  將被定義或修改的屬性描述符。

 

返回值

 

    被傳遞給函數的對象。

描述

 該方法容許精確添加或修改對象的屬性。經過賦值操做添加的普通屬性是可枚舉的,可以在屬性枚舉期間呈現出來(for...in 或 Object.keys 方法), 這些屬性的值能夠被改變,也能夠被刪除。這個方法容許修改默認的額外選項(或配置)。默認狀況下,使用 Object.defineProperty() 添加的屬性值是不可修改的。

屬性描述符

 對象裏目前存在的屬性描述符有兩種主要形式:數據描述符存取描述符數據描述符是一個具備值的屬性,該值多是可寫的,也可能不是可寫的。存取描述符是由getter-setter函數對描述的屬性。描述符必須是這兩種形式之一;不能同時是二者。

數據描述符和存取描述符均具備如下可選鍵值:

configurable
當且僅當該屬性的 configurable 爲 true 時,該屬性描述符纔可以被改變,同時該屬性也能從對應的對象上被刪除。默認爲 false。
enumerable
當且僅當該屬性的enumerabletrue時,該屬性纔可以出如今對象的枚舉屬性中。默認爲 false。

數據描述符同時具備如下可選鍵值:

value
該屬性對應的值。能夠是任何有效的 JavaScript 值(數值,對象,函數等)。默認爲 undefined
writable
當且僅當該屬性的writabletrue時,value才能被賦值運算符改變。默認爲 false

存取描述符同時具備如下可選鍵值

get
一個給屬性提供 getter 的方法,若是沒有 getter 則爲 undefined。當訪問該屬性時,該方法會被執行,方法執行時沒有參數傳入,可是會傳入this對象(因爲繼承關係,這裏的this並不必定是定義該屬性的對象)。
默認爲 undefined
set
一個給屬性提供 setter 的方法,若是沒有 setter 則爲 undefined。當屬性值修改時,觸發執行該方法。該方法將接受惟一參數,即該屬性新的參數值。
默認爲 undefined
若是一個描述符不具備value,writable,get 和 set 任意一個關鍵字,那麼它將被認爲是一個數據描述符。若是一個描述符同時有(value或writable)和(get或set)關鍵字,將會產生一個異常。

記住,這些選項不必定是自身屬性,若是是繼承來的也要考慮。爲了確認保留這些默認值,你可能要在這以前凍結 Object.prototype,明確指定全部的選項,或者經過 Object.create(null)__proto__屬性指向null

相關文章
相關標籤/搜索