• 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);
        };
      });

    欢迎指导、纠错、建议。

  • 相关阅读:
    .NET 请求和接收FormData的值
    span可编辑 属性 html 可编辑td 文字不可 选中
    mvc关于pots请求 哪个函数 出现bug研究
    C#的split函数分割
    HBase入门
    Labview学习之波形图表的历史数据
    VC++学习之GDI概述
    如何撰写项目计划书
    VC++学习之VC中常见问题
    Labview学习之程序Web发布
  • 原文地址:https://www.cnblogs.com/xiaobudiandian/p/Backbone_Collection.html
Copyright © 2020-2023  润新知