精华内容
下载资源
问答
  • 相信大家写代码,做设计的时候都会有一两本书做参考的吧? 不同的技术不同的书籍,不同的人也不同的书。希望大家能交流下,介绍一些好的书做为参考资料给我们这些新入行的。 像我现在学Spring看的是Spring技术...
    相信大家写代码,做设计的时候都会有一两本书做参考的吧?
    不同的技术用不同的书籍,不同的人也用不同的书。希望大家能交流下,介绍一些好的书做为参考资料给我们这些新入行的。
    像我现在学Spring看的是Spring技术手册,林信良的。感觉帮我入门还可以,但是做项目时拿来参考好像不够。谁能介绍基本好书?不限是什么技术的。在此先谢过。
    展开全文
  • 参考书籍:《代码整洁之道》,语言:Python 1.变量命名要与其本意相符合 1)变量、函数或类命名应该能告诉我们,它为什么存在,它做什么事,应该怎么 2)如果一个名称还需要注释来补充,那就不算是名副其实 3)...

    有意义的命名

    参考书籍:《代码整洁之道》,语言:Python

    1.变量的命名要与其本意相符合

    1)变量、函数或类的命名应该能告诉我们,它为什么存在,它做什么事,应该怎么用

    2)如果一个名称还需要注释来补充,那就不算是名副其实

    3)目的:选择体现本意的名称让人更容易理解和维护

    案例1:定义一个变量,用来表示消逝的时间,以日计

    1 #名称d什么也没有说明,它没有引起时间逝去的感觉,也没有说明时间的单位
    2 d = 13  
    4 #定义指明计量对象和计量单位的名称
    5 elapsed_time_in_days = 1
    6 days_since_creation = 1
    7 days_since_modification = 1
    8 file_age_in_days = 1
    

    案例2:一段模糊代码的优化

     1 """
     2     demo1:
     3         a.没有体现list1表示什么
     4         b.没有体现list2的0下标表示什么
     5         c.没有体现值3表示什么
     6         d.没有体现应该怎么使用返回的列表
     7 """
     8 list1 = [[0, 1, 2], [3, 1, 3]]
     9 def get_them():
    10     list2 = []
    11     for x in list1:
    12         if x[0] == 3:
    13             list2.append(x)
    14     return list2
    15 
    16 #print(“demo1 ”, get_them())
    17 
    18 ##假设我们开发一种扫雷游戏,盘面上的单元格都用一个列表表示,0下标表示单元格的一种状态值,值3表示“已标记”
    19 
    20 """
    21     demo2:将变量的命名修改为有意义的名称
    22 """
    23 game_board1 = [[0, 1, 2], [3, 1, 3]]
    24 STATUS_VALUE = 0
    25 FLAGGED = 3
    26 def get_flagged_cells1():
    27     flagged_cells = []
    28     for cell in game_board1:
    29         if cell[STATUS_VALUE] == FLAGGED:
    30             flagged_cells.append(cell)
    31     return flagged_cells
    32 
    33 #print("demo2 ", get_flagged_cells1())
    34 
    35 """
    36     demo3:用一个类表示单元格
    37 """
    38 class Cell():
    39 
    40 
    41     FLAGGED = 3
    42 
    43     def __init__(self, status, x_point, y_point):
    44         self.status = status
    45         self.x_point = x_point
    46         self.y_point = y_point
    47 
    48     def is_flagged(self):
    49         return self.status == FLAGGED
    50 
    51 
    52 game_board2 = [Cell(0, 1, 2), Cell(3, 1, 3)]
    53 def get_flagged_cells2():
    54     flagged_cells = []
    55     for cell in game_board2:
    56         if cell.is_flagged():
    57             flagged_cells.append(cell)
    58     return flagged_cells
    59 
    60 #print("demo3 ", get_flagged_cells2()[0].status, get_flagged_cells2()[0].x_point, get_flagged_cells2()[0].y_point)
    

    2.避免使用引起变量本意误导的命名

    1)变量名不要携带变量类型的信息,比如names_list

    2)accounts比account_group和bunch_of_accounts会好一些

    3)提防两个外形太相似的命名,比如xyyz_controller_for_efficient_handling_of_strings和xyyz_controller_for_efficient_storage_of_strings

    4)不要用误导性的变量命名,比如小写的字母l和大写的字母O,因为它们与1和0很像

    展开全文
  • acm相关书籍

    2019-01-06 19:54:55
    我最开始学dancing link的的时候,自己敲出了代码,然后接下来几道题部分参考了以前的代码,后来基本上是直接copy。现在,当别人问我dancing link算法或有关题目时候,我已经是一脸茫然。 所以,模板是不好...
  • 对应的书籍资料见: SQL Server 2008数据库设计与实现(关系数据库实现的通关宝典) 基本信息 原书名: Pro SQL Server 2008 Relational Database Design and Implementation 原出版社: Apress 作者: (美)Louis ...
  • C++书籍集合

    2013-03-19 22:48:38
    大多时候,一些初学者不知道应该读什么书入门,有一定基础的开发者不知道如何进阶。本文总结了一个C++优秀书籍清单,希望能够为你带来一定的帮助。因为多读一些优秀的书籍,对于开发者稳固编程基础、提高编程技能有...
  • 本篇文章参考书籍《JavaScript设计模式》–张容铭 文章目录前言MVC 模式总个小结 前言   嗨!各位乡绅,上一节字符串模板,有没有很崩溃,模板引擎写起来是有点费劲,但是思路比较简单,慢慢梳理就好。   本节...

    本篇文章参考书籍《JavaScript设计模式》–张容铭

    前言

      嗨!各位乡绅,上一节的字符串模板,有没有很崩溃,模板引擎写起来是有点费劲,但是思路比较简单,慢慢梳理就好。

      本节我们来个硬货,如果各位经常浏览招聘信息,那么很多公司都会在岗位要求上面写上,熟悉 MVC 框架者优先。今天我们就来搞搞,到底什么是传说中 MVC
    在这里插入图片描述

    MVC 模式

      MVC 即模型(model)—视图(view)—控制器(controller),用一种将业务逻辑、数据、视图分离的方式组织架构代码

      现在我们抛出一个问题,在开发的时候,如果把页面分为三个部分,应该怎么划分?

      思维活跃的同学一定第一时间反应到,前端开发老三样, HTML,CSS,JS

      这样划分没错哈,那如果问题再具体一点,按功能把页面划分成三个部分,应该怎么分?

      有同学可能还会说,还是老三样, HTML 是页面骨架, CSS 负责美容, JS 执行逻辑。这样划分也对也不对,问题就出在 JS 身上,这玩意功能太强大,不光能处理逻辑,也能构建 HTML ,控制 CSS ,简直无所不能了有点。

      所以为了划分好 JS 的功能,我们需要把页面分成一下三个层次:

       - 视图层:创建视图

       - 数据层:处理数据

       - 业务逻辑层:处理交互逻辑

      开发中我们需要分清者三个层次,管理好各个层次的内容,页面才会繁而不乱。我们今天学习的 MVC 就是专门处理这类问题的。

    实现传说

      对于 MVC 大家应该都有所耳闻,但是要去实现这么个东西,我估计大家可能就没什么思路了,其实依照 MVC 的思想一步一步来,就可以了。

      首先第一步,那当然是把页面分成三个层面,分别是数据层,视图层,控制器层。

      其次我们知道,视图层可以调用数据层创建视图,控制器层可以调用数据层数据与视图层内的视图创建页面增添逻辑。

      因此,我们可以简化页面为三个部分:

    //为了简化页面操作,这里引用链模式中实现的 A 框架,具体方法在本文最后附录
    //页面加载后创建 MVC 对象
    $(function() {
    	//初始化 MVC 对象
    	var MVC = MVC || {};
    	//初始化 MVC 数据模型层
    	MVC.model = function() {} ();
    	//初始化 MVC 视图层
    	MVC.view = function() {} ();
    	//初始化 MVC 控制器层
    	MVC.ctrl = function() {} ();
    });
    

      上面 MVC 的雏形还是很好理解的,三个层次分别书写在 3 个对象中。有一点需要注意的是,每个对象是一个自动执行函数,这是因为三个层次对象需要可被调用,而声明的函数在执行前是不能被调用的,比如模型对象要被视图和控制器调用,所以执行一遍是为其他对象调用提供接口方法。

    数据模型层

      接下来我们开始实现数据模型层,这一层的服务器端数据和组件配置数据要能被视图层调用,所以我们需要让数据模型对象返回操作这两类数据的接口方法。

    //MVC 数据模型层
    MVC.model = function() {
    	//内部数据对象
    	var M = {};
    	//服务器端获取数据,通过Ajax获取并储存,后面为简化实现,直接作为同步数据写在页面
    	M.data = {};
    	//配置数据,页面加载时即提供
    	M.config = {};
    	//返回数据模型层对象操作方法
    	return {
    		//获取服务器端数据
    		getData: function(m) {
    			//根据数据字段获取数据
    			return M.data[m];
    		},
    		//设置服务器端数据
    		setData: function(m, v) {
    			//设置数据字段 m 对应的数据 v
    			M.data[m] = v;
    			return this;
    		},
    		//设置配置数据
    		setConf: function(c, v) {
    			//设置配置数据字段 c 对应的配置数据 v
    			M.conf[c] = v;
    			return this;
    		}
    	}
    }();
    

      通过 model 接口对象返回的 4 个操作方法即可对 model 内部的服务器端数据与配置数据做增删改查。这样我们可以在视图对象以及控制器对象内部轻松操作模型数据对象内部的数据了。

    视图层

      根据模型数据层对象的设计思想我们可以创建出视图对象,当然视图对象保存着对应组件内部的视图,为了创建这些视图我们还要在视图对象内部引用模型数据对象以操作模型数据对象内部的数据,最后为了让控制器可以操作视图层内的视图,我们则需要返回一些操作接口方法。

    //MVC 视图层
    MVC.view = function() {
    	//模型数据层对象操作方法引用
    	var M = MVC.model;
    	//内部视图创建方法对象
    	var V = {};
    	//获取视图接口方法
    	return function(v) {
    		//根据视图名称返回视图
    		//(由于获取的是一个方法,这里需要将该方法执行一遍以获取相应视图)
    		V[v]();
    	}
    }();
    

      视图层其实要比想象中简单的多吧,只有三个部分,一是操作模型数据对象方法的引用( M ),二是内部视图创建方法对象,三是外部获取视图创建方法接口。

      所以 MVC 只是在以讹传讹的过程中被神化了,其实实现起来并不难。

    控制器层

      最后一个控制器对象,该对象要操作视图层对象与模型数据层对象,参考数据模型层与视图层的实现思想,将控制器层创建出来。

    //MVC 控制器层
    MVC.ctrl = function() {
    	//模型数据层对象操作方法引用
    	var M = MVC.model;
    	//视图数据层对象操作方法引用
    	var V = MVC.view;
    	//控制器创建方法对象
    	var C = {};
    } ();
    

    测试完去干饭

      我们用 MVC 简单实现一个小组件,比如这里有一个导航栏,当鼠标移动到导航栏上时,显示该模块浮层,离开后消失,导航栏下方有按钮可以隐藏整个导航区域。

      对于这个需求我们可以拆分三个模块对象层次,第一显示这些导航模块数据(图片和文字)是可变的,需要从服务器获取数据,属于模型数据层内部数据,由于在导航显隐过程中需要屏蔽箭头按钮的操作功能,因此需要在页面中配置一个记录数据,而该数据应属于模型数据层内的配置数据。

    MVC.model = function() {
    	var M = {};
    	M.data = {
    		//左侧侧边栏导航服务器端请求得到的响应数据
    		slideBar: [
    			{
    				text: '二次元',
    				icon: 'left_quadratic.png',
    				title: '喵耳萝莉千本樱',
    				content: '自古萝莉有三好~',
    				href: 'http://www.hao123.com'
    			},
    			{
    				text: '萌妹子',
    				icon: 'left_meng.png',
    				title: '石原里美哦哈呦',
    				content: '高山流水遇知音~',
    				href: 'http://www.hao123.com'
    			},
    			{
    				text: '动漫',
    				icon: 'left_cartoon.png',
    				title: '少女战士水冰月',
    				content: '无边落木萧萧下~',
    				href: 'http://www.hao123.com'
    			},
    			{
    				text: '电影',
    				icon: 'left_movie.png',
    				title: '倩女幽魂王祖贤',
    				content: '直道相思了无益~',
    				href: 'http://www.hao123.com'
    			},
    			{
    				text: '电视剧',
    				icon: 'left_quadratic.png',
    				title: '神雕侠侣李若彤',
    				content: '曾经沧海难为水~',
    				href: 'http://www.hao123.com'
    			}
    		]
    	}
    	M.conf = {
    		//侧边导航动画配置数据
    		slideBarCloseAnimate: false
    	}
    	return {/*接口方法*/}
    } ();
    

      对于每个导航图标模块结构都是类似的,因此他们有相同的视图模板,将侧边导航模块视图模块分为两部分:一部分是模块容器模板,另一部分是导航图标模块模板。为了得到侧边导航视图,我们需要用该模块对应的数据,通过模板渲染引擎 formateString 来渲染整个模块视图。

      这里需要用到上一节介绍的简单模板模式中定义的 formateString 方法。

    MVC.view = function() {
    	var M = MVC.model;
    	var V = {
    		//创建侧边导航模块视图
    		createSlideBar: function() {
    				//导航图标内容
    			var html = '',
    				//视图渲染数据
    				data = M.getData('slideBar');
    			//屏蔽无效数据
    			if(!data || !data.length) {
    				return;
    			}
    			//创建视图容器(参考附录中 A 框架中创建元素方法 create)
    			var dom = $.create('div', {
    				'class': 'slidebar',
    				'id': 'slidebar'
    			});
    				//视图容器模板
    			var tpl = {
    				container: [
    					'<div class="slidebar_inner"><ul>{#content#}</ul></div>',
    					'<a hidefocus href="#" class="slidebar_close" title="收起"/>'
    				].join(''),
    				//导航图标模块模板
    				item: [
    					'<li>',
    						'<a class="icon" href="{#href#}">',
    							'<img src="common/img/{#icon#}">',
    							'<span>{#text#}</span>',
    						'</a>',
    						'<div class="box">',
    							'<div>',
    								'<a class="title" href="{#href#}">{#title#}</a>',
    								'<a href="{#href#}">{#content#}</a>',
    							'</div>',
    							'<a class="image" href="{#href#}"><img src="common/img/{#img#}" /></a>',
    						'</div>',
    					'</li>',
    				].join('')
    			};
    			//渲染全部导航图片模块
    			for(var i = 0, len = data.length; i < len; i++) {
    				html += $.formateString(tpl.item, data[i]);
    			}
    			//在页面中创建侧边导航视图
    			dom
    				//向侧边导航模块容器中插入侧边导航视图
    				.html(
    					//渲染导航视图
    					$.formateString(tpl.container, {content: html})
    				)
    				//将侧边导航模块容器插入页面中
    				.appendTo('body');
    		}
    	}
    	return function(v) {
    		V[v]();
    	}
    }();
    

      通过调用视图层对象中某组件的视图方法,我们即可在页面中创建侧边导航栏视图。接下来就是为视图添加交互方法,这一部应当放在控制器对象中,获取视图元素,并为试图中的元素绑定事件交互,以及添加动画特效。

    MVC.ctrl = function() {
    	var V = MVC.view;
    	var M = MVC.model;
    	var C = {
    		//侧边导航栏模块
    		initSLideBar: function() {
    			//渲染导航栏模块视图
    			V('createSliderBar');
    			//为每一个导航图标添加鼠标光标划过与离开交互事件(参考附录)
    			$.('li', 'slidebar')
    			//鼠标移入导航 icon 显示导航浮层
    			.on('mouseover', function(e) {
    				$(this).addClass('show');
    			})
    			//鼠标移出导航 icon 隐藏导航浮层
    			.on('mouseout', function(e) {
    				$(this).removeClass('show');
    			});
    			//箭头 icon 动画交互
    			$('.slidebar-close', 'slidebar')
    			//点击箭头 icon 时
    			.on('click', function(e) {
    				//如果正在执行动画
    				if(M.getConf('slideBarCloseAnimate')) {
    					//终止操作
    					return false;
    				}
    				//设置侧边导航模块动画配置数据开关为打开状态
    				M.setConf('slideBarCloseAnimate', true);
    				//获取当前元素(箭头 icon)
    				var $this = $(this);
    				//如果箭头 icon 是关闭状态(含有 is-close 类)
    				if($this.hasClass('is-close')) {
    					//为侧边导航模块添加显示动画
    					$.('.slider-inner', 'slidebar')
    					.animate({
    						//动画时间
    						duration: 800,
    						//动画类型
    						type: 'easeOutQuart',
    						//动画主函数
    						main: function(dom) {
    							//每一帧改变导航模块容器  left 值
    							dom.css('left', -50 + this.tween * 50 + 'px');
    						},
    						//动画结束时回调函数
    						end: function() {
    							//设置箭头 icon 为打开状态(删除 is-close)类
    							$this.removeClass('is-close');
    							//设置侧边导航模块动画配置数据开关为关闭状态(此时可以继续进行模块显隐动画交互)
    							M.setConf('slideBarCloseAnimate', false);
    						}
    					});
    				//如果箭头 icon 是打开状态(不含 is-close)类
    				} else {
    					//为侧边导航模块添加显示动画
    					$('.slidebar-inner', 'slidebar')
    					.animate({
    						//动画时间
    						duration: 800,
    						//动画类型
    						type: 'easyOutQuart',
    						//动画主函数
    						main: function(dom) {
    							//每一帧改变导航模块容器 left 值
    							dom.css('left', this.tween * -50 + 'px');
    						},
    						//动画结束时回调函数
    						end: function() {
    							//设置箭头 icon 为打开状态(删除 is-close)类
    							$this.addClass('is-close');
    							//设置侧边导航模块动画配置数据开关为关闭状态(此时可继续进行模块显隐动画交互)
    							M.setConf('slideBarCloseAnimate', false);
    						}
    					});
    				}
    			})
    		}
    	};
    	//为侧边导航模块添加交互与动画特效
    	C.initSlideBar();
    } ();
    

      接下来轮到执行控制器了,控制器要做的事很简单,第一创建视图页面,第二添加交互与动画特效。而在 MVC 的控制器中,由于创建视图的主要逻辑在视图层对象中,因此也就弱化了控制器中创建对象的功能。

      我们只需要一行就能搞定(直接调用视图层对象接口方法渲染),而控制器对象也将自己的主要的精力放在交互与特效上。

      最后为了实现控制器中的功能,我们显性的调用了 C.initSlideBar() 方法,但是如果模块很多,那么一次一次的调用会造成控制器内部混乱,有以下两种解决办法。第一种,可以将对象中的方法改为创建即执行:

    var C = {
    	//侧边导航栏模块
    	initSlideBar: function() {} ()
    };
    //第二种,可以在对象末尾处遍历内部对象 C 中的每一个方法并执行
    for(var i in C) {
    	//如果模块方法存在则执行
    	C[i] && C[i]();
    }
    

      这样设计程序的好处在于可以实结构层次清晰,用数据时可以专心处理数据,创建视图时也可责无旁贷,将更多的精力放在交互与特效上。

      比如我们再增加一个新闻模块,只需要为三个对象添加代码即可。

    MVC.model = function() {
    	var M = {};
    	M.data = {
    		//左侧侧边栏导航服务器端请求得到响应数据
    		slideBar: {
    			//...
    		},
    		/*新增模块追加代码*/
    		newMod: {
    			//...
    		}
    	}
    	M.conf = {
    		//侧边导航动画配置数据
    		slideBarCloseAnimateL: falase
    		/*新增模块追加代码*/
    		// newModConf...
    	}
    	return {
    		// ...
    	}
    }();
    MVC.view = function() {
    	var M = MVC,model;
    	var V = {
    		//创建侧边导航模块视图
    		createSlideBar: function() {
    			//...
    		},
    		/*新增模块追加代码*/
    		createNewMod: function() {
    			//...
    		}
    	}
    	return function(v) {
    		V[v]();
    	}
    }();
    MVC.ctrl = function() {
    	var V = MVC.view;
    	var M = MVC.model;
    	var C = {
    		//侧边导航栏模块
    		initSlideBar: function() {
    			//...
    		}
    		/*新增模块追加代码*/
    		initNewMod: function() {
    			//...
    		}
    	};
    	//为侧边导航模块添加交互与动画特效
    	for(var i in C) {
    		C[i] && C[i]();
    	}
    }();
    

    总个小结

      只通过一个小例子大家可能感觉不到 MVC 框架的好处,大家可以了解下现在比较火的框架, Vue,React ,用了框架后,大部分人都不再想回到那个获取页面元素,处理页面逻辑,渲染页面数据的年代了,所有的无聊操作,框架帮你实现,你只需要关注数据就可以了。

    附录

      下面是 A 框架的代码。有点长,大家不方便使用的话,可以去我上传的资源里面去下载。

    /**
    * A Library v1.0.0
    * Author  Louhongxing
    * Date:   2021-1-5
    */
    ~(function(window) {
    	/**
    	* @name   框架单体对象 A
    	* @param  selector  选择器或页面加载回调函数
    	* @param  context   查找元素上下文
    	*/
    	var A = function(selector, context) {
    		//如果 selector 为放大则为窗口添加页面加载完成事件监听
    		if(typeof selector == 'function') {
    			A(window).on('load', selector);
    		} else {
    			//创建 A 对象
    			return new A.fn.init(selector, context);
    		}
    	}
    	//原型方法
    	A.fn = A.prototype= {
    		//强化构造函数
    		constructor: A,
    		//构造函数
    		init: function(selector, context) {
    			//modify 选择器为元素
    			if(typeof selector === 'object') {
    				this[0] = selector;
    				this.length = 1;
    				return this;
    			};
    			//设置获取到的元素长度属性
    			this.length = 0,
    			//矫正上下文
    			context = document.getELementById(context) || document;
    			//如果是 id 选择器
    			if(~selector.indexOf('#')) {
    				this[0] = document.getElementById(selector.slice(1));
    				this.length = 1;
    			//如果是类选择器
    			} else if(~selector.indexOf('.')) {
    				var doms = [],
    					className = selector.slice(1);
    				//支持通过类获取元素方法
    				if(context.getElementsByClassName) {
    					doms = context.getElementsByClassName(className);
    				} else {
    					doms = context.getELementsByTagName('*');
    				}
    				//设置获取到的元素
    				for(var i = 0, len = doms.length; i < len; i++) {
    					if(doms[i].className && !!~doms[i].className.indexOf(className)) {
    						this[this.length] = doms[i];
    						//矫正长度
    						this.length++;
    					}
    				}
    			//否则为元素名选择器
    			} else {
    				var doms = context.getElementsByTagName(selector),
    					i = 0,
    					len = doms.length;
    				for(; i < len; i++) {
    					this[i] = doms[i];
    				}
    				this.length = len;
    			}
    			//设置当前对象的选择上下文
    			this.context = context;
    			//设置当前对象的选择器
    			this.selector = selector;
    			return this;
    		},
    		//元素长度
    		length: 0,
    		//曾强数组
    		push: [].push,
    		splice: [].splice
    	}
    	//设置构造函数原型
    	A.fn.init.prorotype = A.fn;
    	/**
    	* @name  对象拓展
    	* @param[0]       目标对象
    	* @param [1, ...] 拓展对象
    	*/
    	A.extend = A.fn.extend = function() {
    		var i = 1,
    			len = arguments.length,
    			target = arguments[0],
    			j;
    		//如果一个参数,则为当前对象拓展方法
    		if(i == len) {
    			target = this;
    			i--;
    		}
    		//遍历拓展对象
    		for(; i < len; i++) {
    			//遍历拓展对象中方法与属性
    			for(j in arguments[i]) {
    				//浅复制
    				target[j] = arguments[i][j];
    			}
    		}
    		//返回目标对象
    		return target;
    	};
    	//单体对象 A 方法拓展
    	A.extend({
    		/*
    		* @name  将横线式命名字符串转化为驼峰式
    		* eg:  'test-demo' -> 'testDemo'
    		*/
    		cameCase: function(str) {
    			return str.replace(/\-(\w)/g, function(match, letter) {
    				return letter.toUpperCase();
    			});
    		},
    		/*
    		* @name  去除字符串两端空白
    		* eg:  '  t e s t  ' -> 't e s t'
    		*/
    		trim: function(str) {
    			return str.replace(/^\s+\\s+$/g, '')
    		},
    		/*
    		* @name 创建一个元素并包装成 A 对象
    		* @param type    元素类型
    		* @param value   元素属性对象
    		*/
    		create: function(type, value) {
    			var dom = document.createElement(type);
    			return A(dom).attr(value);
    		},
    		/*
    		* @name   格式化模板
    		* @param  str   模板字符串
    		* @param  data  渲染数据
    		* eg: '<div>{#value#}</div>' + {value: 'test'} => '<div>test</div>'
    		*/
    		formateString: function(str, data) {
    			var html = '';
    			//如果渲染数据是数组,则遍历数组并渲染
    			if(data instanceof Array) {
    				for(var i = 0, len = data.length; i < len; i++) {
    					html += arguments.callee(str, data[i]);
    				}
    				return html;
    			} else {
    				//搜索{#key#}格式字符串,并在 data 中查找对应的 key 属性替换
    				return str.replace(/\{#(\w+)#\}/g, function(match, key) {
    					return typeof data === 'string' ? data : (typeof data[key] === 'undefined' ? '' : data[key])
    				});
    			}
    		}
    	});
    	//事件绑定方法
    	var _on = (function() {
    		//如果标准浏览器
    		if(document.addEventListener) {
    			return function(dom, type, fn, data) {
    				dom.addEventListener(type, function(e) {
    					fn.call(dom, e, data);
    				}, false);
    			}
    		//如果 IE
    		} else if(document.attachEvent) {
    			return function(dom, type, fn, data) {
    				dom.c('on' + type, function(e) {
    					fn.call(dom, e, data);
    				});
    			}
    		//如果是老版本浏览器
    		} else {
    			return function(dom, type, fn, data) {
    				dom['on' + type] = function(e) {
    					fn.call(dom, e, data);
    				};
    			}
    		}
    	})();
    	A.fn.extend({
    		//添加事件
    		on: function(type, fn, data) {
    			var i = this.length;
    			for(; --i >= 0;) {
    				//通过闭包实现对 i 变量保存
    				_on(this[i], type, fn, data);
    			}
    			return this;
    		},
    		//设置或者获取元素样式
    		css: function() {
    			var arg = arguments,
    				len = arg.length;
    			//如果无获取到的元素则返回
    			if(this.length < 1) {
    				return this;
    			}
    			//如果是一个参数
    			if(len === 1) {
    				//如果参数是字符串则返回获取到的第一个元素的样式
    				if(typeof arg[0] === 'string') {
    					//ie 浏览器
    					if(this[0].currentStyle) {
    						return this[0].currentStyle[name];
    					} else {
    						return getComputedStyle(this[0], false)[name];
    					}
    				//如果参数为对象,则为获取到的所有元素设置样式
    				} else if(typeof arg[0] === 'object') {
    					for(var i in arg[0]) {
    						for(var j = this.length - 1; j >= 0; j--) {
    							this[j].style[A.camelCase(i)] = arg[0][i];
    						}
    					}
    				}
    			//如果两个参数
    			} else if(len === 2) {
    				//为获取到的所有元素设置样式
    				for(var j = this.length - 1; j >= 0; j--) {
    					this[j].style[A.camelCase(arg[0])] = arg[1];
    				}
    			}
    			return this;
    		}
    		//设置或者获取元素属性
    		,attr: function() {
    			var arg = arguments,
    				len = arg.length;
    			//如果无获取到的元素则返回
    			if(this.length < 1) {
    				return this;
    			}
    			//如果是一个参数
    			if(len === 1) {
    				//如果参数是字符串则返回获取到的第一个元素的属性值
    				if(typeof arg[0] === 'string') {
    					return this[0].getAttribute(arg[0]);
    				//如果参数为对象则为获取到的所有元素设置属性
    				} else if (typeof arg[0] === 'object') {
    					for(var i in arg[0]) {
    						for(var j = this.length - 1; j >= 0; j--) {
    							this[j].setAttribute(i, arg[0][i]);
    						}
    					}
    				}
    			//如果是两个参数
    			} else if(len === 2) {
    				//为获取到的所有元素设置属性
    				for(var j = this.length - 1; j >= 0; j--) {
    					this[j].setAttribute(arg[0], arg[1]);
    				}
    			}
    			return this;
    		}
    		//获取或者设置元素内容
    		,html: function() {
    			var arg = arguments,
    				len = arg.length;
    			//如果无获取到的元素则返回
    			if(this.length < 1) {
    				return this;
    			}
    			//如果无参数则返回获取到的第一个元素内容
    			if(len === 0) {
    				return this[0].innerHTML;
    			//如果是一个参数,则设置获取到的所有元素内容
    			} else if(len === 1) {
    				for(var i = this.length - 1; i >= 0; i--) {
    					this[i].innerHTML = arg[0];
    				}
    			//如果有两个参数,而且第二个参数值为 true, 则为获取到的所有元素追加内容
    			} else if(len === 2 && arg[1]) {
    				for(var i = this.length - 1; i >= 0; i--) {
    					this[i].innerHTML += arg[0];
    				}
    			}
    			return this;
    		}
    		/*
    		* @name 判断类存在
    		* @param val 类名
    		*/
    		,hasClass: function(val) {
    			//如果无获取到的元素则返回
    			if(!this[0]) {
    				return;
    			}
    			//类名去除首尾空白符
    			var value = A.trim(val);
    			//如果获取到的第一个元素类名包含 val 则返回 true,否则返回 false
    			return this[0].calssName && this[0].calssName.indexOf(value) >= 0 ?  true : false;
    		}
    		/*
    		* @name 添加类
    		* @param val 类名
    		*/
    		,addClass: function(val) {
    			var value = A.trim(val),
    				str = '';
    			//遍历所有获取到的元素
    			for(var i = 0, len = this.length; i < len; i++) {
    				src = this[i].className;
    				//如果元素类名包含添加类则为元素添加类
    				if(!~str.indexOf(value)) {
    					this[i].calssName += ' ' + value;
    				}
    			}
    			return this;
    		}
    		/*
    		* @name 移除类
    		* @param val 类名
    		*/
    		,removeClass: function(val) {
    			var value = A.trim(val),
    				classNameArr,   //将元素类名转化为数组
    				result;         //元素类名最终结果
    			//遍历所有获取到的元素
    			for(var i = 0, len = this.length; i < len; i++) {
    				//如果类名包含删除类
    				if(this[i].calssName && ~this[i].className.indexOf(value)) {
    					//通过空格符将元素类名切割成数组
    					classNameArr = this[i].calssName.split(' ');
    					result = '';
    					//遍历类名
    					for(var j = className.length - 1; j >= 0; j--) {
    						//去除类名首尾空白符
    						classNameArr[j] = A.trim(classNameArr[j]);
    						//如果类名存在并且类名不等于移除类,则保留该类
    						result += classNameArr[j] && classNameArr[j] != value ? '' + classNameArr[j] : '';
    					}
    					//重置元素类名
    					this[i].className = result;
    				}
    			}
    			return this;
    		}
    		/*
    		* @name 插入元素
    		* @param parent 父元素
    		*/
    		,appendTo: function(parent) {
    			var doms = A(parent);
    			//如果获取到父元素
    			if(doms.length) {
    				//遍历父元素
    				for(var j = this.length - 1; j >= 0; j--) {
    					//简化元素克隆(cloneNode)操作,只向第一个父元素中插入子元素
    					doms[0].appendChild(this[j]);
    				}
    			}
    		}
    	});
    	//运动框架单体对象
    	var Tween = {
    		//计时器句柄
    		timer: 0,
    		//运动成员队列
    		queen: [],
    		//运动间隔
    		interval: 16,
    		//缓冲函数
    		easing: {
    			//默认运动缓存算法 匀速运动
    			def: function(time, starValue, changeValue, duration) {
    				return changeValue * time / duration + startValue
    			},
    			//缓慢结束
    			easeQutQuart: function(time, startValue, changeValue, duration) {
    				return -changeValue * ((time = time / duration - 1) * time * time * time - 1) + startValue;
    			}
    		},
    		/*
    		* @name 添加运动成员
    		* @param instance 运动成员
    		*/
    		add: function(instance) {
    			//添加成员
    			this.queen.push(instance);
    			//运动框架
    			this.run();
    		},
    		/*
    		* @name 停止框架运行
    		*/
    		clear: function() {
    			clearInterval(this.timer);
    			this.timer = 0;
    		},
    		/*
    		* @name 运行框架
    		*/
    		run: function() {
    			//如果在运行则返回
    			if(this.timer) return;
    			//重置计时器
    			this.clear();
    			//运行框架
    			this.timer = setInterval(this.loop, this.interval);
    		},
    		/*
    		* @name 运动框架循环方法
    		*/
    		loop: function() {
    			//如果运动队列中没有成员
    			if(Tween.queen.length === 0) {
    				//停止框架运行
    				Tween.clear();
    				//返回
    				return;
    			}
    			//获取当前时间
    			var now = +new Date();
    			//遍历运动成员
    			for(var i = Tween.queen.length - 1; i >= 0; i--) {
    				//获取当前成员
    				var instance = Tween.queen[i];
    				//当前成员已运动的时间
    				instance.passed = now - instance.start;
    				//如果当前成员已运动的时间小于当前成员运动时间
    				if(instance.passed < instance.duration) {
    					//执行当前成员主函数
    					Tween.workFn(instance);
    				} else {
    					//结束当前成员运行
    					Tween.endFn(instance);
    				}
    			}
    		},
    		/*
    		* @name 运行方法
    		* @param instance 运动成员
    		*/
    		workFn: function(instance) {
    			//获取当前成员在当前时刻下的运动进程
    			instance.tween = this.easing[instance.type](instance.passed,
    			instance.from, instance.to - instance.from, instance.duration);
    			//执行主函数
    			this.exec(instance);
    		},
    		/*
    		* @name 结束方法
    		* @param instance 运动成员
    		*/
    		endFn: function(instance) {
    			instance.pass = instance.duration;
    			instance.tween = instance.to;
    			this.exec(instance);
    			this.distory(instance);
    		},
    		/*
    		* @name 执行主函数
    		* @param instance 运动成员
    		*/
    		exec: function(instance) {
    			try{
    				//执行当前成员主函数
    				instance.main(instance.dom)
    			} catch(e) {}
    		},
    		/*
    		* @name 注销运动成员
    		* @param instance 运动成员
    		*/
    		distory: function(instance) {
    			//结束当前成员
    			instance.end();
    			//在运动成员队列中删除该成员
    			this.queen.splice(this.queen.indexOf(instance), 1);
    			//删除成员中的每一个属性
    			for(var i in instance) {
    				delete instance[i];
    			}
            }
        }
        /**
         * @name 获取当前成员在运动成员中的位置
         * @param  instance 运动成员
        */
        Tween.queen.indexOf = function() {
            var that = this;
            //如果有该方法则返回,如果没有则创建一个
            return Tween.queen.indexOf || function(instance) {
                //遍历每个成员
                for(var i = 0, len = that.length; i < len; i++) {
                    //如果该成员是需求成员,则返回该成员在队列中的位置
                    if(that[i] === instance) {
                        return i;
                    }
                }
                //否则返回 -1 表示不存在
                return -1;
            }
        }();
        //A.fn 对象拓展方法
        A.fn.extend({
            /**
             * @name  动画模块
             * @param obj 动画成员对象
             */
            animate: function(obj) {
                    //适配运动对象
                var obj = A.extend({
                    duration: 400,       //默认运行时间
                    type: 'def',         //默认动画缓存函数
                    from: 0,             //开始点
                    to: 1,               //结束点
                    start: +new Date(),  //开始时间
                    dom: this,           //当前元素
                    main: function() {}, //运行主函数
                    end: function() {}   //结束函数
                }, obj);
                //像运动框架中载入运动成员对象
                Tween.add(obj);
            }
        });
        /**
         * @name 避免框架别名冲突(主要作用与页面中引入多核框架)
         * @param library 其他框架
         */
        A.noConflict = function(library) {
            //如果传其他框架
            if(!library) {
                //为 library 绑定 $ 别名
                window.$ = library;
            } else {
                //否则删除 $ 别名
                window.$ = null;
                delete window.$;
            }
            //返回 A 对象
            return A;
        }
        //为全局对象绑定 A 框架,并绑定别名 $
        window.$ = window.A = A;
    })(window);
    




    展开全文
  • 本篇文章参考书籍《JavaScript设计模式》–张容铭 文章目录前言MVC 模式总个小结 前言   嗨!各位乡绅,上一节字符串模板,有没有很崩溃,模板引擎写起来是有点费劲,但是思路比较简单,慢慢梳理就好。   本节...

    本篇文章参考书籍《JavaScript设计模式》–张容铭

    前言

      同学们, MVC 模式有没有搞搞清楚,可能有点感觉了,但还是不太熟悉对吧,没关系, MVC 之所以被大家传的神乎其神,其实是开启了一种新模式,这种模式,简化了我们开发过程中的一些繁琐操作,让我们可以把精力更多的放在业务上,而 MVC 本身其实还是可以优化的。

      从上一节的代码来看, MVC 模式在添加新功能的时候,获取的数据改变了,我们不仅要修改控制器层,还要修改视图层,这对于复杂需求修改成本是很高的。

      这对于这一问题,衍生出了今天的主题, MVP 模式

    MVP 模式

      MVP 即模型(Model)—视图(View)—管理器(Presenter)

       View 层不直接引用 Model 层内的数据,而是通过 Presenter 层实现对 Model 层内的数据访问。即所有层次的交互都发生在 Presenter 层中。

      我们知道,在 MVC 模式中,视图层常常因为要渲染页面而直接引用数据层内的数据,对于这一切,控制器是不知情的,那就会导致,在新增需求的时候,不仅要修改视图层,还要修改控制器。

      在 MVP 模式中,我们将视图层和数据层解耦,统一交给控制器层管理,这样视图层只负责创建视图模板,数据层只负责管理数据,功能就独立了出来,而剩下的管理数据, UI 视图创建,交互逻辑,动画特效等都交给控制层,这样控制层的功能多了,晋升成为了管理层, C 就变成了 P

      基本原理我们清楚了,接下来就是如何实现,通过上面描述,我们知道数据层的变化不需要太多,主要是视图层不一样了,我们首先先创建一个单体对象。

    // MVP 单体对象
    ~(function(window) {
        //MVP 构造函数
        var MVP = function() {};
        //数据层
        MVP.model = function() {};
        //视图层
        MVP.view = function() {};
        //管理层
        MVP.presenter = function() {};
        //MVP入口
        MVP.init= function() {};
        //暴露 MVP 对象,这样可在外部访问 MVP
        window.MVP = MVP;
    }) (window)
    

      数据层变化不大,代码如下:

    //数据层与 MVC 相似
    MVP.model = function() {
        var M = {};
        M.data = {}
        M.conf = {}
        return {
            getData: function(m) {
                return M.data[m];
            },
            /**
             * 设置数据
             * @param   m   模块名称
             * @param   v   模块数据
             */
            setData: function(m, v) {
                M.data[m] = v;
                return v;
            },
            getConf: function(c) {
                return M.conf[c];
            },
            /**
             * 设置配置
             * @param   c   配置项名称
             * @param   v   配置项值
             */
            setConf: function(c, v) {
                M.conf[c] = v;
                return v;
            }
        }
    } ()
    

      接下来是改革重点,视图层修改,比如说我们做一个如下的导航条。
    在这里插入图片描述
      为了在管理层中渲染并创建视图,每次渲染都需要视图层提供一个导航视图模板。

    var tpl = [
        '<li class="{#mnode#} {#choose#} {#last#}" data-mode="{#mode#}">',
            '<a id="nav_{#mode#}" class="nav_{#mode#}" href="{#url#}" title="{#text#}">',
                '<i class="nav-icon-{#mnode#}"></i>',
                '<span>{#text#}</span>',
            '</a>',
        '</li>',
    ].join('');
    

      又是这种头疼的字符串模板,写起来超级费劲,各位估计也看烦了。我们可以借鉴一下 Zen Coding 那样,快速创建模板,比如上面的模板转化成 Zen Coding 模式如下:

    'li.@mode @choose @last[data-mode=@mode]>a#nav_@mode.nav-@mode[href=@url title=@text]>i.nav-icon-@mode+span{@text}'
    

      上面这种写法,稍加观察就能理解,对于这种写法,我们需要对视图层做如下处理:

    //视图层
    MVP.view = MVP.view = function() {
        return function(str) {
            //将参数字符串转化成期望模板
            return html2canvas;
        }
    } ();
    

      接下来我们要解析字符串并创建视图,对于参数字符串 str ,我们做的第一步是分层,也就是说要确认每一个元素之间的层级关系,我们发现 ‘>’ 表示后面的元素是前面的元素的子元素,而 ‘+’ 表示前面元素与后面元素是兄弟元素。由于兄弟元素处在元素树中的同一层级上,因此我们要先做 ‘>’ 不同层级处理后作 ‘+’ 同一层级处理,最后针对每一个元素做处理并按层级顺序拼接成期望模板字符串。

    MVP.view = function() {
        //子元素或者兄弟元素替换模板
        var REPLACEKEY = '__REPLACEKEY__';
        //获取完整元素模板
        function getHTML(str, replacePos) {}
        /**
         * 数组迭代器
         * @param arr  数组
         * @param fn   回调函数
         */
        function eachArray(arr, fn) {
            //遍历数组
            for (let i = 0, len = arr.length; i < len; i++) {
                //将索引值,索引对应值,数组长度传入回调函数中并执行
                fn(i, arr[i], len);
            }
        }
        /**
         * 替换兄弟元素模板或者子元素模板
         * @param str   原始字符串
         * @param rep   兄弟元素模板或者子模版
         */
        function formateItem(str, rep) {
            //用对应元素字符串替换兄弟元素模板或者子元素模板
            return str.replace(new RegExp(REPLACEKEY, 'g'), rep);
        }
        //模板解析器
        return function(str) {
                //模板层级数组
            var part = str
            //去除首位空白符
            .replace(/^\s+|\s+$/g, '')
            //去除 > 两端空白符
            .replace(/^\s+(>)\s+/g, '$1')
            //以 > 分组
            .split('>'),
                //模块视图根模板
                html = REPLACEKEY,
                //同层元素
                item,
                //同级元素模板
                nodeTpl;
            //遍历每组元素
            eachArray(part, function(partIndex, partValue, partLen) {
                //为同级元素分组
                item = partValue.split('+');
                //设为同级元素初始模板
                nodeTpl = REPLACEKEY;
                //遍历同级每一个元素
                eachArray(item, function(itemIndex, itemValue, itemLen) {
                    /**
                     * 用渲染元素得到的模板去渲染同级元素模板,此处简化逻辑操作
                     * 如果 itemIndex (同级元素索引) 对应元素不是最后一个 则做兄弟元素处理
                     * 否则,如果 partIndex (同级索引) 对应的层级不是最后一层 则作为父层级处理
                     * (该层级有子层级,即该元素是父元素)
                     * 否则,该元素无兄弟元素无子元素
                     */
                    nodeTpl = formateItem(nodeTpl, getHTML(itemValue, itemIndex === itemLen - 1 ? (partIndex === partLen - 1 ? '' : 'in') : 'add'));
                });
                //用渲染子层级得到的模板去渲染父层级模板
                html = formateItem(html, nodeTpl);
            })
            //返回期望视图模板
            return html;
        }
    } ();
    

      最后我们要做的就是对一个元素模板的渲染,即 getHTML 方法。 getHTML 方法比较复杂,首先要分清该元素是否拥有子元素,或者拥有兄弟元素,或是最后一个叶子元素 3 中情况。并针对三种情况分别处理。

      然后要将元素补成完整元素, 如 div 要转化成 <div></div>

      接下来要对元素的特殊属性 id # 标识)或 class . 标识)做处理。

      然后在处理元素的其他属性( [] 内的用空格分割的属性组)。

      最后要将可替换内容标识( @ 标识)替换成代码库中模板渲染方法中可嗅探内容标识形式(比如我们引用 A 框架中 formateString 方法的可嗅探内容标识为 {##} )。

    /**
     * 获取完整元素模板
     * @param  str    元素字符串
     * @param  type   元素类型
     */
    function getHTML(str, type) {
        //简化实现,只处理字符串中第一个{}里面的内容
        return str
            .replace(/^(\w+)([^\{\}]*)?(\{([@\w]+)\})?(.*?)$/, function(match, $1, $2, $3, $4, $5) {
                $2 = $2 || '';    //元素属性参数容错处理
                $3 = $3 || '';    //(元素内容)参数容错处理
                $4 = $4 || '';    //元素内容参数容错处理
                //去除元素内容后面添加的元素属性中的{}内容
                //以 str=div 为例,如果div元素有子元素则表示成<div>__REPLACEKEY__</div>
                //如果div有兄弟元素则表示成<div></div>__REPLACEKEY__,否则表示成<div></div>
                $5 = $5.replace(/\{([@\w]+)\}/g, '');
                return type === 'in' ?
                    '<' + $1 + $2 + $5 + '>' + $4 + REPLACEKEY + '</' + $1 + '>' : 
                type === 'add' ?
                    '<' + $1 + $2 + $5 + '>' + $4 + '</' + $1 + '>' + REPLACEKEY :
                    '<' + $1 + $2 + $5 + '>' + $4 + '</' + $1 + '>' 
            })
            //处理特殊标识#--id属性
            .replace(/#([@\-\w]+)/g, ' id="$1"')
            //处理特殊标识.--class属性
            .replace(/\.([@\-\s\w]+)/g, ' class="$1"')
            //处理其他属性组
            .replace(/\[(.+)\]/g, function(match, key) {
                    //元素属性组
                var a = key
                        //过滤其中引号
                        .replace(/'|"/g, '')
                        //以空格分组
                        .split(' '),
                    //属性模板字符串
                    h = '';
                //遍历属性组
                for(var j = 0, len = a.length; j < len; j++) {
                    //处理并拼接每一个属性
                    h += ' ' + a[j].replace(/=(.*)/g, '="$1"');
                }
                //返回属性组模板字符串
                return h;
            })
            //处理可替换内容,可根据不同模板渲染引擎自由处理
            .replace(/@(\W+)/g, '(#$1#)');
    }
    

      有了模板引擎,我们在管理器中实现就容易多了,为了使管理器更适合我们的 MVP 模式,只要对管理器稍加改动,添加管理器执行方法 init ,这样方便在任何时候执行我们的管理器,不过总体来说还是和 MVC 中的控制器很类似。

    //管理器层
    MVP.presenter = function() {
        var V = MVP.view;
        var M = MVP.model;
        var C = {};
        return {
            //执行方法
            init: function() {
                //遍历内部管理器
                for(var i in C) {
                    //执行所有管理器内部逻辑
                    C[i] && C[i](M, V, i);
                }
            }
        };
    } ();
    

      完整的 MVP 对象创建出来了,接下来我们创建一个导航,首先为管理器添加导航管理器逻辑。

    var C = {
        /**
         * 导航管理器
         * @param   M   数据层对象
         * @param   V   视图层对象
         */
        nav: function(M, V) {
            //处理导航渲染数据
            data[0].choose = 'choose';
            data[data.length - 1].last = 'last';
            //获取导航渲染模板
            var tpl = V('li.@mode @choose @last[data-mode=@mode]>a#nav_@mode.nav-@mode[href=@url title=@text]>i.nav-icon-@mode+span{@text}');
            $
            //创建导航容器
            .create('ul', {
                'class': 'navigation',
                'id': 'nav'
            })
            //插入导航视图
            .html(
                //渲染导航视图
                A.formateString(tpl, data)
            )
            //导航模块添加到页面中
            .appendTo('#container');
            //其他交互逻辑与动画逻辑
            //...
        }
    };
    

      假设我们现在可以从后端获取导航模块数据并已经通过 setData 方法设置在数据层中。

    M.data = {
        //导航模块渲染数据
        nav: [
            {
                text: '新闻头条',
                mode: 'news',
                url: 'http://www.example.com/01'
            },
            {
                text: '最新电影',
                mode: 'movie',
                url: 'http://www.example.com/02'
            },
            {
                text: '热门游戏',
                mode: 'game',
                url: 'http://www.example.com/03'
            },
            {
                text: '进入特价',
                mode: 'price',
                url: 'http://www.example.com/04'
            },
        ]
    }
    

      万事俱备,只欠执行了,接下来为 MVP 对象创建一个快捷执行方法 init

    //MVP 入口
    MVP.init = function() {
        this.presenter.init();
    }
    

      等到页面加载完毕后我们就可以渲染并创建导航模块了。

    window.onload = function() {
        //执行管理器
        MVP.init();
    }
    

      是不是比 MVC 方便多了,有什么需求增加只要修改响应的管理器就可以了。不过现在模块化开发是主流,一个模块的开发是依赖 MVP 对象实现的,因此我们要将 MVP 封装在模块内。

    F.module('lib/MVP', function() {
        //MVP构造函数
        var MVP = function() {};
        //MVP实现
        //...
        return MVP;
    });
    

      有了 MVP 对象模块,我们就可以在其他模块中引用 MVP 模块了,不过目前为止我们还不能使用 MVP 为管理器添加其他控制器模块。所以我们完成 MVP 构造函数,实现通过 MVP (模块名称,模块管理器,服务器端获取的数据) 的方式添加模块。

    //MVP构造函数
    var MVP = function(modName, pst, data) {
        //在数据层中添加 modName 渲染数据模块
        MVP.model.setData(modName, data);
        //在管理器层中添加 modName 管理器模块
        MVP.presenter.add(modName, pst);
    };
    

      从上面代码可以看出 MVP 构造函数做了两件事,首先为数据层添加模块数据,然后为管理器曾添加管理器模块,我们已经在数据层 model 中实现了 setData 方法,所以现在只剩下管理器层中的 add 方法有待实现。

    //管理器层
    MVP.presenter = function() {
        //...
        return {
            /**
             * 为管理器添加模块
             * @param  modName   模块名称
             * @param  pst       模块管理器
             */
            add: function(modName, pst) {
                C[modName] = pst;
                return this;
            }
        };
    } ();
    

      管理器的 add 方法允许我们以管理器名称 + 模块管理器的形式在管理器对象层中添加模块管理器,这样我们在外部就可以自由的添加模块了。比如创建一个简单的网址模块。
    在这里插入图片描述
      我们可以在外部模块中创建网址模块。

    //网址模块
    F.module(['lib/MVP', 'lib/A'], function(MVP, $) {
        //页面加载完成执行 参考 A 框架(可以在上一节的代码中查看,或者从我上传的资源中查看)
        $(function() {
            //为 MVP 对象添加一个网址模块
            MVP(
                //模块名称
                'sites',
                /**
                 * 模块控制器
                 * @param   M        数据对象层引用
                 * @param   V        视图对象层引用
                 * @param   modName  模块名称
                 */
                function(M, V, modName) {
                    //渲染模板<li><a href="#">{#text#}</a></li>
                    var tpl = V('li>a[href="#"]{@text}');
                    $
                    //创建网址模块容器
                    .create('ul', {
                        'class': 'store-nav',
                        'id': modName
                    })
                    //向网址模块容器中插入网址模块视图
                    .html(
                        //创建网址模块视图
                        $.formateString(tpl, M.getData(modName))
                    )
                    //插入页面中
                    .appendTo('#container');
                    //其他交互与特效...
                },
                //模块数据
                [
                    '聚划算',
                    '1号店',
                    '九块邮',
                    '优购网',
                    '爱淘宝',
                    '1折网',
                ]
            );
        });
    });
    

      模块创建完毕我们就可以执行所有模块控制器了。

    $(function() {
    	MVP.init();
    })
    

      当然对于模块间的通信我们还可以通过观察者模式来实现。

    总个小结

       MVP MVC 的最大区别就是将视图层与数据层完全解耦,使得对视图层的修改不会影响到数据层,数据层内的数据改动又不会影响到视图层。在管理器中对数据或者视图灵活的调用就可使数据层内的数据与视图层内的视图得到高效复用。

      因此, MVP 模式可以实现一个管理器,可以调用多个数据,或者创建多种视图,而且不受限制。



    展开全文
  • 代码大全中文版

    2007-08-21 14:12:13
    繁杂的日常工作往往使程序员们穷于应付而无暇阅读浩如烟海的书籍与资料,本书丰富而翔实的第一手资料将弥补这一缺憾,使你对软件开发的策略作出正确决策而不致陷入旷日持久的消耗战中。 通用的概念 无论你的...
  • 有关操作系统的书籍资料可以找到很多,但是关注如何帮助读者实现一个试验性操作系统的书籍却不多见,本书便是从一个简单的引导扇区开始,讲述一个操作系统成长的故事,以作读者参考。 本书面向实践,通过具体...
  • 有关操作系统的书籍资料可以找到很多,但是关注如何帮助读者实现一个试验性操作系统的书籍却不多见,本书便是从一个简单的引导扇区开始,讲述一个操作系统成长的故事,以作读者参考。 本书面向实践,通过具体...
  • EJB中JNDI使用源码例子 1个目标文件,JNDI使用例子,有源代码,可以下载参考,JNDI使用,初始化Context,它是连接JNDI树起始点,查找你要对象,打印找到对象,关闭Context…… ftp文件传输 2个目标文件...
  • 我们在JAVA开发过程中,往往需要在程序一启动时就执行一些操作,例如... 这是了监听器(Listener)实现参考书籍《轻量级Java EE》企业应用实战(第4版) 李刚编著 package com.cn.bwq.init; import java...
  • costas_loop代码

    2012-10-08 14:40:33
    %通过噪声带宽可以求出环路滤波器参数,(通常限定在3阶环路以下), 具体方法可以参考书籍: % GPS原理及应用 第一, 二版, 还有一本是fundamentals of global positonging system receiver % 这两本书比较适合初学者...
  • 对应的书籍资料见: 深入解析SQL Server 2008(微软SQL Server开发团队必读之作) 基本信息 原书名: Microsoft SQL Server 2008 Internals 原出版社: Microsoft Press 作者: (美)Kalen Delaney Paul S. Randal ...
  • iPhone开发秘籍(第2版)--源代码

    热门讨论 2012-12-11 13:51:22
    对应的书籍资料见: iPhone开发秘籍:第2版(iphone开发必备佳作,在第一版的基础上进行了全面修订和大量扩充) 基本信息 原书名: The iPhone Developer's Cookbook: Building Applications with the iPhone 3.0 ...
  • C++程序设计语言(特别版)--源代码

    热门讨论 2012-04-23 07:33:51
    提供是书中代码,非课后练习源代码。 本版本是高清版,是第1版第18次印刷,是书签最全最好版本。 基本信息 原书名: The C++ Programming Language, Special Edition 原出版社: Addison Wesley 作者: ...
  • 对应的书籍资料见: Android开发入门教程(汇集Android社区智慧图) 基本信息 原书名: Beginning Android 2 原出版社: Apress 作者: (美)Mark L.Murphy 译者: 李雪飞 吴明晖 丛书名: 图灵程序设计丛书 移动开发...
  • 提供是本书课后习题源代码,也就是《C++程序设计语言(特别版)题解》代码。非书中源代码。 本版本是高清版,是第1版第18次印刷,是书签最全最好版本。 基本信息 原书名: The C++ Programming Language...
  • 精通qt4编程(源代码

    热门讨论 2010-03-17 19:10:40
    当我们准备编写自己的应用软件时,却发现图书市场上没有一本关于Qt 4的书籍,仅有的只是一些关于Qt 3的资料。由于Qt 3到Qt 4的变化很大,甚至源代码都不兼容,所以这些资料的参考价值并不是太大。于是,我们通过阅读...
  • 然而,如果不经训练就贸然使用,C++ 也会导致不可理解,难以维护,无法扩展,低效率,错误百出的代码。 本书目的在于引导你如何高效使用 C++。我假设你已经熟悉了作为语言 C++ 并有使用它一些经验。...
  • CruiseYoung提供带有详细书签电子书籍目录 http://blog.csdn.net/fksec/article/details/7888251 SQL Server 2008实战(SQL Server程序员和DBA不可或缺权威参考手册) 基本信息 原书名: SQL Server 2008 ...
  • 对应的书籍资料见: Oracle SQL高级编程(资深Oracle专家力作,OakTable团队推荐) 基本信息 原书名: Pro Oracle SQL 原出版社: Apress 作者: (美)Karen Morton Kerry Osborne Robyn Sands Riyaj Shamsudeen Jared...
  • 对应的书籍资料见: 深入解析SQL Server 2008(微软SQL Server开发团队必读之作) 基本信息 原书名: Microsoft SQL Server 2008 Internals 原出版社: Microsoft Press 作者: (美)Kalen Delaney Paul S. Randal ...
  • 本书作者根据自己学习C++亲身体会及多年教学经验,简单例子和简练叙述讲解C++编程,别具特色。 全书共分十八章,内容涉及对象演化、数据抽象、隐藏实现、初始化与清除、函数重载与缺省参数、输入输出流...
  • 2003年,kochan编写了《programming in objective-c》(sams,2003),之后编写了另一本与mac有关的书籍《befinning applescript》(wiley,2004)。 目录 1引言 1.1本书内容 1.2本书组织方式 1.3支持 1.4致谢...
  • tips[11] = '书籍是作者思想渣滓,通过咀嚼作者渣滓,体会作者嘴里味道,否则永远是在吃泔水。'; tips[12] = '简单抄袭,必死无疑。'; tips[13] = '网站三个要素中,内容永远最重要,功能其次,表现在最后。...
  • 设计模式前须知

    2017-06-08 14:43:18
    设计模式前须知1: 为什么有人说设计模式故弄玄虚大部分程序员编写程序可以分为三类(应用程序、工具包 、框架),使用设计模式目的是提高代码的可复用性和可扩展性(灵活性), 但是设计模式在这三类软件中所...
  • CruiseYoung提供带有详细书签电子书籍目录 http://blog.csdn.net/fksec/article/details/7888251 特别说明:该版本是目前网络上最全版本:修正了所有缺页和错页问题。 Oracle Database 11g完全参考手册 ...

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 226
精华内容 90
关键字:

参考的书籍用什么代码