• backbone 学习之Model


    直接看源码:

    // Backbone.Model
      // --------------
    
      // Backbone **Models** are the basic data object in the framework --
      // frequently representing a row in a table in a database on your server.
      // A discrete chunk of data and a bunch of useful, related methods for
      // performing computations and transformations on that data.
    
      // Create a new model with the specified attributes. A client id (`cid`)
      // is automatically generated and assigned for you.
      var Model = Backbone.Model = function(attributes, options) {
        var defaults;
        var attrs = attributes || {}; // 数据
        options || (options = {}); // 配置的可选项
        this.cid = _.uniqueId('c'); // 每一个model都有一个cid属性 方便collection
                                    // 可以根据cid得到当前model
        this.attributes = {};
        // 如果options指定了 url urlRoot 和 collection的话就添加到this上
        _.extend(this, _.pick(options, modelOptions)); 
        // 如果有parse解析数据的函数的话 就处理数据
        if (options.parse) attrs = this.parse(attrs, options) || {};
        // 存在defaults默认信息,有的话就给attrs设置上
        if (defaults = _.result(this, 'defaults')) {
          attrs = _.defaults({}, attrs, defaults);
        }
        this.set(attrs, options);
        this.changed = {};// 记录当前model有那些东西更改了
        this.initialize.apply(this, arguments); // 初始化
      };
    
      // A list of options to be attached directly to the model, if provided.
      var modelOptions = ['url', 'urlRoot', 'collection'];
    
      // Attach all inheritable methods to the Model prototype.
      _.extend(Model.prototype, Events, {
    
        // A hash of attributes whose current and previous value differ.
        changed: null,
    
        // The value returned during the last failed validation.
        validationError: null,
    
        // The default name for the JSON `id` attribute is `"id"`. MongoDB and
        // CouchDB users may want to set this to `"_id"`.
        // 数据的id属性值是什么 默认id
        idAttribute: 'id',
    
        // Initialize is an empty function by default. Override it with your own
        // initialization logic.
        initialize: function(){},
    
        // Return a copy of the model's `attributes` object.
        // 得到新的对象
        toJSON: function(options) {
          return _.clone(this.attributes);
        },
    
        // Proxy `Backbone.sync` by default -- but override this if you need
        // custom syncing semantics for *this* particular model.
        // 请求
        sync: function() {
          return Backbone.sync.apply(this, arguments);
        },
    
        // Get the value of an attribute.
        // 得到数据attr属性信息
        get: function(attr) {
          return this.attributes[attr];
        },
    
        // Get the HTML-escaped value of an attribute.
        // 转义数据attr属性信息
        escape: function(attr) {
          return _.escape(this.get(attr));
        },
    
        // Returns `true` if the attribute contains a value that is not null
        // or undefined.
        has: function(attr) {
          return this.get(attr) != null;
        },
    
        // Set a hash of model attributes on the object, firing `"change"`. This is
        // the core primitive operation of a model, updating the data and notifying
        // anyone who needs to know about the change in state. The heart of the beast.
        // model的核心操作 在这里会触发change事件
        set: function(key, val, options) {
          var attr, attrs, unset, changes, silent, changing, prev, current;
          if (key == null) return this;
    
          // Handle both `"key", value` and `{key: value}` -style arguments.
          if (typeof key === 'object') {
            attrs = key;
            options = val;
          } else {
            (attrs = {})[key] = val;
          }
    
          options || (options = {});
    
          // Run validation.
          // 验证数据合法性
          if (!this._validate(attrs, options)) return false;
    
          // Extract attributes and options.
          unset           = options.unset; // 设置数据 || 全部设置undefined 
          silent          = options.silent; // 静悄悄? 是否触发change事件
          changes         = []; // 所有的改变数据集合
          changing        = this._changing; // 是否已经在改变
          this._changing  = true;
    
          if (!changing) {
            this._previousAttributes = _.clone(this.attributes); // 为改变之前的数据
            this.changed = {};
          }
          current = this.attributes, prev = this._previousAttributes;
    
          // Check for changes of `id`.
          if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
    
          // For each `set` attribute, update or delete the current value.
          // 更新或者删除当前要设置的值 并记录改变属性值
          for (attr in attrs) {
            val = attrs[attr];
            if (!_.isEqual(current[attr], val)) changes.push(attr);
            if (!_.isEqual(prev[attr], val)) {
              this.changed[attr] = val;
            } else {
              delete this.changed[attr];
            }
            unset ? delete current[attr] : current[attr] = val;
          }
    
          // Trigger all relevant attribute changes.
          if (!silent) {
            if (changes.length) this._pending = true; // 如果有某些属性改变 触发change
            for (var i = 0, l = changes.length; i < l; i++) {
              this.trigger('change:' + changes[i], this, current[changes[i]], options);
            }
          }
    
          // You might be wondering why there's a `while` loop here. Changes can
          // be recursively nested within `"change"` events.
          // 避免change的递归嵌套问题
          if (changing) return this;
          if (!silent) {
            while (this._pending) {
              this._pending = false;
              this.trigger('change', this, options);
            }
          }
          this._pending = false;
          this._changing = false;
          return this;
        },
    
        // Remove an attribute from the model, firing `"change"`. `unset` is a noop
        // if the attribute doesn't exist.
        // 移除数据中的某属性值
        unset: function(attr, options) {
          return this.set(attr, void 0, _.extend({}, options, {unset: true}));
        },
    
        // Clear all attributes on the model, firing `"change"`.
        // 清楚所有数据 触发change事件
        clear: function(options) {
          var attrs = {};
          for (var key in this.attributes) attrs[key] = void 0;
          return this.set(attrs, _.extend({}, options, {unset: true}));
        },
    
        // Determine if the model has changed since the last `"change"` event.
        // If you specify an attribute name, determine if that attribute has changed.
        // 是否有数据发生改变 || 某个属性是否发生了改变
        hasChanged: function(attr) {
          if (attr == null) return !_.isEmpty(this.changed);
          return _.has(this.changed, attr);
        },
    
        // Return an object containing all the attributes that have changed, or
        // false if there are no changed attributes. Useful for determining what
        // parts of a view need to be updated and/or what attributes need to be
        // persisted to the server. Unset attributes will be set to undefined.
        // You can also pass an attributes object to diff against the model,
        // determining if there *would be* a change.
        // 得到改变的属性信息
        changedAttributes: function(diff) {
          if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
          var val, changed = false;
          var old = this._changing ? this._previousAttributes : this.attributes;
          for (var attr in diff) {
            if (_.isEqual(old[attr], (val = diff[attr]))) continue;
            (changed || (changed = {}))[attr] = val;
          }
          return changed;
        },
    
        // Get the previous value of an attribute, recorded at the time the last
        // `"change"` event was fired.
        // 在change之前的上一次数据
        previous: function(attr) {
          if (attr == null || !this._previousAttributes) return null;
          return this._previousAttributes[attr];
        },
    
        // Get all of the attributes of the model at the time of the previous
        // `"change"` event.
        previousAttributes: function() {
          return _.clone(this._previousAttributes);
        },
    
        // Fetch the model from the server. If the server's representation of the
        // model differs from its current attributes, they will be overridden,
        // triggering a `"change"` event.
        // 取得所有数据 请求
        // 成功的时候会触发change事件
        fetch: function(options) {
          options = options ? _.clone(options) : {};
          if (options.parse === void 0) options.parse = true;
          var model = this;
          var success = options.success;
          options.success = function(resp) {
            if (!model.set(model.parse(resp, options), options)) return false;
            if (success) success(model, resp, options);
            model.trigger('sync', model, resp, options);
          };
          wrapError(this, options);
          return this.sync('read', this, options);
        },
    
        // Set a hash of model attributes, and sync the model to the server.
        // If the server returns an attributes hash that differs, the model's
        // state will be `set` again.
        // 保存数据到服务端 可能是创建 或者 删除 更改等
        save: function(key, val, options) {
          var attrs, method, xhr, attributes = this.attributes;
    
          // Handle both `"key", value` and `{key: value}` -style arguments.
          if (key == null || typeof key === 'object') {
            attrs = key;
            options = val;
          } else {
            (attrs = {})[key] = val;
          }
    
          // If we're not waiting and attributes exist, save acts as `set(attr).save(null, opts)`.
          if (attrs && (!options || !options.wait) && !this.set(attrs, options)) return false;
    
          options = _.extend({validate: true}, options);
    
          // Do not persist invalid models.
          if (!this._validate(attrs, options)) return false;
    
          // Set temporary attributes if `{wait: true}`.
          if (attrs && options.wait) {
            this.attributes = _.extend({}, attributes, attrs);
          }
    
          // After a successful server-side save, the client is (optionally)
          // updated with the server-side state.
          if (options.parse === void 0) options.parse = true;
          var model = this;
          var success = options.success;
          options.success = function(resp) {
            // Ensure attributes are restored during synchronous saves.
            model.attributes = attributes;
            var serverAttrs = model.parse(resp, options);
            if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
            if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
              return false;
            }
            if (success) success(model, resp, options);
            model.trigger('sync', model, resp, options);
          };
          wrapError(this, options);
    
          method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
          if (method === 'patch') options.attrs = attrs;
          xhr = this.sync(method, this, options);
    
          // Restore attributes.
          if (attrs && options.wait) this.attributes = attributes;
    
          return xhr;
        },
    
        // Destroy this model on the server if it was already persisted.
        // Optimistically removes the model from its collection, if it has one.
        // If `wait: true` is passed, waits for the server to respond before removal.
        // 销毁 删除 服务端同步更新 触发destroy事件
        destroy: function(options) {
          options = options ? _.clone(options) : {};
          var model = this;
          var success = options.success;
    
          var destroy = function() {
            model.trigger('destroy', model, model.collection, options);
          };
    
          options.success = function(resp) {
            if (options.wait || model.isNew()) destroy();
            if (success) success(model, resp, options);
            if (!model.isNew()) model.trigger('sync', model, resp, options);
          };
    
          if (this.isNew()) {
            options.success();
            return false;
          }
          wrapError(this, options);
    
          var xhr = this.sync('delete', this, options);
          if (!options.wait) destroy();
          return xhr;
        },
    
        // Default URL for the model's representation on the server -- if you're
        // using Backbone's restful methods, override this to change the endpoint
        // that will be called.
        // 此model请求的url
        url: function() {
          var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError();
          if (this.isNew()) return base;
          return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id);
        },
    
        // **parse** converts a response into the hash of attributes to be `set` on
        // the model. The default implementation is just to pass the response along.
        // 解析数据的函数 做一些操作什么的
        parse: function(resp, options) {
          return resp;
        },
    
        // Create a new model with identical attributes to this one.
        clone: function() {
          return new this.constructor(this.attributes);
        },
    
        // A model is new if it has never been saved to the server, and lacks an id.
        // 此model是否保存到服务端过 没有的话就认为是新的 通过的是判断id
        isNew: function() {
          return this.id == null;
        },
    
        // Check if the model is currently in a valid state.
        isValid: function(options) {
          return this._validate({}, _.extend(options || {}, { validate: true }));
        },
    
        // Run validation against the next complete set of model attributes,
        // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
        // 验证函数validate 是否会验证成功 失败的话会触发invalid事件
        _validate: function(attrs, options) {
          if (!options.validate || !this.validate) return true;
          attrs = _.extend({}, this.attributes, attrs);
          var error = this.validationError = this.validate(attrs, options) || null;
          if (!error) return true;
          this.trigger('invalid', this, error, _.extend(options || {}, {validationError: error}));
          return false;
        }
    
      });

    欢迎指导、纠错、建议。

  • 相关阅读:
    SpringBoot的多环境配置及配置文件位置
    SpringBoot;yaml配置, JSR303校验
    springboot原理探寻,自动装配
    SpringBoot入门:搭建SpringBoot
    Android控件阴影库
    Android开发Utils工具类集合
    Android 实现顶部状态栏的沉浸模式(任意设置状态栏的颜色)
    推荐一个博客代码高亮插件
    H5+Css+js 做App UI 与原生的区别
    Android线程切换简便方法
  • 原文地址:https://www.cnblogs.com/xiaobudiandian/p/Backbone_Model.html
Copyright © 2020-2023  润新知