您的位置:首页 > Web前端 > JavaScript

五十行javascript代码实现简单的双向数据绑定

2017-04-25 09:33 841 查看


五十行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。

这里只说一下 
get
 和 
set


看一下这个例子:

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', function () {
var property = this.getAttribute('v-model');
var value = this.value;
self.vData[property] = value;
})
}
};

var vm = new Vue({
data: {
name: 1
}
})
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: