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. My安卓知识5--百度地图api的使用,周边信息检索

    虽然查了很多资料,但是这个问题还是解决不了,不知道为什么检索城市内的相关信息能用,检索周边信息语句就是用不了.代码如下,第一段是检索保定市内的加油站,第二段是检索周边的加油站.centerToMyLo ...

  2. opencv 处女作

    显示一幅图:主要是运用功能:imread namedWindow imshowimread:从字面意思我们就可以看懂,用来读取图片的:namedWindow:显然,我们也可以看到这是用来命名窗口名称的 ...

  3. HTML字体及颜色设置

    字体(FONT)标记(TAGS) 标题字体(Header) <h#> ... </h#> #=1, 2, 3, 4, 5, 6<h1>今天天气真好!</h1& ...

  4. js定义多行字符串

    js本身没有提供类似的定义方式,但是可以通过多行注释(/* */),已经借助function的方式来达到多行字符的定义,例如代码: var jstr = function() { var fun = ...

  5. sql server操作类(本人自己写的)

    package com.mytest; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Prepa ...

  6. 第一个jemter测试脚本

    通过jmeter代理录制脚本后,会产生大量的无用的请求,尽管在代理中已经过滤了一部分图片或者CSS.JS文件. 手动查看主要的请求:这里主要关注登陆请求,要确定有效的URL请求 删除除/Login.a ...

  7. Fragment要点复习

    做系统时间久了,应用层的很多东西都忘了,但是基础还是得巩固,下面对fragment相关的知识点简单整理一下. 一.fragment的引入 fragment是从Android3.0(API level ...

  8. git 笔记

    查看某个提交的修改:git show commitID 查看某个提交修改涉及的文件:git show commitID --oneline --name-status 清理掉没有入库的文件,并把修改了 ...

  9. asp.net core 使用protobuf

    在一些性能要求很高的应用中,使用protocol buffer序列化,优于Json.而且protocol buffer向后兼容的能力比较好. 由于Asp.net core 采用了全新的MiddleWa ...

  10. 彻底弄懂js循环中的闭包问题

    来源:http://www.108js.com/article/article1/10177.html?id=899 第一次接触这个问题还是在我刚开始学js的时候,当时就是一头雾水,时隔一年多了,突然 ...