谈谈关于JavaScript 中的 MVC 模式


Posted in Javascript onApril 11, 2013

原文:Model-View-Controller (MVC) with JavaScript
作者:Alex@Net
译文:JavaScript 的 MVC 模式
译者:justjavac

本文介绍了模型-视图-控制器模式在 JavaScript 中的实现。

我喜欢 JavaScript,因为它是在世界上最灵活的语言之一。 在 JavaScript 中,程序员可以根据自己的口味选择编程风格:面向过程或面向对象。 如果你是一个重口味,JavaScript 一样可以应付自如: 面向过程,面向对象,面向方面, 使用 JavaScript 开发人员甚至可以使用函数式编程技术。

这篇文章中,我的目标是编写一个简单的 JavaScript 组件,来向大家展示一下 JavaScript 的强大。 该组件是一个可编辑的项目列表(HTML中的 select 标签):用户可以选择某一项并删除它,或者添加新的项目到列表中。 组件将由三个类构成,分别对应着 MVC 设计模式的模型-视图-控制器。

这篇文章只是一个简单的指导。 如果你希望在实际的项目中使用它,你需要进行适当的修改。 我相信你拥有创建和运行 JavaScript 程序的一切:大脑,双手,文本编辑器(如记事本),浏览器(例如我的最爱 Chrome)。

既然我们的代码要使用 MVC 模式,因此我在这里简单介绍一个这个设计模式。 MVC 模式的英文名称是 Model-View-Controller pattern,顾名思义,其主要部分组成:

•模型Model(),用于存储程序中使用到的数据;
•视图(View),用不同的表现形式来呈现数据;
•控制器(Controller),更新模型。
在维基百科对 MVC 体系结构的定义中,它由如下三部分组成:

模型(Model) -“数据模型”(Model)用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。 “模型”有对数据直接访问的权力。 “模型”不依赖“视图”和“控制器”,也就是说,模型不关心它会被如何显示或是如何被操作。

视图(View) - 视图层能够实现数据有目的的显示,通常是一个用户界面元素。 在视图中一般没有程序上的逻辑。 在 Web 应用程序中的 MVC,通常把显示动态数据的 html 页面称为视图。

控制器(Controller) - 处理和响应事件,通常是用户操作,并监控模型上的变化,然后去修改视图。

The data of the component is a list of items, in which one particular item can be selected and deleted. So, the model of the component is very simple - it is stored in an array property and selected item property; and here it is:

我们将基于 MVC 实现一个数据列表组件,列表中的项目可以被选择和删除。 因此,组件模型是非常简单的 - 它只需要两个属性:

1.数组 _items 用来存储所有元素
2.普通变量 _selectedIndex 用来存储选定的元素索引
代码如下:

/**
 * 模型。
 *
 * 模型存储所有元素,并在状态变更时通知观察者(Observer)。
 */ 
function ListModel(items) {
    this._items = items;        // 所有元素
    this._selectedIndex = -1;   // 被选择元素的索引
    this.itemAdded = new Event(this);
    this.itemRemoved = new Event(this);
    this.selectedIndexChanged = new Event(this);
}
ListModel.prototype = {
    getItems : function () {
        return [].concat(this._items);
    },
    addItem : function (item) {
        this._items.push(item);
        this.itemAdded.notify({item : item});
    },
    removeItemAt : function (index) {
        var item;
        item = this._items[index];
        this._items.splice(index, 1);
        this.itemRemoved.notify({item : item});
        if (index === this._selectedIndex) {
            this.setSelectedIndex(-1);
        }
    },
    getSelectedIndex : function () {
        return this._selectedIndex;
    },
    setSelectedIndex : function (index) {
        var previousIndex;
        previousIndex = this._selectedIndex;
        this._selectedIndex = index;
        this.selectedIndexChanged.notify({previous : previousIndex});
    }
};

Event 是一个简单的实现了观察者模式(Observer pattern)的类:

function Event(sender) {
    this._sender = sender;
    this._listeners = [];
}
Event.prototype = {
    attach : function (listener) {
        this._listeners.push(listener);
    },
    notify : function (args) {
        var index;
        for (index = 0; index < this._listeners.length; index += 1) {
            this._listeners[index](this._sender, args);
        }
    }
};

View 类需要定义控制器类,以便与它交互。 虽然这个任务可以有许多不同的接口(interface),但我更喜欢最简单的。 我希望我的项目是在一个 ListBox 控件和它下面的两个按钮:“加号”按钮添加项目,“减”删除所选项目。 组件所提供的“选择”功能则需要 select 控件的原生功能的支持。

一个 View 类被绑定在一个 Controller 类上, 其中「…控制器处理用户输入事件,通常是通过一个已注册的回调函数」(wikipedia.org)。

下面是 View 和 Controller 类:

/**
 * 视图。
 * 
 * 视图显示模型数据,并触发 UI 事件。
 * 控制器用来处理这些用户交互事件
 */ 
function ListView(model, elements) {
    this._model = model;
    this._elements = elements;
    this.listModified = new Event(this);
    this.addButtonClicked = new Event(this);
    this.delButtonClicked = new Event(this);
    var _this = this;
    // 绑定模型监听器
    this._model.itemAdded.attach(function () {
        _this.rebuildList();
    });
    this._model.itemRemoved.attach(function () {
        _this.rebuildList();
    });
    // 将监听器绑定到 HTML 控件上
    this._elements.list.change(function (e) {
        _this.listModified.notify({ index : e.target.selectedIndex });
    });
    this._elements.addButton.click(function () {
        _this.addButtonClicked.notify();
    });
    this._elements.delButton.click(function () {
        _this.delButtonClicked.notify();
    });
}
ListView.prototype = {
    show : function () {
        this.rebuildList();
    },
    rebuildList : function () {
        var list, items, key;
        list = this._elements.list;
        list.html('');
        items = this._model.getItems();
        for (key in items) {
            if (items.hasOwnProperty(key)) {
                list.append($('<option>' + items[key] + '</option>'));
            }
        }
        this._model.setSelectedIndex(-1);
    }
};
/**
 * 控制器。
 *
 * 控制器响应用户操作,调用模型上的变化函数。
 */ 
function ListController(model, view) {
    this._model = model;
    this._view = view;
    var _this = this;
    this._view.listModified.attach(function (sender, args) {
        _this.updateSelected(args.index);
    });
    this._view.addButtonClicked.attach(function () {
        _this.addItem();
    });
    this._view.delButtonClicked.attach(function () {
        _this.delItem();
    });
}
ListController.prototype = {
    addItem : function () {
        var item = window.prompt('Add item:', '');
        if (item) {
            this._model.addItem(item);
        }
    },
    delItem : function () {
        var index;
        index = this._model.getSelectedIndex();
        if (index !== -1) {
            this._model.removeItemAt(this._model.getSelectedIndex());
        }
    },
    updateSelected : function (index) {
        this._model.setSelectedIndex(index);
    }
};

当然,Model, View, Controller 类应当被实例化。

下面是一个使用此 MVC 的完整代码:

$(function () {
    var model = new ListModel(['PHP', 'JavaScript']),
    view = new ListView(model, {
        'list' : $('#list'), 
        'addButton' : $('#plusBtn'), 
        'delButton' : $('#minusBtn')
    }),
    controller = new ListController(model, view);        
    view.show();
});
<select id="list" size="10" style="width: 15em"></select><br/>
<button id="plusBtn">  +  </button>
<button id="minusBtn">  -  </button>

 

 

 

 

 

Javascript 相关文章推荐
JavaScript的面向对象方法以及差别
Mar 31 Javascript
flash javascript之间的通讯方法小结
Dec 20 Javascript
Jquery easyui 实现动态树
Nov 17 Javascript
跟我学习javascript的this关键字
May 28 Javascript
javascript制作照片墙及制作过程中出现的问题
Apr 04 Javascript
JS表格组件神器bootstrap table详解(强化版)
May 26 Javascript
jQuery中JSONP的两种实现方式详解
Sep 26 Javascript
javascript加载xml 并解析各节点的值(实现方法)
Oct 12 Javascript
常用jQuery选择器汇总
Feb 02 Javascript
Node 升级到最新稳定版的方法分享
May 17 Javascript
vue从零实现一个消息通知组件的方法详解
Mar 16 Javascript
使用Vue3+Vant组件实现App搜索历史记录功能(示例代码)
Jun 09 Vue.js
在JavaScript并非所有的一切都是对象
Apr 11 #Javascript
在JavaScript中typeof的用途介绍
Apr 11 #Javascript
浅谈关于JavaScript的语言特性分析
Apr 11 #Javascript
javascript中的delete使用详解
Apr 11 #Javascript
将字符串转换成gb2312或者utf-8编码的参数(js版)
Apr 10 #Javascript
原生js实现给指定元素的后面追加内容
Apr 10 #Javascript
图片无缝滚动代码(向左/向下/向上)
Apr 10 #Javascript
You might like
水质对咖图啡风味的影响具体有哪些
2021/03/03 冲泡冲煮
PHP简介
2006/10/09 PHP
php MySQL与分页效率
2008/06/04 PHP
javascript Zifa FormValid 0.1表单验证 代码打包下载
2007/06/08 Javascript
js 判断 enter 事件
2009/02/12 Javascript
javascript xml为数据源的下拉框控件
2009/07/07 Javascript
jQuery中[attribute=value]选择器用法实例
2014/12/31 Javascript
JavaScript中诡异的delete操作符
2015/03/12 Javascript
如何利用JSHint减少JavaScript的错误
2016/08/23 Javascript
BootStrap3使用错误记录及解决办法
2016/12/22 Javascript
JavaScript实现简单的星星评分效果
2017/05/18 Javascript
vue-cli项目无法用本机IP访问的解决方法
2018/09/20 Javascript
js限制input只能输入有效的数字(第一个不能是小数点)
2018/09/28 Javascript
javascript中一些奇葩的日期换算方法总结
2018/11/14 Javascript
vue.js的vue-cli脚手架中使用百度地图API的实例
2019/01/21 Javascript
H5+css3+js搭建带验证码的登录页面
2020/10/11 Javascript
[01:27]DOTA2电竞之夜 今夜共饮庆功酒
2014/08/02 DOTA
[02:04]2018DOTA2亚洲邀请赛Secret赛前采访
2018/04/03 DOTA
[56:46]2018DOTA2亚洲邀请赛 3.31 小组赛 B组 VP vs Effect
2018/04/01 DOTA
Python实现Youku视频批量下载功能
2017/03/14 Python
python win32 简单操作方法
2017/05/25 Python
使用Turtle画正螺旋线的方法
2017/09/22 Python
Python多线程原理与用法实例剖析
2019/01/22 Python
将string类型的数据类型转换为spark rdd时报错的解决方法
2019/02/18 Python
如何在windows下安装Pycham2020软件(方法步骤详解)
2020/05/03 Python
CSS3地图动态实例代码(圆圈向外扩散)
2018/06/15 HTML / CSS
HTML5在IE10、火狐下中文乱码问题的解决方法
2013/11/18 HTML / CSS
Html5移动端适配IphoneX等机型的方法
2019/06/25 HTML / CSS
健身场所或家用健身设备:Life Fitness
2017/11/01 全球购物
Tirendo比利时:在线购买轮胎
2018/10/22 全球购物
下列程序在32位linux或unix中的结果是什么
2014/03/25 面试题
写给女生的道歉信
2014/01/08 职场文书
关于抽烟的检讨书
2014/02/25 职场文书
社区文艺活动方案
2014/08/19 职场文书
实体类或对象序列化时,忽略为空属性的操作
2021/06/30 Java/Android
「女孩的钓鱼慢活」全新版权绘公布
2022/03/21 日漫