backbone 学习之Collection
Collection,model的集合,提供了很多可枚举的函数,方便操作、管理。直接看源码:
// Backbone.Collection // ------------------- // If models tend to represent a single row of data, a Backbone Collection is // more analagous to a table full of data ... or a small slice or page of that // table, or a collection of rows that belong together for a particular reason // -- all of the messages in this particular folder, all of the documents // belonging to this particular author, and so on. Collections maintain // indexes of their models, both in order, and for lookup by `id`. // Create a new **Collection**, perhaps to contain a specific type of `model`. // If a `comparator` is specified, the Collection will maintain // its models in sort order, as they're added and removed. var Collection = Backbone.Collection = function(models, options) { options || (options = {}); if (options.url) this.url = options.url; if (options.model) this.model = options.model; // 某一类型Model // 如果设置了comparator,那么就会根据comparator对collection中的models进行排序 if (options.comparator !== void 0) this.comparator = options.comparator; this._reset();// 重置 this.initialize.apply(this, arguments); if (models) this.reset(models, _.extend({silent: true}, options)); }; // Default options for `Collection#set`. var setOptions = {add: true, remove: true, merge: true}; var addOptions = {add: true, merge: false, remove: false}; // Define the Collection's inheritable methods. _.extend(Collection.prototype, Events, { // The default model for a collection is just a **Backbone.Model**. // This should be overridden in most cases. // 默认model集合就是Backbone.Model实例的集合 model: Model, // Initialize is an empty function by default. Override it with your own // initialization logic. initialize: function(){}, // The JSON representation of a Collection is an array of the // models' attributes. toJSON: function(options) { return this.map(function(model){ return model.toJSON(options); }); }, // Proxy `Backbone.sync` by default. sync: function() { return Backbone.sync.apply(this, arguments); }, // Add a model, or list of models to the set. // 增加一个model或者model集合 add: function(models, options) { return this.set(models, _.defaults(options || {}, addOptions)); }, // Remove a model, or a list of models from the set. // 移除一个model或者model集合 // 会触发model的remove事件 remove: function(models, options) { models = _.isArray(models) ? models.slice() : [models]; options || (options = {}); var i, l, index, model; for (i = 0, l = models.length; i < l; i++) { model = this.get(models[i]); if (!model) continue; delete this._byId[model.id]; delete this._byId[model.cid]; index = this.indexOf(model); this.models.splice(index, 1); this.length--; if (!options.silent) { options.index = index; model.trigger('remove', model, this, options); } this._removeReference(model); } return this; }, // Update a collection by `set`-ing a new list of models, adding new ones, // removing models that are no longer present, and merging models that // already exist in the collection, as necessary. Similar to **Model#set**, // the core operation for updating the data contained by the collection. // 更新这个collection的models集合 set: function(models, options) { options = _.defaults(options || {}, setOptions); if (options.parse) models = this.parse(models, options); if (!_.isArray(models)) models = models ? [models] : []; var i, l, model, attrs, existing, sort; var at = options.at; var sortable = this.comparator && (at == null) && options.sort !== false; var sortAttr = _.isString(this.comparator) ? this.comparator : null; var toAdd = [], toRemove = [], modelMap = {}; // Turn bare objects into model references, and prevent invalid models // from being added. for (i = 0, l = models.length; i < l; i++) { // 检查是否是Model的实例 并且查看此model是否设置了collection属性 // 没的话就设置成this当前的collection if (!(model = this._prepareModel(models[i], options))) continue; // If a duplicate is found, prevent it from being added and // optionally merge it into the existing model. // 如果已经存在此model // 如果存在 那么看是否是移除 还是合并 if (existing = this.get(model)) { if (options.remove) modelMap[existing.cid] = true; if (options.merge) { existing.set(model.attributes, options); if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true; } // This is a new model, push it to the `toAdd` list. } else if (options.add) { toAdd.push(model); // Listen to added models' events, and index models for lookup by // `id` and by `cid`. // 监听model的all事件 model.on('all', this._onModelEvent, this); this._byId[model.cid] = model; // 方便根据id得到model if (model.id != null) this._byId[model.id] = model; } } // Remove nonexistent models if appropriate. if (options.remove) { for (i = 0, l = this.length; i < l; ++i) { if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model); } if (toRemove.length) this.remove(toRemove, options); } // See if sorting is needed, update `length` and splice in new models. // 是否需要排序 增加或者插入新增的models if (toAdd.length) { if (sortable) sort = true; this.length += toAdd.length; if (at != null) { splice.apply(this.models, [at, 0].concat(toAdd)); } else { push.apply(this.models, toAdd); } } // Silently sort the collection if appropriate. // 排序 if (sort) this.sort({silent: true}); if (options.silent) return this; // Trigger `add` events. // 触发model的add事件 for (i = 0, l = toAdd.length; i < l; i++) { (model = toAdd[i]).trigger('add', model, this, options); } // Trigger `sort` if the collection was sorted. // 触发collection的sort事件 if (sort) this.trigger('sort', this, options); return this; }, // When you have more items than you want to add or remove individually, // you can reset the entire set with a new list of models, without firing // any granular `add` or `remove` events. Fires `reset` when finished. // Useful for bulk operations and optimizations. // 重置models 记录上次的models // 触发collection的reset事件 reset: function(models, options) { options || (options = {}); for (var i = 0, l = this.models.length; i < l; i++) { this._removeReference(this.models[i]); } options.previousModels = this.models; this._reset(); this.add(models, _.extend({silent: true}, options)); if (!options.silent) this.trigger('reset', this, options); return this; }, // Add a model to the end of the collection. // 在最后新增一个model push: function(model, options) { model = this._prepareModel(model, options); this.add(model, _.extend({at: this.length}, options)); return model; }, // Remove a model from the end of the collection. // 移除最后一个model pop: function(options) { var model = this.at(this.length - 1); this.remove(model, options); return model; }, // Add a model to the beginning of the collection. // 在开始的位置增加一个model unshift: function(model, options) { model = this._prepareModel(model, options); this.add(model, _.extend({at: 0}, options)); return model; }, // Remove a model from the beginning of the collection. // 移除collection的第一个model shift: function(options) { var model = this.at(0); this.remove(model, options); return model; }, // Slice out a sub-array of models from the collection. // 得到collection中的从begin位置到end位置的所有的model slice: function(begin, end) { return this.models.slice(begin, end); }, // Get a model from the set by id. get: function(obj) { if (obj == null) return void 0; return this._byId[obj.id != null ? obj.id : obj.cid || obj]; }, // Get the model at the given index. at: function(index) { return this.models[index]; }, // Return models with matching attributes. Useful for simple cases of // `filter`. // 返回符合attrs的models // first是否只要第一个符合的 where: function(attrs, first) { if (_.isEmpty(attrs)) return first ? void 0 : []; return this[first ? 'find' : 'filter'](function(model) { for (var key in attrs) { if (attrs[key] !== model.get(key)) return false; } return true; }); }, // Return the first model with matching attributes. Useful for simple cases // of `find`. findWhere: function(attrs) { return this.where(attrs, true); }, // Force the collection to re-sort itself. You don't need to call this under // normal circumstances, as the set will maintain sort order as each item // is added. // 对collection进行排序(根据comparator) // 触发集合的sort事件 sort: function(options) { if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); options || (options = {}); // Run sort based on type of `comparator`. // comparator是string或者长度是1 if (_.isString(this.comparator) || this.comparator.length === 1) { this.models = this.sortBy(this.comparator, this); } else {// 函数 this.models.sort(_.bind(this.comparator, this)); } if (!options.silent) this.trigger('sort', this, options); return this; }, // Figure out the smallest index at which a model should be inserted so as // to maintain order. // model在collection中排序后的位置 sortedIndex: function(model, value, context) { value || (value = this.comparator); var iterator = _.isFunction(value) ? value : function(model) { return model.get(value); }; return _.sortedIndex(this.models, model, iterator, context); }, // Pluck an attribute from each model in the collection. // 从集合中的每个模型拉取 attribute pluck: function(attr) { return _.invoke(this.models, 'get', attr); }, // Fetch the default set of models for this collection, resetting the // collection when they arrive. If `reset: true` is passed, the response // data will be passed through the `reset` method instead of `set`. // 从服务器端请求默认集合的模型 // 成功后会 set或者reset 集合 // 会触发read事件 fetch: function(options) { options = options ? _.clone(options) : {}; if (options.parse === void 0) options.parse = true; var success = options.success; var collection = this; options.success = function(resp) { var method = options.reset ? 'reset' : 'set'; collection[method](resp, options); if (success) success(collection, resp, options); collection.trigger('sync', collection, resp, options); }; wrapError(this, options); return this.sync('read', this, options); }, // Create a new instance of a model in this collection. Add the model to the // collection immediately, unless `wait: true` is passed, in which case we // wait for the server to agree. // 从collection中创建一个model 可能的话会直接加入到collection中 create: function(model, options) { options = options ? _.clone(options) : {}; if (!(model = this._prepareModel(model, options))) return false; if (!options.wait) this.add(model, options); var collection = this; var success = options.success; options.success = function(resp) { if (options.wait) collection.add(model, options); if (success) success(model, resp, options); }; model.save(null, options); return model; }, // **parse** converts a response into a list of models to be added to the // collection. The default implementation is just to pass it through. parse: function(resp, options) { return resp; }, // Create a new collection with an identical list of models as this one. clone: function() { return new this.constructor(this.models); }, // Private method to reset all internal state. Called when the collection // is first initialized or reset. _reset: function() { this.length = 0; this.models = []; this._byId = {}; }, // Prepare a hash of attributes (or other model) to be added to this // collection. // 判断attrs是否是Model实例 // 并且查看此model是否设置了collection属性 _prepareModel: function(attrs, options) { if (attrs instanceof Model) { if (!attrs.collection) attrs.collection = this;// 指定model的collection指向this collection return attrs; } options || (options = {}); options.collection = this; var model = new this.model(attrs, options); if (!model._validate(attrs, options)) { this.trigger('invalid', this, attrs, options); return false; } return model; }, // Internal method to sever a model's ties to a collection. // 移除model监听的all事件 _removeReference: function(model) { if (this === model.collection) delete model.collection; model.off('all', this._onModelEvent, this); }, // Internal method called every time a model in the set fires an event. // Sets need to update their indexes when models change ids. All other // events simply proxy through. "add" and "remove" events that originate // in other collections are ignored. // 当前集合中的model发生改变的时候就会执行此函数 _onModelEvent: function(event, model, collection, options) { if ((event === 'add' || event === 'remove') && collection !== this) return; if (event === 'destroy') this.remove(model, options); if (model && event === 'change:' + model.idAttribute) { delete this._byId[model.previous(model.idAttribute)]; if (model.id != null) this._byId[model.id] = model; } this.trigger.apply(this, arguments); } }); // Underscore methods that we want to implement on the Collection. // 90% of the core usefulness of Backbone Collections is actually implemented // right here: var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest', 'tail', 'drop', 'last', 'without', 'indexOf', 'shuffle', 'lastIndexOf', 'isEmpty', 'chain']; // Mix in each Underscore method as a proxy to `Collection#models`. // 丰富collection的方法 _.each(methods, function(method) { Collection.prototype[method] = function() { var args = slice.call(arguments); args.unshift(this.models); return _[method].apply(_, args); }; }); // Underscore methods that take a property name as an argument. var attributeMethods = ['groupBy', 'countBy', 'sortBy']; // Use attributes instead of properties. _.each(attributeMethods, function(method) { Collection.prototype[method] = function(value, context) { var iterator = _.isFunction(value) ? value : function(model) { return model.get(value); }; return _[method](this.models, iterator, context); }; });
欢迎指导、纠错、建议。