使用原生JavaScript实现数据绑定
发布在每天学点javascript2015年8月7日view:17331MVVM
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

使用原生JavaScript实现数据绑定

一直以来,双向数据绑定都是一个非常有用的特性,它能够让你的JS模型和HTML视图保持一致,这样可以大大减少代码量并增强视图功能。在本文中,我们将使用原生JavaScript,而无需使用任何框架,以两种方式来实现双向数据绑定 – 一种方式是使用新技术(Object.observe),另一种是使用目前已有的概念(重载 get/set)。

1:Object.observe && DOM.onchange

对于绝大多数JavaScript API来所,Object.observe() 绝对算得上是镇上新来的姑娘。虽然这个API是ES7中的一部分,但是令人激动的是它已经在Chrome的稳定版本总实现了。它能够允许我们以更具交互性的形式‘来更新一个JS对象的变化。或者说得通俗一些 – 当一个对象(或者它的属性)发生变化时,都会自动触发一个回调函数。

下面是一个最简单的例子:

var log = console.log;
user = {};
Object.observe(user,function(changes){
   changes.forEach(function(change){
     log(change.name);
     log(change.type);
   });
});
user.firstname = '张小俊128';
//张小俊123
//add

使用起来非常简单直观,下面是一个稍微复杂一点的例子:

//<input id="foo">
user = {};
div = $("#foo");
Object.observe(user, function(changes){    
    changes.forEach(function(change) {
        var fullName = (user.firstName || "") + " " + (user.lastName || "");         
        div.text(fullName);
    });
});

user.firstName = 'Bill';
user.lastName = 'Clinton';

div.text() //Bill Clinton

太棒了,到这里我们已经实现了模型到视图的数据绑定。现在我们来把复杂的部分抽象成一个辅助函数:

//<input id="foo">
function bindObjPropToDomElem(obj, property, domElem) { 
  Object.observe(obj, function(changes){    
    changes.forEach(function(change) {
      $(domElem).text(obj[property]);        
    });
  });  
}

user = {};
bindObjPropToDomElem(user,'name',$("#foo"));
user.name = 'William'
$("#foo").text() //'William'

现在我们还需要来实现另一个方向的数据绑定 – 从DOM元素到一个JS值的绑定。一个最简单的途径就是使用jQuery中的.change

//<input id="foo">
$("#foo").val("");
function bindDomElemToObjProp(domElem, obj, propertyName) {  
  $(domElem).change(function() {
    obj[propertyName] = $(domElem).val();
    alert("user.name is now "+user.name);
  });
}

user = {}
bindDomElemToObjProp($("#foo"), user, 'name');
//在input中输入 'obama'
user.name //Obama. 

将上面两个步骤结合起来,你就创建出一个用来实现双向数据绑定了的应用了:

function bindObjPropToDomElem(obj, property, domElem) { 
  Object.observe(obj, function(changes){    
    changes.forEach(function(change) {
      $(domElem).text(obj[property]);        
    });
  });  
}

function bindDomElemToObjProp(obj, propertyName, domElem) {  
  $(domElem).change(function() {
    obj[propertyName] = $(domElem).val();
    console.log("obj is", obj);
  });
}

function bindModelView(obj, property, domElem) {  
  bindObjPropToDomElem(obj, property, domElem)
  bindDomElemToObjProp(obj, propertyName, domElem)
}

需要注意的是,在实现双向绑定时,不同的DOM元素(input,div,textarea,select)中值的引用方式不同(text,val)。

2:改变getset

除了上面提到的使用Object.observe之外,我们还可以使用对象的geteer和setter来实现双向数据绑定。相比Object.observe来说,浏览器目前对于getter和setter的支持程度要好得多。在这里,我们需要使用Object.defineProperty方法。例如下面的例子:

user = {}
nameValue = 'Joe';
Object.defineProperty(user, 'name', {
  get: function() { return nameValue }, 
  set: function(newValue) { nameValue = newValue; },
  configurable: true // 允许在稍后重定义这个属性 
  });

user.name //Joe 
user.name = 'Bob'
user.name //Bob
nameValue //Bob

在上面的例子中,我们可以将user.name看做是nameValue的一个别名。但是我们可以做到更多,比如说将数据在模型和视图之间进行绑定。例如:

//<input id="foo">
Object.defineProperty(user, 'name', {
  get: function() { return document.getElementById("foo").value }, 
  set: function(newValue) { document.getElementById("foo").value = newValue; },
  configurable: true // 让我们可以在之后重新定义这个属性
    });

现在,user.name绑定到了input元素#foo。 总结一下,我们可以像这样来使用set/get方法来实现数据双向绑定:

function bindModelInput(obj, property, domElem) {
  Object.defineProperty(obj, property, {
    get: function() { return domElem.value; }, 
    set: function(newValue) { domElem.value = newValue; },
    configurable: true
  });
}
user = {};
inputElem = document.getElementById("foo");
bindModelInput(user,'name',inputElem);

user.name = "Joe";
alert("input value is now "+inputElem.value) //input元素现在的值是'Joe'
inputElem.value = 'Bob';
alert("user.name is now "+user.name) //现在model中的value是Bob

需要注意的一点是上面的用法并不是真正的绑定,例如有时在一个input发生变化时,将变化反映到另一个绑定到同一个model的input上。这个时候,我们就需要添加一个新的自定义’接触器’。如下面的例子所示:

//<input id='input1'>
//<input id='input2'>
input1 = document.getElementById('input1')
input2 = document.getElementById('input2')
user = {}
Object.defineProperty(user, 'name', {
  get: function() { return input1.value; }, 
  set: function(newValue) { input1.value = newValue; input2.value = newValue; },
  configurable: true
});
input1.onchange = function() { user.name = user.name } // 同步两个input

总结

上面两种方法都能够实现数据双向绑定,但是目前来说,使用set/get是更好的方式,因为浏览器对set/get的支持度要更好。


本文参考自Native JavaScript Data-Binding,原文地址http://www.sellarafaeli.com/blog/native_javascript_data_binding

评论
发表评论
暂无评论
WRITTEN BY
张小俊128
Intern in Baidu mobile search department。认真工作,努力钻研,期待未来更多可能。
TA的新浪微博
PUBLISHED IN
每天学点javascript

javascript进阶级教程,循序渐进掌握javascript

友情链接 大搜车前端团队博客
我的收藏