【干货理解】理解javascript中实现MVC的原理

理解javascript中的MVC

MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller);

模型:模型用于封装与应用程序的业务逻辑相关的数据以及对数据处理的方法。模型有对数据直接访问的权利。模型不依赖 "视图" 和 "控制器", 也就是说 模型它不关心页面如何显示及如何被操作.

视图:视图层最主要的是监听模型层上的数据改变,并且实时的更新html页面。当然也包括一些事件的注册或者ajax请求操作(发布事件),都是放在视图层来完成。

控制器:控制器接收用户的操作,最主要是订阅视图层的事件,然后调用模型或视图去完成用户的操作;比如:当页面上触发一个事件,控制器不输出任何东西及对页面做任何处理; 它只是接收请求并决定调用模型中的那个方法去处理请求, 然后再确定调用那个视图中的方法来显示返回的数据。

下面我们来实现一个简单的下拉框控件,我们可以对它进行增删操作;如下图所示:

代码如下:

/*
 模型用于封装与应用程序的业务逻辑相关的数据以及对数据处理的方法。模型有对数据直接访问的权利。
 模型不依赖 "视图" 和 "控制器", 也就是说 模型它不关心页面如何显示及如何被操作.
*/
function Mode(elems) {
    // 所有元素
    this._elems = elems;

    // 被选中元素的索引
    this._selectedIndex = -1;

    // 增加一项
    this.itemAdd = new Event(this);

    // 删除一项
    this.itemRemoved = new Event(this);

    this.selectedIndexChanged = new Event(this);
}

Mode.prototype = {

    constructor: ‘Mode‘,

    // 获取所有的项
    getItems: function(){
        return [].concat(this._elems);
    },
    // 增加一项
    addItem: function(elem) {
        this._elems.push(elem);
        this.itemAdd.notify({elem:elem});
    },
    // 删除一项
    removeItem: function(index) {
        var item = this._elems[index];
        this._elems.splice(index,1);
        this.itemRemoved.notify({elem:item});

        if(index === this._selectedIndex) {
            this.setSelectedIndex(-1);
        }
    },
    getSelectedIndex: function(){
        return this._selectedIndex;
    },
    setSelectedIndex: function(index){
        var previousIndex = this._selectedIndex;
        this._selectedIndex = index;
        this.selectedIndexChanged.notify({previous : previousIndex});
    }
};
/*
 下面是观察者模式类,它又叫发布---订阅模式;它定义了对象间的一种一对多的关系,
  让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。
*/
function Event(observer) {
    this._observer = observer;
    this._listeners = [];
}
Event.prototype = {
    constaructor: ‘Event‘,
    attach : function(listeners) {
        this._listeners.push(listeners);
    },
    notify: function(objs){
        for(var i = 0,ilen = this._listeners.length; i < ilen; i+=1) {
            this._listeners[i](this._observer,objs);
        }
    }
};

/*
 * 视图显示模型数据,并触发UI事件。
 */
function View(model,elements){
    this._model = model;
    this._elements = elements;

    this.listModified = new Event(this);
    this.addButtonClicked = new Event(this);
    this.delButtonClicked = new Event(this);
    var that = this;

    // 绑定模型监听器
    this._model.itemAdd.attach(function(){
        that.rebuildList();
    });
    this._model.itemRemoved.attach(function(){
        that.rebuildList();
    });

    // 将监听器绑定到HTML控件上
    this._elements.list.change(function(e){
        that.listModified.notify({index: e.target.selectedIndex});
    });
    // 添加按钮绑定事件
    this._elements.addButton.click(function(e){
        that.addButtonClicked.notify();
    });
    // 删除按钮绑定事件
    this._elements.delButton.click(function(e){
        that.delButtonClicked.notify();
    });
}
View.prototype = {
    constructor:  ‘View‘,
    show:  function(){
        this.rebuildList();
    },
    rebuildList: function(){
        var list = this._elements.list,
            items,
            key;
        list.html("");
        items = this._model.getItems();
        for(key in items) {
            if(items.hasOwnProperty(key)) {
                list.append(‘<option value="‘+items[key]+‘">‘ +items[key]+ ‘</option>‘);
            }
        }
        this._model.setSelectedIndex(-1);
    }
};
/*
 控制器响应用户操作,调用模型上的变化函数
 负责转发请求,对请求进行处理
*/
function Controller(model,view) {
    this._model = model;
    this._view = view;
    var that = this;

    this._view.listModified.attach(function(sender,args){
        that.updateSelected(args.index);
    });
    this._view.addButtonClicked.attach(function(){
        that.addItem();
    });
    this._view.delButtonClicked.attach(function(){
        that.delItem();
    });
}
Controller.prototype = {
    constructor: ‘Controller‘,

    addItem: function(){
        var item = window.prompt(‘Add item:‘, ‘‘);
        if (item) {
            this._model.addItem(item);
        }
    },

    delItem: function(){
        var index = this._model.getSelectedIndex();
        if(index !== -1) {
            this._model.removeItem(index);
        }
    },

    updateSelected: function(index){
        this._model.setSelectedIndex(index);
    }
};

HTML代码如下:

<select id="list" size="10" style="width: 10rem"></select><br/>
<button id="plusBtn">  +  </button>
<button id="minusBtn">  -  </button>

页面初始化代码如下:

$(function () {
    var model = new Mode([‘PHP‘, ‘JavaScript‘]),
      view = new View(model, {
        ‘list‘ : $(‘#list‘),
        ‘addButton‘ : $(‘#plusBtn‘),
        ‘delButton‘ : $(‘#minusBtn‘)
       }),
       controller = new Controller(model, view);
        view.show();
}); 

代码分析如下:

先分下下 我们是要实现什么样的功能; 基本功能有:一个下拉框,通过用户输入的操作来实现用户增加一项及用户选中一项后删除一项的功能;
当然也添加了用户切换到那一项的事件;

比如我们现在来增加一条数据的时候,在视图层上添加监听事件,如下代码:
// 添加按钮绑定事件
this._elements.addButton.click(function(e){
    that.addButtonClicked.notify();
});
然后调用观察者类Event中的方法notify(发布一个事件) that.addButtonClicked.notify();大家都知道,观察者模式又叫发布-订阅模式,
让多个观察者对象同时监听某一个主题对象,当某一个主题对象发生改变的时候,所有依赖它的对象都会得到通知;
因此在控制层(Controller)我们可以使用如下代码对发布者进行监听操作:
this._view.addButtonClicked.attach(function(){
    that.addItem();
});
之后调用自身的方法addItem();代码如下:
addItem: function(){
    var item = window.prompt(‘Add item:‘, ‘‘);
    if (item) {
        this._model.addItem(item);
    }
}
调用模型层(model)的方法addItem();把一条数据插入到select框里面去;model(模型层)的addItem()方法代码如下:
// 增加一项
addItem: function(elem) {
    this._elems.push(elem);
    this.itemAdd.notify({elem:elem});
},
如上代码 增加一项后,通过 this.itemAdd 发布一个消息,然后在视图层(View)上通过如下代码来监听这个消息;代码如下:
// 绑定模型监听器
this._model.itemAdd.attach(function(){
      that.rebuildList();
});
最后监听到模型上(Model)的数据发生改变后,及时调用自身的方法rebuildList()去更新页面上的数据;

模型层(Model)最主要做业务数据封装操作。视图层(View)主要发布事件操作及监听模型层上的数据,如果模型层上有数据改变的时候,及时更新页面操作,
最后显示给页面上来,控制层(Controller)主要监听视图层(View)的事件,调用模型层(Model)的方法来更新模型上的数据,模型层数据更新后,会发布
一条消息出去,最后视图层(View)通过监听模型层(Model)的数据变化,来更新页面的显示; 如上是MVC的基本流程。
MVC的优点:
1. 耦合性低:视图层和业务层分离了,如果页面上显示改变的话,直接在视图层更改即可,不用动模型层和控制层上的代码;也就是视图层 与 模型层和控制层
已经分离了;所以很容易改变应用层的数据层和业务规则。
2. 可维护性:分离视图层和业务逻辑层也使得WEB应用更易于维护和修改。
MVC的缺点:
个人觉得适合于大型项目,对于中小型项目并不适合,因为要实现一个简单的增删改操作,只需要一点点JS代码,但是MVC模式代码量明显增加了。
对于学习成本也就提高了,当然如果使用一些封装好的MVC库或者框架就好了。

时间: 2024-10-12 21:17:15

【干货理解】理解javascript中实现MVC的原理的相关文章

JavaScript中this的工作原理以及注意事项

在JavaScript中,this 的概念比较复杂.除了在面向对象编程中,this 还是随处可用的.这篇文章介绍了this 的工作原理,它会造成什么样的问题以及this 的相关例子. 要根据this 所在的位置来理解它,情况大概可以分为3种: 1.在函数中:this 通常是一个隐含的参数. 2.在函数外(顶级作用域中):在浏览器中this 指的是全局对象:在Node.js中指的是模块(module)的导出(exports). 3.传递到eval()中的字符串:如果eval()是被直接调用的,th

JavaScript中实现DI的原理(二)

JavaScript中实现DI的原理 在JavaScript中实现DI,看起来难,实际上原理很简单,它的核心技术是Function对象的toString().我们都知道,对一个函数对象执行toString(),它的返回值是函数的源码,知道了这一点,接下来就简单的:我获取了函数源码,然后我对函数的声明进行解析,伪码如下: var giveMe = function(config) { }; var registry = {}; var inject = function(func, thisFor

我所理解的JavaScript中的prototype与__proto__、constructor

前几天当我同时向往说起node.js的时候,我逐渐发现原来JS是个多么丰富语言——我原来一直都在井底(初学者的悲哀).当我想认认真真学习JavaScript的时候,我发现prototype与__proto__.constructor这几个家伙老是让我很有挫败感(学一门语言属性都搞不懂,还学个P).但我是一个不轻易服输的人,也不是不会妥协,就是有这种习惯,我就要去做就要去弄懂哪怕挫折感更多.于是,我在网上搜大神们的对这几个让我烦恼的东西的阐述与解释.最后的结果是--还是搞不懂.“什么对象原型,什么

javascript中闭包的工作原理

一.什么是闭包? 官方”的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.相信很少有人能直接看懂这句话,因为他描述的太学术.其实这句话通俗的来说就是:JavaScript中所有的function都是一个闭包.不过一般来说,嵌套的function所产生的闭包更为强大,也是大部分时候我们所谓的“闭包”.看下面这段代码: 1 2 3 4 5 6 7 function a() { var i = 0; function b() { ale

简单介绍 javascript 中 __proto__ 属性的原理

在 javascript 中我们会约定俗成,如果一个方法是被 new 出来使用的,那么该方法名首字母通常会大写,例如下面代码块中的 Person.(我们也可以把 Person 看成 java 或 c# 中的类) var Person = function(name) { this.name = name; } 在 javascript 中一个类被 new 出来的具体过程如下: // 初始化一个对象 p var p = new Person(); // 初始化一个对象 p var p = {};

javascript中new操作符的原理

javascript中的new是一个语法糖,对于学过c++,java 和c#等面向对象语言的人来说,以为js里面是有类和对象的区别的,实现上js并没有类,一切皆对象,比java还来的彻底 new的过程实际上是创建一个新对象,把新象的原型设置为构造器函数的原型,在使用new的过程中,一共有3个对象参与了协作,构造器函数是第一个对象,原型对象是二个,新生成了一个空对象是第三个对象,最终返回的是一个空对象,但这个空对象不是真空的,而是已经含有原型的引用(__proto__) 步骤如下: (1)    

30行代码实现JavaScript中的MVC

从09年左右开始,MVC逐渐在前端领域大放异彩,并终于在刚刚过去的2015年随着React Native的推出而迎来大爆发:AngularJS.EmberJS.Backbone.ReactJS.RiotJS.VueJS…… 一连串的名字走马观花式的出现和更迭,它们中一些已经渐渐淡出了大家的视野,一些还在迅速茁壮成长,一些则已经在特定的生态环境中独当一面舍我其谁.但不论如何,MVC已经并将持续深刻地影响前端工程师们的思维方式和工作方法. 很多讲解MVC的例子都从一个具体的框架的某个概念入手,比如B

JavaScript中的原型继承原理

在JavaScript当中,对象A如果要继承对象B的属性和方法,那么只要将对象B放到对象A的原型链上即可.而某个对象的原型链,就是由该对象开始,通过__proto__属性连接起来的一串对象.__proto__属性是JavaScript对象中的内部属性,任何JavaScript对象,包括我们自己构建的对象,JavaScript的built-in对象,任何函数(在JavaScript当中,函数也是对象)都具有这个属性.如下图就是一个原型链的例子: 上图中,A,B,C分别代表3个对象,蓝色箭头串接起来

JavaScript中的作用域链原理

执行环境 作用域链的形成与执行环境(Execution Environment)相关,在JavaScript当中,产生执行环境有如下3中情形: 1 进入全局环境 2 调用eval函数 3 调用function 在一个执行环境A上可以创建执行环境B,执行环境B又可以创建执行环境C...,这一系列的执行环境构成执行环境栈,最新创建的执行环境位于栈顶(栈底永远是全局执行环境),当栈顶执行环境结束之后(与之相关的代码执行结束)就会被弹出站外,底下的执行环境就会成为新的栈顶.如下图所示: 一个执行环境由3