今天进入第二个部分:控制器。

控制器和状态

从以往的开发经验来看。我们都是将状态保存在server的session或者本地cookie中,但Javascript应用往往被限制在单页面,所以我们也能够将状态保存在client的内存里面。保存在内存还意味着能带来更快的界面响应。

在MVC里面,状态都是保存在控制器里的,控制器相当于应用中视图和模型的纽带。

当载入页面的时候,控制器将事件处理程序绑定在视图里。并适时处理回调,以及和模型必要的对接。

控制器是模块化的并且非常独立,理想状况下不应该定义不论什么全局变量,而是应该定义为全然解耦的功能组件。

所以我们通过模块模式将控制器放在一个马上处理的匿名函数里并传递全局參数进去,避免在内部訪问全局变量时须要遍历作用域,也清晰地表明了这个模块使用了哪些全局变量。

(function($){
var mod = {}; //改变參数的上下文并马上运行
mod.load = function(func){
$($.proxy(func, this));
}; //点击事件
mod.assetsClick = function(){
//处理点击
console.log("click!");
} //调用load方法
mod.load(function(){
//为元素加入单击事件
this.view = $("#view");
this.view.find(".assets").click(
$.proxy(this.assetsClick, this)
);
});
})(jQuery);

假设页面内有例如以下元素

<div id="view">
<button class="assets">button</button>
</div>

单击button后。将输出”click”,这也暗示了控制器和视图的联系。事实上书的这一部分讲的就是怎样在控制器内保存状态,依据状态的不同改变不同的视图。

(注)jQuery.proxy(method, context)

$.proxy方法是jQuery中的代理方法,它接收两个參数。返回一个新method,该方法始终保持context上下文。

比方在button的监听里:

$("#btn").click(function(){
//这里的this代表button
});
$("#btn").click(
$.proxy(function(){
//这里的this代表window
}, window);
);

(注解完)

抽象出库

如今将控制器抽象成库,并加入一些新的方法。这样就能在外部或别的模块中重用它了。

(function($, exports){
//假设通过构造方法传递參数,则调用include方法
var mod = function(includes){
if(includes){
this.include(includes);
}
};
//改变原型对象名(便于调用)
mod.fn = mod.prototype; //定义自己的代理方法。上下文始终指向控制器自己
mod.fn.proxy = function(func){
return $.proxy(func, this);
}; //马上运行函数
mod.fn.load = function(func){
$(this.proxy(func));
}; //为构造器加入方法
mod.fn.include = function(ob){
$.extend(this, ob);
}; //将构造器暴露出全局,在外界也能够訪问
exports.Controller = mod;
})(jQuery, window);

在别的地方须要用到控制器的时候,就调用Controller就可以:

(function($, Controller){
//创建控制器
var mod = new Controller(); //改变view的类名
mod.toggleClass = function(e){
//jQuery中的toggleClass方法代表假设存在此类名则删除,不存在则加入
this.view.toggleClass("over");
} //页面载入完马上运行
mod.load(function(){
//绑定页面元素,加入监听
this.view = $("#view");
this.view.mouseover(this.proxy(this.toggleClass));
this.view.mouseout(this.proxy(this.toggleClass));
});
})(jQuery, Controller)

在以上的使用中,通过匿名函数马上调用的方法并非在DOM载入之后载入的。而是在生成DOM之前,然而控制器的load方法又是在页面文档载入完毕之后才进行回调。我们能够对控制器进行进一步改写,在DOM生成之后统一载入控制器。

//定义一个全局对象
var exports = this;
(function($){
var mod = {};
//提供一个create方法,生成控制器
mod.create = function(includes){
//create方法返回的就是result,一种控制器
var result = function(){
//创建控制器实例的时候调用初始化方法
this.init.apply(this, arguments);
}
result.fn = result.prototype;
result.fn.init = function(){}; result.proxy = function(func){
return $.proxy(func, this);
};
result.fn.proxy = result.proxy; result.include = function(ob){
$.extend(this.fn, ob);
};
result.extend = function(ob){
$.extend(this, ob);
};
if(includes){
result.include(includes);
}
return result;
}; exports.Controller = mod;
})(jQuery);

在创建控制器的时候必须手动指定init方法,在init方法为dom元素加入监听:

$(function(){
//创建控制器
var ToggleView = Controller.create({
init: function(view){
this.view = $(view);
this.view.mouseover(this.proxy(this.toggleClass));
this.view.mouseout(this.proxy(this.toggleClass));
},
toggleClass: function(){
this.view.toggleClass("over");
}
}); //创建一个控制器的实例
new ToggleView("#view");
});

在创建实例的时候将在构造函数里触发init事件。另外依据实例化的情况将视图传入控制器而不是写死在控制器内,我们就能够将控制器重用于不同的元素。同一时候保持代码最短。

訪问视图

一种常见的模式是一个视图相应一个控制器,视图包括Id,而在控制器内使用视图的元素则使用class,这样和其它视图的元素不会产生冲突,比方上面的ToggleView传入了Id为view的元素,所以在view内的元素则使用类名进行訪问。

init: function(view){
this.view = $(view);
this.form = this.view.find(".form");
}

但这意味着控制器中会有非常多选择器,须要不断查找DOM,我们能够在控制器中专门开辟一个空间来存放选择器到变量的映射表:

elements: {
"form.searchForm": "searchForm",
"form input[type=text]": "searchInput"
}

有了这种映射表之后。控制器的属性名(比方searchForm)就能和详细的元素(类名为searchForm的form)相相应了,并且在控制器实例化的时候创建他们:

$(function($){
exports.SearchView = Controller.create({
//视图中的元素使用类名查找
elements: {
"input[type=search]": "searchInput",
"form": "searchForm"
}, init:function(element){
//获取视图元素
this.el = $(element);
//依据映射表创建属性
this.refreshElements();
//为元素加入监听等
this.searchForm.submit(this.proxy(this.search));
},
//事件处理函数
search: function(e){
console.log("Searching", this.searchInput.val());
}, //内部使用的选择器,将上下文指定为视图元素
$: function(selector){
return $(selector, this.el);
}, //创建视图内的元素
refreshElements: function(){
for(var key in this.elements){
//key为选择器名。值为属性名
this[this.elements[key]] = this.$(key);
}
}
});
//视图用id指定
new SearchView("#users");
});

状态机

状态机是“有限状态机”的简称,本质上由两部分构成:状态和转换器。它仅仅有一个活动状态,但也包括非常多非活动状态。当活动状态之间相互切换的时候就会调用状态转换器。

比方应用中存在非常多视图,它们的显示是相互独立的,一个视图用来显示联系人,一个视图用来编辑联系人,这两个视图一定是相互排斥关系,当中一个显示还有一个一定隐藏,这个场景就非常适合引入状态机。由于它能确保每一个时刻仅仅有一个是激活的。

首先看一下状态机的思路。我们构造一个状态机:

var StateMachine = function(){};
StateMachine.fn = StateMechine.prototype; StateMachine.fn.bind = function(){
if(!this.o){
this.o = $({});
}
//绑定自己定义事件
this.o.bind.apply(this.o, arguments);
} StateMachine.fn.trigger = function(){
if(!this.o){
this.o = $({});
}
//触发自己定义事件
this.o.trigger.apply(this.o, arguments);
} StateMechine.fn.add = function(controller){
//为状态机绑定自己定义事件
this.bind("change", function(e, current){
if(controller == current){
controller.activate();
}else{
controller.deactivate();
}
}); //为控制器创建激活方法
controller.active = $.proxy(function(){
this.trigger("change", controller);
}, this);
}

(注)jQuery中的自己定义事件

上述代码重点在于bind和trigger,在jQuery中,利用这两个方法能够非常轻易地实现自己定义事件:

var $obj = $({});
$obj.bind("myEvent", function(){
console.log("自己定义事件");
});
$obj.trigger("myEvent"); //"自己定义事件"

bind代表绑定。trigger表示触发,你仅仅须要一个jQuery对象就够了。所以上面创建了一个空的对象,使用$包装成了jQuery对象。

(注解完)

这个状态机的add()方法将传入的控制器加入至状态列表,并为状态机中的o绑定一个自己定义的change事件(加入一个绑定一个)。然后创建一个active()函数。当控制器调用active()的时候。触发所有的change事件,则除了调用的控制器将运行activate方法外。其它控制器所有运行deactivate方法:

//控制器1
var con1 = {
activate: function(){
console.log("con1 activate");
},
deactivate: function(){
console.log("con1 deactivate");
}
};
//控制器2
var con2 = {
activate: function(){
console.log("con2 activate");
},
deactivate: function(){
console.log("con2 deactivate");
}
}; var sm = new StateMachine();
sm.add(con1);
sm.add(con2); con1.active(); //输出"con1 activate"和"con2 deactivate"

当然也能够直接通过状态机触发

sm.trigger("change", con1);

通过状态机的切换状态,我们能够结合控制器改变视图,当con1激活的时候显示一个视图,否则隐藏;con2激活的时候显示还有一个视图,否则隐藏。

这样就能依据状态的不同对视图进行切换。

Javascript MVC 学习笔记(二) 控制器和状态的更多相关文章

  1. JavaScript正则表达式学习笔记(二) - 打怪升级

    本文接上篇,基础部分相对薄弱的同学请移步<JavaScript正则表达式学习笔记(一) - 理论基础>.上文介绍了8种JavaScript正则表达式的属性,本文还会追加介绍几种JavaSc ...

  2. Javascript MVC 学习笔记(一) 模型和数据

    写在前面 近期在看<MVC的Javascript富应用开发>一书.本来是抱着一口气读完的想法去看的.结果才看了一点就傻眼了:太多不懂的地方了. 仅仅好看一点查一点,一点一点往下看吧,进度虽 ...

  3. ThinkPHP 学习笔记 ( 二 ) 控制器 ( Controller )

    /** * ThinkPHP version 3.1.3 * 部署方式:应用部署 * 文内的 http://localhost/ 由实际主机地址代替 */ 入口文件 index.php: <?p ...

  4. Javascript MVC 学习笔记(三) 视图和模板

    模板 Javascript中模板的核心概念是,将包括模板变量的HTML片段和Javascript对象做合并.把模板变量替换为对象中的属性值. 书中讲到了几种库作为模板引擎,可是链接失效了.能够在这里下 ...

  5. JavaScript入门-学习笔记(二)

    关于js变量 变量,就是一个用来存储数据的容器 一般来说,我们的变量都是可以得先声明,再使用,就像是一个东西先必须存在,才能看得见摸得着.然而在js里(es5),可以先使用,后声明. a = 100; ...

  6. spring mvc学习笔记二:@RequestMapping

    @RequestMapping RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上.用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径. @RequestMapp ...

  7. JavaScript Object学习笔记二

    Object.create(proto, [propertiesObject])//创建对象,使用参数一来作为新创建对象的__proto__属性,返回值为在指定原型对象上添加自身属性后的对象 //参数 ...

  8. ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则

    ASP.NET MVC 学习笔记-7.自定义配置信息   ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...

  9. .NET MVC 学习笔记(二)— Bootstrap框架

    二..NET MVC 学习笔记(一)—— Bootstrap框架 在实际开发过程中,页面的样式问题是让人很头疼的问题,良好的用户界面可以大大提高用户体检,而在你没有前端开发人员的火力支援情况下,并不是 ...

随机推荐

  1. 关于postman使用上发现的一点问题

    之前后台用的java,一直用的postman测试接口数据,之前不管是get.post.delete.put请求都是在param传递的数据,java下面是没问题可以测试的.但是今天自己写Node发现po ...

  2. 关于sass和less做自适应网页的区别

    less 可以这么写  @r: 15rem;   body{margin-top:40/@r}; 但是sass这么写会报错 sass应该这么写 $r: 15; body{margin-top:40re ...

  3. UVA 10131 Is Bigger Smarter?(DP最长上升子序列)

    Description   Question 1: Is Bigger Smarter? The Problem Some people think that the bigger an elepha ...

  4. 'Add Solution': A timeout has occurred while invoking commands in SharePoint host process.

    一.问题描述: 在部署SharePoint solution的时候,出现Time out 的问题,错误提示: Error occurred in deployment step 'Add Soluti ...

  5. (12)ubunto 快捷键

    -----------------------------------------------------vi快捷键------------------------------------ 指令模式: ...

  6. BZOJ3751 NOIP2014 解方程(Hash)

    题目链接  BZOJ3751 这道题的关键就是选取取模的质数. 我选了4个大概几万的质数,这样刚好不会T 然后统计答案的时候如果对于当前质数,产生了一个解. 那么对于那些对这个质数取模结果为这个数的数 ...

  7. codevs——2956 排队问题

    2956 排队问题  时间限制: 1 s  空间限制: 32000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description 有N个学生去食堂,可教官规定:必须2人或3 ...

  8. Java 对象的序列化、反序列化

    对象的序列化(Serialize):将内存中的Java对象转换为与平台无关的二进制流(字节序列),然后存储在磁盘文件中,或通过网络传输给另一个网络节点. 对象的反序列化(Deserialize):获取 ...

  9. Codeforces A. Bear and Big Brother

    ...不行.这题之后.不做1000分以下的了.很耻辱   A. Bear and Big Brother time limit per test 1 second memory limit per t ...

  10. kafka技术分享02--------kafka入门

    kafka技术分享02--------kafka入门 1. 消息系统 ​ 所谓的Messaging System就是一组规范,企业利用这组规范在不同的系统之间传递语义准确对的消息,实现松耦合的异步数据 ...