javascript ORM
前端ORM框架其实也就是一个对于DAO数据访问接口的封装,主要是封装CRUD四种类型基本操作。
所谓对象关系映射的构建,最基本的还是在于模型这一层,也就是数据模型,我们应该用对象来封装我们的数据,以形成模型。
例如一个基本的数据结构为:
1 var data = { 2 name: 'ken', 3 age: 18 4 };
我们需要将这样一个数据转化为一个对象,可以将之作为一个参数传递到一个具有某些功能的对象中去,例如:
1 function Model(data) { 2 this.data = data; 3 } 4 Model.prototype.save = function() { 5 //do create or update 6 }; 7 Model.prototype.read = function() { 8 9 }; 10 Model.prototype.delete = function() { 11 12 }; 13 Model.prototype.get = function(attr) { 14 return this.data[attr]; 15 };
上面的代码定义了一个基本的数据模型,这个能够完成与服务器之间的数据交互,而数据最终还是需要展现在页面上的,这就需要另外一个对于数据和视图之间的处理了。数据变化之后能够通知页面进行重新渲染,我们可以使用比较常用的一种设计模式,也就是发布/订阅模式来完成这种事件的交互。在数据层发布事件,在视图层去监听事件。我们可以在网上找到很多实现了该功能的代码,其原理也很简单,只需要维护一个用于保存事件名和相应事件的Map表即可,下面是一个实现代码:
1 var Events = Spine.Events = { 2 bind: function(ev, callback) { 3 var evs = ev.split(" "); 4 var calls = this._callbacks || (this._callbacks = {}); 5 6 for (var i=0; i < evs.length; i++) 7 (this._callbacks[evs[i]] || (this._callbacks[evs[i]] = [])).push(callback); 8 9 return this; 10 }, 11 12 trigger: function() { 13 var args = makeArray(arguments); 14 var ev = args.shift(); 15 16 var list, calls, i, l; 17 if (!(calls = this._callbacks)) return this; 18 if (!(list = this._callbacks[ev])) return this; 19 20 for (i = 0, l = list.length; i < l; i++) 21 if (list[i].apply(this, args) === false) 22 return false; 23 return this; 24 }, 25 26 unbind: function(ev, callback){ 27 if ( !ev ) { 28 this._callbacks = {}; 29 return this; 30 } 31 32 var list, calls, i, l; 33 if (!(calls = this._callbacks)) return this; 34 35 if (!(list = this._callbacks[ev])) return this; 36 37 if (!callback) { 38 delete this._callbacks[ev]; 39 } 40 41 for (i = 0, l = list.length; i < l; i++) { 42 if (callback === list[i]) { 43 list.splice(i, 1); 44 break; 45 } 46 } 47 48 return this; 49 } 50 };
我们可以给任何需要用到发布订阅模式的对象继承该Event对象,然后我们便可以很方便的发布/订阅事件了。
当然,我们的对象默认情况下并没有一个可直接使用的继承方法,但是要实现基层并不难,我们可以给我们的Model对象添加下面这两个方法:
1 Model.include = function(obj){ 2 for(var key in obj) { 3 this.prototype[key] = obj[key]; 4 } 5 6 var included = obj.included; 7 if (included) included.apply(this); 8 return this; 9 }, 10 11 Model.extend = function(obj){ 12 for(var key in obj) { 13 this[key] = obj[key]; 14 } 15 16 var extended = obj.extended; 17 if (extended) extended.apply(this); 18 return this; 19 }
有了上面的两个方法之后,我们便可以通过include方法来扩展实例方法,通过extend来扩展静态方法,对于Event,我们可以用extend来进行扩展:
1 Model.extend(Event);
扩展完成之后,我们便可以在我们的代码中添加事件的发布或者订阅了,在模型中有关于数据保存或者更新的地方我们可以发布change事件,在数据销毁的时候我们可以发布destroy事件:
1 Model.prototype.save = function() { 2 //todo create or update 3 this.trigger('change'); 4 }; 5 Model.prototype.delete = function() { 6 //todo delete 7 this.trigger('destroy'); 8 }
在页面中,我们会根据得到的数据来生成对应的模型,因为我们的模型已经扩展了发布订阅模式,因此在代码中,我们给我们的模型添加订阅事件的代码:
1 //子视图 2 function View(data) { 3 var model = new Model(data); 4 model.bind('change', this.render); 5 } 6 View.prototype.render = function() { 7 //todo view render 8 };
上面便是前端ORM的一个基础框架模型,也是核心点所在,不过要完成整个框架,仍然需要添加很多的东西。
上面的代码仅仅是能实现唯一的模型实例,而不能对模型实例集合进行操作,什么意思呢,比如你的数据是一个数组,
里面包含了多条数据,没一条数据都能转换为一个对象的模型对象。在现在的代码中,我们只完成了一条数据的转换而没有完成整个数据数据的转换,因此,需要做出一些调整;
在服务器端获取回来的数据,我们应该将之转换为一个模型,数据集里面的每一个数据记录,就用来转换成为该模型的每一个实例,按照这个思路,我们的代码使用起来可能像下面这个样子:
1 var User = Model.create(); 2 var user = User.init();
模型的创建,也就是User的创建,我们是不需要传递数据的,只需要传递我们需要的一些数据字段即可,这些字段也就是模型实例的一些内置属性;根据User来生成的实例都会具有User所具有的一些信息,在这里就是字段属性。
同时Model还应该具有一些静态方法用来查找实例,修改实例等,因此该Model还要有一个用于保存模型实例的容器,也就是一个数组或者对象。
1 Model.extend({ 2 records: {}, //保存实例 3 find: function(id) { 4 5 }, 6 exists: function(id) { 7 8 }, 9 ... 10 });
至此,一个大致的ORM构建思路就算是完成了,具体的代码还需要不断的添加完成。