Vue框架想必從事前端開發的同窗都使用過,它的雙向數據綁定機制能給咱們帶來很大的方便。今天閒着沒事,嘗試着實現一下雙向數據綁定,接下來給你們分享一下。javascript
Object.defineProperty
方法容許精確添加或修改對象的屬性。它的第一個參數 obj
是要在其上定義屬性的對象,第二個參數 prop
是要定義或修改的屬性的名稱,第三個參數 descriptor
是一個將被定義或修改的屬性的描述符。html
返回值: 被傳遞給函數的對象。前端
來舉個例子:java
var o = Object.defineProperty({}, 'name', { value: 1 }); console.log(o) // {name: 1}
這是最基本的定義一個對象的方式。對於屬性描述符,還有不少其餘屬性:git
數據描述符和存取描述符均具備如下可選鍵值:github
configurable
當且僅當該屬性的 configurable 爲 true 時,該屬性描述符纔可以被改變,也可以被刪除。默認爲 false。enumerable
當且僅當該屬性的 enumerable 爲 true 時,該屬性纔可以出如今對象的枚舉屬性中。默認爲 false。數據描述符同時具備如下可選鍵值:api
value
該屬性對應的值。能夠是任何有效的 JavaScript 值(數值,對象,函數等)。默認爲 undefined。writable
當且僅當該屬性的 writable 爲 true 時,該屬性才能被賦值運算符改變。默認爲 false。存取描述符同時具備如下可選鍵值:瀏覽器
get
一個給屬性提供 getter 的方法,若是沒有 getter 則爲 undefined。該方法返回值被用做屬性值。默認爲 undefined。set
一個給屬性提供 setter 的方法,若是沒有 setter 則爲 undefined。該方法將接受惟一參數,並將該參數的新值分配給屬性。默認爲 undefined。
這裏只說一下 get
和 set
:
看一下這個例子:markdown
var o = Object.defineProperty({}, 'name', { get: function () { return this._name_; }, set: function (value) { this._name_ = value * 2; } }); o.name = 1; console.log(o.name); // 2
給屬性 name
定義了一個 get
和 set
,爲何使用 _name_
而不是name
呢?由於name
是正在被定義的屬性,若是在get
或者set
中使用name
無形之中就會發生遞歸,致使棧溢出。_name_
這個是本身自定義的,你徹底能夠設置爲$name
、__name__
等等。框架
另外,使用對象的字面量形式也能夠設置get
與set
:
var o = { get name(){ return this._name_; }, set name(value){ this._name_ = value * 2; } }; o.name = 1; console.log(o.name); // 2
要實現雙向數據綁定,確定要從 get
與 set
下手,在 set
的函數中從新渲染相關的數據,因此一開始應該是這樣的:
var o = { get name(){ return this._name_; }, set name(value){ this._name_ = value; this.render('name'); }, render: function(pro){ document.write(this[pro]); } };
在瀏覽器控制檯修改一下o.name
試試:
如何實現表單控件到數據的綁定呢?在 Vue
中,表單元素經過 v-model
綁定一個變量,類型這樣:
<input type="text" v-model="name">
其實 v-model
的元素是綁定了一個 input
的自定義事件,咱們不考慮那麼多,就使用原生的 oninput
事件來模擬下。
var o = { get name(){ return this._name_; }, set name(value){ this._name_ = value; console.log(this._name_); }, inputInit: function () { var self = this; var vModels = document.querySelectorAll('[v-model]'); for (let i = 0; i < vModels.length; i++) { vModels[i].addEventListener('input', function () { var property = this.getAttribute('v-model'); var value = this.value; self.name = value; }) } } }.inputInit();
至此一個簡單的雙向數據綁定就差很少了,咱們模仿一下 Vue
的api格式,再將代碼封裝一下:
html:
<input type="text" v-model="name"> <p v-text="name"></p>
javascript:
function Vue(options) { var data = options.data || {}; var dKeys = Object.keys(data); var _this = this; this.vData = {}; // 根據data中的變量數量動態的綁定 get 與 set for (let i = 0; i < dKeys.length; i++) { Object.defineProperty(this.vData, dKeys[i], { get: function () { return this['__' + dKeys[i] + '__']; }, set: function (value) { this['__' + dKeys[i] + '__'] = value; _this.render(dKeys[i]); // 從新渲染相關數據 } }) } for(let i in data) { // 初始化時設置一變vData,觸發一遍 set this.vData[i] = data[i]; } this.inputInit(); // 給表單組件綁定事件監聽 } Vue.prototype.render = function (pro) { var vModels = document.querySelectorAll('[v-model=' + pro + ']'); var vText = document.querySelectorAll('[v-text=' + pro + ']'); for (var i = 0; i < vModels.length; i++) { vModels[i].value = this.vData[pro]; } for (var j = 0; j < vText.length; j++) { vText[j].innerText = this.vData[pro]; } }; Vue.prototype.inputInit = function () { var self = this; var vModels = document.querySelectorAll('[v-model]'); for (let i = 0; i < vModels.length; i++) { vModels[i].addEventListener('input',