五十行javascript代碼實現簡單的雙向數據綁定


五十行javascript代碼實現簡單的雙向數據綁定

Vue框架想必從事前端開發的同學都使用過,它的雙向數據綁定機制能給我們帶來很大的方便。今天閑着沒事,嘗試着實現一下雙向數據綁定,接下來給大家分享一下。

Object.defineProperty(obj, prop, descriptor)

Object.defineProperty 方法允許精確添加或修改對象的屬性。它的第一個參數 obj 是要在其上定義屬性的對象,第二個參數 prop 是要定義或修改的屬性的名稱,第三個參數 descriptor 是一個將被定義或修改的屬性的描述符。

返回值: 被傳遞給函數的對象。

來舉個例子:

var o = Object.defineProperty({}, 'name', {
	value: 1
});

console.log(o) // {name: 1}

這是最基本的定義一個對象的方式。對於屬性描述符,還有很多其他屬性:

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

  • configurable當且僅當該屬性的 configurable 為 true 時,該屬性描述符才能夠被改變,也能夠被刪除。默認為 false。
  • enumerable當且僅當該屬性的 enumerable 為 true 時,該屬性才能夠出現在對象的枚舉屬性中。默認為 false。

數據描述符同時具有以下可選鍵值:

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

存取描述符同時具有以下可選鍵值:

  • get一個給屬性提供 getter 的方法,如果沒有 getter 則為 undefined。該方法返回值被用作屬性值。默認為 undefined。
  • set一個給屬性提供 setter 的方法,如果沒有 setter 則為 undefined。該方法將接受唯一參數,並將該參數的新值分配給屬性。默認為 undefined。

這里只說一下 getset
看一下這個例子:

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 定義了一個 getset ,為什么使用 _name_而不是name呢?因為name是正在被定義的屬性,如果在get或者set中使用name無形之中就會發生遞歸,導致棧溢出。_name_這個是自己自定義的,你完全可以設置為$name__name__等等。

另外,使用對象的字面量形式也可以設置getset

var o = {
    get name(){
        return this._name_;
    },
    set name(value){
        this._name_ = value * 2;
    }
};

o.name = 1;
console.log(o.name);  // 2

實現雙向數據綁定

要實現雙向數據綁定,肯定要從 getset 下手,在 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', function () {
            var property = this.getAttribute('v-model');
            var value = this.value;
            self.vData[property] = value;
        })
    }
};

var vm = new Vue({
    data: {
        name: 1
    }
})


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM