MVC是一种设计模式 它将应用划分为三个部分: 数据,展现和用户交互
一个事件的发生:
1 用户和应用产生交互
2 控制器的事件处理器被触发
3 控制器从模型中请求数据,并交给视图
4 视图数据呈现给用户
模型:
模型用来存放应用的所有数据对象
模型不必知晓视图和控制器的细节,模型只需要包含数据以及直接和这些数据相关的逻辑,模型是最应该从应用中解耦出来的部分。
当控制器从服务器抓取数据或创建新的纪录时,它就将数据包装成模型实例,也就是说数据是面向对象的,任何定义在这个数据模型上的函数或逻辑都可以被直接调用。
视图: 视图层是 呈现给用户的,用户与之产生交互。在JavaScript应用中,视图大多数是由HTML,CSS和JavaScript模板组成。除了简单的条件语句外,视图不应该包含任何其他逻辑。
将逻辑混入视图之中是编程的大忌。
这并不是说MVC不允许包含视觉呈现相关的逻辑,只要这部分逻辑没有定义在视图之内即可。
我们将视觉呈现逻辑归类为“视图助手”:和视图有关的独立的小型工具函数。
控制器: 控制器是模型和视图之间的纽带。控制器从视图获得事件和输入,对它们进行处理,并相应的更新视图。当页面加载时,控制器会给视图添加事件监听,比如监听表单提交或按钮点击。然后当用户和应用产生交互时,控制器重的事件触发器就开始工作了。
JavaScript中的类
当使用new关键字来调用构造函数时,执行上下文从全局对象window变成一个空的上下文,这个上下文代表一个新生成的实例。
默认情况下,如果你的构造函数中没有返回任何内容,就会返回this——当前的上下文。
创建类
创建自己的类模拟库
1 var Class = function(){ 2 var klass = function(){ 3 klass.init.apply(this, arguments); 4 } 5 klass.prototype.init() = function(){}; 6 return klass; 7 }; 8 var Person = new Class; 9 10 Person.prototype.init = function() {11 //基于Person的初始化内容12 }13 14 var person = new Person;
给类的prototype起一个别名fn,写起来也方便。
1 Person.fn = Person.prototype;2 Person.fn.run = function(){ /*......*/};
给类库添加方法
我们采用另外一种方法添加属性
1 klass.extend = function(obj){ 2 var extended = obj.extended; 3 for( var i in obj){ 4 klass[i] = obj[i]; 5 } 6 if( extended ) 7 extended(klass); 8 }; 9 10 klass.include = function(obj){11 var included = obj.included;12 for( var i in obj){13 klass[i].fn = obj.[i];14 }15 if( included )16 included(klass);17 }18 19 var Person = new Class;20 Person.extend({21 find: function(id){ /*...*/},22 exists: function(id){ /*....*/}23 });24 var person = Person.find(1);
同样,这里支持回调,即将属性传入后会触发这个回调:
1 Person.extend({2 extended: funtcion(klass) {3 console.log(klass,"was extended");4 }5 });
这种写法之美在于它可以支持模块。
1 var ORMModule = {2 save: funtction() {3 //共享的函数4 }5 };6 Person.include(ORMModule);7 Asset.include(ORMModule);
原型
JavaScript是基于原型的编程语言,原型用来区别类和实例, 原型是一个“模板”对象,它上面的属性被用做初始化一个新对象。任何对象都可以作为另一个对象的原型对象,以此来共享属性。可以理解为某种形式的继承。
当你读取一个对象的属性时,JavaScript首先会在本地对象中查找属性,如果没有找到,会在对象的原型中查找,若还未找到则会继续查找原型的原型,知道找到Object.prototype。如果找到就返回值,否则返回undefined。
换句话说,如果你给Array.prototype添加了属性,那么所有的JavaScript数组都有了这些属性。
给类库添加继承
现在来给我们定义的“类”库添加继承,
1 var Class = function(parent){ 2 var klass = function(){ 3 klass.init.apply(this, arguments); 4 } 5 6 if(parent){ 7 var subclass = function () {}; 8 subclass.prototype = parent.prototype; 9 klass.prototype = new subclass;10 }11 12 klass.prototype.init() = function(){};13 klass.fn = klass.prototype;14 klass.fn.parent = klass;15 klass._super = klass.__proto__;16 17 /*18 extend.....19 include......20 */21 22 return klass;23 };
将parent传入Class构造函数,所有的子类则会共享一个原型。
函数调用
JavaScript中,函数也是一个对象,然后不同的是,它是可以调用的,函数内上下文取决于调用它的位置。
除了使用方括号调用函数外,还可以用apply和call调用。
function.apply(this,[1,2,3]);
function.call(this,1,2,3);
function(1,2,3){}
JavaScript中允许更换上下文是为了共享状态,尤其是在事件回调中。
若要访问原始上下文,则可将原始上下文的this值存入一个局部变量中。
使用call和apply还有一个功能,就是将调用委托给另一个调用
即在获得参数args=jQuery.makeArray(arguments);和上下文this时, 可以调用其他部分功能相同的函数实现同样功能function.apply(this,args);
其中arguments并不是真正的数组,需要通过jQuery的makeArray转换得到可以使用的数组。
控制类库的作用域
1 var Class = function(parent){ 2 var klass = function(){ 3 this.init.apply(this, arguments); 4 }; 5 klass.prototype.init = function(){}; 6 klass.fn = klass.prototype; 7 // 添加一个proxy 函数 8 klass.proxy = function(func){ 9 var self = this;10 return(function(){11 return func.apply(self, arguments);12 });13 }14 // 在实例中也添加这个函数15 klass.fn.proxy = klass.proxy;16 return klass;17 };
ECMAScript中加入了bind()函数用以控制调用的作用域
有的浏览器不支持bind,所以我们也可以自己手动实现bind()
1 if(!Function.prototype.bind){ 2 Function.prototype.bind = function(obj) { 3 var slice = [].slice; 4 args = slice.call(arguments,1), 5 self = this, 6 nop = function() {}, 7 bound = function() { 8 return self.apply( this instanceof nop ? this:(obj || {}), 9 args.concat(slice.call(arguments)));10 };11 nop.prototype = self.prototype;12 bound.prototype = new nop;13 return bound;14 };15 }
推荐es5-shim
添加私有函数
很多开发者习惯在私有属性下面加_, 便于区分。但是方法太丑陋。。。
我们可以利用JavaScript匿名函数来创建私有作用域。
1 var Person = function(){};2 (function{3 var findById = function(){ /*....*/};4 5 Person.find = function(id) {6 if( typeof id = = "integer")7 return findById(id);8 };9 })();
类库的方法:
介绍了HJS和Spine。