zoukankan      html  css  js  c++  java
  • Backbone源码解析(三):Collection模块

    Collection模块式是对分散在项目中model的收集,他可以存储所有的model,构成一个集合,并且通过自身的方法统一操作model。Collection模块包装着若干对象,对象本身不具有一些方法,而它可以应用集合自身方法进行管理。它自身定义了一些方法,同时继承依赖库的一些方法,使得它能够对自身存储的model进行统一操作管理。这篇文章将对collection进行分析,源码写在下面:

    //构造函数
    e.Collection = function(a, b) {
    	b || (b = {});
    	//b.comparator是指的排序函数,当调用sort方法是,模型会根据comparator方法排序。
    	if (b.comparator) this.comparator = b.comparator;
    	//binbAll是underscore的方法,查阅源码可以看到bindAll方法是改变_onModelEvent和_removeReference中this的指向,在外部调用用到它们时this始终指向Collection
    	f.bindAll(this, "_onModelEvent", "_removeReference");
    	//重置模型集合,确保环境是干净的。
    	this._reset();
    	//如果传入了参数a(模型对象) 那么就把a储存起来。
    	a && this.reset(a, {
    		silent: !0
    	});
    	//执行初始化函数
    	this.initialize.apply(this, arguments)
    };
    //扩展Collection的原型
    f.extend(e.Collection.prototype, e.Events, {
    	//model在这里是一个Collection的属性,默认值是e.Model,通过它我们可以分类别储存模型。需要时我们可以覆盖它。
    	model: e.Model,
    	//初始化函数
    	initialize: function() {},
    	//序列化所有模型的值,可以得到该集合下所有model序列化的数组。
    	toJSON: function() {
    		return this.map(function(a) {
    			return a.toJSON();
    		})
    	},
    	//添加一个模型。可以看到add方法里面引用的是私有方法_add。
    	add: function(a, b) {
    		if (f.isArray(a)) for (var c = 0,
    		d = a.length; c < d; c++) this._add(a[c], b);
    		else this._add(a, b);
    		return this
    	},
    	//移除一个模型对象。内部也是调用私有方法_remove
    	remove: function(a, b) {
    		if (f.isArray(a)) for (var c = 0,
    		d = a.length; c < d; c++) this._remove(a[c], b);
    		else this._remove(a, b);
    		return this
    	},
    	//通过id获取模型对象 参数a(模型)
    	get: function(a) {
    		if (a == null) return null;
    		return this._byId[a.id != null ? a.id: a]
    	},
    	//通过Cid获取模型对象 传入参数a(模型)
    	getByCid: function(a) {
    		return a && this._byCid[a.cid || a]
    	},
    	//通过索引查找模型
    	at: function(a) {
    		return this.models[a]
    	},
    	//对模型排序 如果设置了comparator,那么就会根据comparator对collection中的models进行排序
    	sort: function(a) {
    		a || (a = {});
    		if (!this.comparator) throw Error("Cannot sort a set without a comparator");
    		this.models = this.sortBy(this.comparator);
    		//如果设置了silent属性为true则不会出发reset。在Event中我们讲到过trigger方法。
    		a.silent || this.trigger("reset", this, a);
    		return this
    	},
    	//获取所有模型中摸个属性的集合,生成一个数组。参数a(属性名称)
    	pluck: function(a) {
    		//通过引用underscore的map方法返回数组。this.models中存储的时所有的模型
    		return f.map(this.models,
    		function(b) {
    			return b.get(a)
    		})
    	},
    	//重置集合内的模型对象,this.models会被清空。a 非必要参数,重置后传入新的模型对象
    	reset: function(a, b) {
    		a || (a = []);
    		b || (b = {});
    		this.each(this._removeReference);
    		this._reset();
    		this.add(a, {
    			silent: !0
    		});
    		b.silent || this.trigger("reset", this, b);
    		return this
    	},
    	//从服务器端获取最新的模型
    	fetch: function(a) {
    		a || (a = {});
    		var b = this,
    		c = a.success;
    		a.success = function(d, f, e) {
    			b[a.add ? "add": "reset"](b.parse(d, e), a);
    			c && c(b, d)
    		};
    		a.error = i(a.error, b, a);
    		return (this.sync || e.sync).call(this, "read", this, a)
    	},
    	//创建一个新的模型,并将其保存到服务器上去
    	create: function(a, b) {
    		var c = this;
    		b || (b = {});
    		//创建或者拷贝一个新的模型
    		a = this._prepareModel(a, b);
    		if (!a) return ! 1;
    		var d = b.success;
    		b.success = function(a, e, f) {
    			c.add(a, b);
    			d && d(a, e, f)
    		};
    		//调用模型的保存方法,上传到服务器
    		a.save(null, b);
    		return a
    	},
    	parse: function(a) {
    		return a
    	},
    	// 返回一个封装的对象. 在封装的对象上调用方法会返回封装的对象本身, 直到 value 方法调用为止.
    	chain: function() {
    		return f(this.models).chain();
    	},
    	//重置Collection中所有的模型
    	_reset: function() {
    		this.length = 0;
    		this.models = [];
    		this._byId = {};
    		this._byCid = {}
    	},
    	//返回一个当前类型的模型
    	_prepareModel: function(a, b) {
    		//首先判断a是否继承e.Model,
    		if (a instanceof e.Model) {
    			//将a的collection指向collection
    			if (!a.collection) a.collection = this
    		//若a不继承Collection对象中的mode对象则创建该类型的model
    		} else {
    			var c = a;
    			a = new this.model(c, {
    				collection: this
    			});
    			a.validate && !a._performValidation(c, b) && (a = !1)
    		}
    		return a
    	},
    	//这个是内部私有的方法,不直接给外部使用。该方法是向collection集合中添加一个新的模型
    	_add: function(a, b) {
    		b || (b = {});
    		//确定a是该model子集
    		a = this._prepareModel(a, b);
    		if (!a) return ! 1;
    		var c = this.getByCid(a);
    		if (c) throw Error(["Can't add the same model to a set twice", c.id]);
    		this._byId[a.id] = a;
    		this._byCid[a.cid] = a;
    		this.models.splice(b.at != null ? b.at: this.comparator ? this.sortedIndex(a, this.comparator) : this.length, 0, a);
    		/*给a绑定all事件,在Event中all是个特殊事件,表示所有正对model操作的行为都会触发对应的绑定事件。在this._onModelEvent中包含了add remove destroy等模型事件
    		  我们在这里把所有事件都交给_onModelEvent处理,在里面分别处理。*/
    		a.bind("all", this._onModelEvent);
    		this.length++;
    		b.silent || a.trigger("add", a, this, b);
    		return a
    	},
    	//移除一个模型
    	_remove: function(a, b) {
    		b || (b = {});
    		a = this.getByCid(a) || this.get(a);
    		if (!a) return null;
    		delete this._byId[a.id];
    		delete this._byCid[a.cid];
    		this.models.splice(this.indexOf(a), 1);
    		this.length--;
    		b.silent || a.trigger("remove", a, this, b);
    		this._removeReference(a);
    		return a
    	},
    	//私有方法,供remove方法调用,承担remove的执行责任。
    	_removeReference: function(a) {
    		this == a.collection && delete a.collection;
    		//解绑all特殊事件
    		a.unbind("all", this._onModelEvent)
    	},
    	/*
    真正将model和collection关联起来的是该方法。在这里设计者很聪明,在前面的add方法中,通过bind函数将所有触发model改变的事件转移到该方法中。使得我们能对所有事件进行分别处理。每一个触发model改变(remove,add,destroy)的事件都会相应地在collection中有对应的行为。该方法是作用是指当model的某个事件触发的时候,colletion中采取对应的措施操作该模型对象。比如我们在外面把modelA摧毁,那么对应的在collection中的modelA将会被移除,a:事件行为名,b 模型对象, c :collection对象 d 传入的其他配置参数比如slient等。
    
    	*/
    	_onModelEvent: function(a, b, c, d) { (a == "add" || a == "remove") && c != this || (a == "destroy" && this._remove(b, d), b && a === "change:" + b.idAttribute && (delete this._byId[b.previous(b.idAttribute)], this._byId[b.id] = b), this.trigger.apply(this, arguments))
    	}
    });
    //将underscore的一些方法扩展到Collection上来。使得collection可以利用underscore中的方法直接操作在它里面存储的模型集合。
    f.each(["forEach", "each", "map", "reduce", "reduceRight", "find", "detect", "filter", "select", "reject", "every", "all", "some", "any", "include", "contains", "invoke", "max", "min", "sortBy", "sortedIndex", "toArray", "size", "first", "rest", "last", "without", "indexOf", "lastIndexOf", "isEmpty", "groupBy"],
    function(a) {
    	e.Collection.prototype[a] = function() {
    		return f[a].apply(f, [this.models].concat(f.toArray(arguments)))
    	}
    });
    

    Collection是一个将数据储存并且统一操作的模块。它不仅将各个模型集合起来,而且能通过模型的改变对应操作存储在集合中的各个模型对象。它的亮点在_onModelEvent
    事件,响应所有的model改变事件,通过bindAll和bind两个方法,它指向了this属性,并且巧妙地运用Event中继承到的方法对collection和model做重要的关联。Collection使得
    在我们之后操作众多的model时有个更好的统一。Collection采用外观模式,将多个接口集成在一个公共方法里面。它同时集成了underscore中的一些方法,从而更简易地操作model对象。

    下面我们通过一个实例,看一下他是如何工作的:

    var modelA = new Backbone.Model();//创建模型A
    var modelB = new Backbone.Model();//创建模型B
    var collection = new Backbone.Collection();//建立收集器
    
    collection.add([modelA,modelB]);//收集两个模型
    

    首先我们建立两个模型,然后收集它们在collection中。在浏览器中输入collection,查看其结构:

    箭头指向的地方代表着e.model中的collection是指向收集器本身,方框中选中的事收集器存储model的地方,圆形圈中的事collection原型链上的方法。具体的方法可以参照api里面的说明,搭建简单的环境去实现它们。这里就不一一介绍了。

    以上就是collection的介绍,该模块代码结构比较简单,阅读起来较容易。但是它的设计思路很新颖。这也是阅读框架源码给我们带来益处之一:学习到作者的设计思想和实现技巧。

  • 相关阅读:
    springMVC的请求方法中处理多个mybatis方法,到某个方法中断了:BindingException:has an unsupported return type
    mybatis执行删除的restful操作的时候:Access to the specified resource has been forbidden.
    Mybatis整合spring的时候用log4j输出sql信息的debug设置
    mybatis整合ss的时候,无法autowire使用mapper的自动注入,找不到bean:NoSuchBeanDefinitionException
    springMVC提交更新:e JSPs only permit GET POST or HEAD
    ssh整合context:component-scan包名写了*号:Failed to parse configuration class [org.springframework.cache.aspectj.AspectJJCacheConfiguration]
    springMVC:java.io.FileNotFoundException: Could not open ServletContext resource
    springmvc提交表单,格式问题:The request sent by the client was syntactically incorrect.
    idea在处理spring国际化解决中文乱码,properties的格式:native-to-ascii
    REST删除操作
  • 原文地址:https://www.cnblogs.com/constantince/p/4361952.html
Copyright © 2011-2022 走看看