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

Backbone源码解析(三):Collection模块的更多相关文章

  1. Celery 源码解析三: Task 对象的实现

    Task 的实现在 Celery 中你会发现有两处,一处位于 celery/app/task.py,这是第一个:第二个位于 celery/task/base.py 中,这是第二个.他们之间是有关系的, ...

  2. Mybatis源码解析(三) —— Mapper代理类的生成

    Mybatis源码解析(三) -- Mapper代理类的生成   在本系列第一篇文章已经讲述过在Mybatis-Spring项目中,是通过 MapperFactoryBean 的 getObject( ...

  3. 【原创】backbone1.1.0源码解析之Collection

    晚上躺在床上,继续完成对Backbone.Collection的源码解析. 首先讲讲它用来干嘛? Backbone.Collection的实例表示一个集合,是很多model组成的,如果用model比喻 ...

  4. ReactiveCocoa源码解析(三) Signal代码的基本实现

    上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...

  5. ReactiveSwift源码解析(三) Signal代码的基本实现

    上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...

  6. React的React.createRef()/forwardRef()源码解析(三)

    1.refs三种使用用法 1.字符串 1.1 dom节点上使用 获取真实的dom节点 //使用步骤: 1. <input ref="stringRef" /> 2. t ...

  7. Backbone源码解析(二):Model(模型)模块

    Model(模型)模块在bk框架中的作用主要是存储处理数据,它对外和对内都有很多操作数据的接口和方法.它与视图(Views)模块精密联系着,通过set函数改变数据结构从而改变视图界面的变化.下面我们来 ...

  8. Backbone源码解析(一):Event模块

    Backbone是一个当下比较流行的MVC框架.它主要分为以下几个模块: Events, View, Model, Collection, History, Router等几大模块.它强制依赖unde ...

  9. Backbone源码解析(四):View(视图)模块

    View视图故名思义,它控制的是界面.我们可以把一个大的网页分成很多部分的视图,按照backbone的架构,每一个视图对应都是一个对象,我们可以通过元素的钩子(id或者class或者其他选择器)把它们 ...

随机推荐

  1. css 等高布局

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. ReportViewer内存泄漏问题解决方案[上]

    做这个项目有点倒霉,快要验收的时候,发现微软ReportViewer控件的一个bug,导致我们的项目无法正常验收. 问题描叙:用ReportViewer本地模式做的报表,在ASP.NET页面中呈现.在 ...

  3. 父窗口,子窗口之间的JS"通信"方法

    今天需要在iframe内做一个弹窗,但使用弹窗组件的为子窗口,所以弹窗只在子窗口中显示掩膜层和定位,这样不符合需求. 后来晓勇哥指点,了解到一个以前一直没关注到的东西,每个窗口的全局变量,其实都存在对 ...

  4. css3中的字体样式

    text-overform:ellipsis省略号/clip裁剪. overform:hidden溢出隐藏文字. 但是text-overflow只是用来说明文字溢出时用什么方式显示,要实现溢出时产生省 ...

  5. Numpy 中一维数据转置的几种方法

    把一个一维数组转置有如下几种方法.就是把 一行 n列的数组 转换成 n 行一列的数组, 如 如 [1,2,3,4] => [[1] [2]  [3] [4]] 方法一: np.transpose ...

  6. 大数据通过PHP快速插入MYSQL的方法

    如果您的mysql是通过brew安装的,那么请 vi /usr/local/Cellar/mysql/5.6.23/my.cnf 将 max_allowed_packet = 64M 写入保存并重启m ...

  7. C语言的选择和循环上机题目(部分)

    /*(1)某市不同车牌的出租车3公里的起步价和计费分别为:夏利7元/公里,3公里以外2.1元/公里:富康8元/公里,3公里以外2.4元/公里:桑塔纳9元,3公里以外2.7元/公里.编程:从键盘输入乘车 ...

  8. centos安装lamp环境

    通过yum安装,需要联网且为su账号 yum -y install httpd php mysql mysql-server php-mysql 设置开启启动mysql,httpd     /sbin ...

  9. 在64位windows下使用instsrv.exe和srvany.exe创建windows服务[转]

    本文转自:https://www.iflym.com/index.php/computer-use/201205020001.html 在32位的windows下,包括windows7,windows ...

  10. Spring中使用Schedule调度

    在spring中两种办法使用调度,以下使用是在spring4.0中. 一.基于application配置文件,配置入下: <bean id="jobDetail" class ...