JavaScript MVC 学习笔记(六)模型之ORM


MVC 和命名空间

要确保应用中的视图、状态和数据彼此清晰分离,才能让架构更加整洁有序且更加健壮。模型应当从视图和控制器中解耦出来。与数据操作和行为相关的逻辑都应当放入模型中,通过命名空间
进行管理。

在JavaScript 中,通过给对象添加属性来管理一个命名空间,这个命名空间可以是函数,也可以是变量,比如:

var User = {
    records: [ /* ... */ ]
};

User 的数组数据就在命名空间User.records 中。和user 相关的函数也可以放入User 模型的命名空间里。比如用fetchRemote() 函数来从服务器端获取user 的数据:

var User = {
    records: [],
    fetchRemote: function(){ /* ... */ }
};

将模型的属性保存至命名空间中的做法可以确保不会产生冲突,这也是符合MVC 原则的,同时也能避免代码变成一堆函数和回调混杂在一起的大杂烩。

可以对命名空间做一点改进,将那些在真实user 对象上的和user 实例相关的函数也都添加进去。假设user 记录包含一个destory() 函数,它是和具体的user 相关的,因此这个函数应当基于User 实例进行调用:

var user = new User;
user.destroy()

为了做到这一点,应当将User 写成一个类,而不是一个简单对象:

var User = function(atts){
    his.attributes = atts || {};
};

User.prototype.destroy = function(){
    /* ... */
};

对于那些和具体的user 不相关的函数和变量,则可以直接定义在User 对象中:

User.fetchRemote = function(){
    /* ... */
};

构建对象关系映射(ORM)

对象关系映射(Ojbect-relational mapper,简称ORM)是在除JavaScript 以外的编程语言中常见的一种数据结构。在JavaScript 应用中,对象关系映射也是一种非常有用的技术,它可以用来做数据管理及用做模型。比如使用ORM 可以将模型和远程服务捆绑在一起,任何模型实例的改变都会在后台发起一个Ajax 请求到服务器端。或者将模型实例和HTML 元素绑定在一起,任何对实例的更改都会在界面中反映出来。

现在让来创建一个自定义ORM。

本质上讲,ORM 是一个包装了一些数据的对象层。以往ORM 常用于抽象SQL 数据库,但在这里ORM 只是用于抽象JavaScript 数据类型。这个额外的层有一个好处,可以通过给它添加自定义的函数和属性来增强基础数据的功能。比如添加数据的合法性验证、监听、数据持久化及服务器端的回调处理等,这样会增加代码的重用率。

原型继承

这里使用Object.create() 来构造ORM,这里使用基于原型(prototype-based)的继承,而没有用到构造函数和new关键字。

Object.create() 只有一个参数即原型对象,它返回一个新对象,这个新对象的原型就是传入的参数。换句话说,传入一个对象,返回一个继承了这个对象的新对象。

对于不支持的Object.create()的浏览器 ,可以很容易地模拟出这个函数:

if (typeof Object.create !== "function")
    Object.create = function(o) {
        function F() {}
        F.prototype = o;
        return new F();
    };

现在来创建Model 对象,Model 对象将用于创建新模型和实例:

var Model = {
    inherited: function(){},
    created: function(){},
    prototype: {
        init: function(){}
    },

    create: function(){
        var object = Object.create(this);
        object.parent = this;
        object.prototype = object.fn = Object.create(this.prototype);
        object.created();
        this.inherited(object);
        return object;
    },

    init: function(){
        var instance = Object.create(this.prototype);
        instance.parent = this;
        instance.init.apply(instance, arguments);
        return instance;
    }
};

create() 函数返回一个新对象,这个对象继承自Model 对象,使用它来创建新模型。
init() 函数返回一个新对象,它继承自Model.prototype——如Model 对象的一个实例:

var Asset = Model.create();
var User = Model.create();
var user = User.init();

添加ORM 属性

现在如果给Model 对象添加属性,对于继承的模型来说,这些新增属性都是可访问的:

// 添加对象属性
jQuery.extend(Model, {
    find: function(){}
});

// 添加实例属性
jQuery.extend(Model.prototype, {
    init: function(atts) {
        if (atts) this.load(atts);
    },
    load: function(attributes){
        for(var name in attributes)
        this[name] = attributes[name];
    }
});

jQuery.extend() 只是代替for 循环手动复制属性的一种快捷方式,这和在load()
函数中的做法差不多。现在,对象和实例属性都传播到了单独的模型里:

assertEqual( typeof Asset.find, "function" );

实际上我们会增加很多属性,因此还需将extend() 和include() 添加至Model 对象中:

var Model = {
    /* ……代码片段……*/
    extend: function(o){
        var extended = o.extended;
        jQuery.extend(this, o);
        if (extended) extended(this);
    },

    include: function(o){
        var included = o.included;
        jQuery.extend(this.prototype, o);
        if (included) included(this);
    }
};

// 添加对象属性
Model.extend({
    find: function(){}
});

// 添加实例属性
Model.include({
    init: function(atts) { /* ... */ },
    load: function(attributes){ /* ... */ }
});

现在可以创建新的资源并设置一些属性:

var asset = Asset.init({name: "foo.png"});

【公开记录学习JS MVC,不知道能坚持多久= =。以《基于MVC的JavaScript web富应用开发》为主要学习资料。】


发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>