Backbone 模型

来源:互联网 发布:家庭影院网络播放器 编辑:程序博客网 时间:2024/06/06 15:00
BackBone模型源码2013-06-06 11:12:01

分类: JavaScript

Backbone 模型 - Backbone.Model


首先是 Model 模块的源码:

  1.   // Backbone **Models** 在框架中是基础的数据对象 --
  2.   // 常常代表你服务器中数据库表中的一行.
  3.   // 为一个离散的数据块,和一堆对这些数据进行计算转换的有用的相关的方法

  4.   // 使用指定的属性创建一个新的模型.
  5.   // 会自动生成并分配一个用户id (`cid`),依赖与 Underscore 的几个方法
  6.   var Model = Backbone.Model= function(attributes,options) {
  7.     var defaults;
  8.     var attrs = attributes || {};
  9.     options ||(options = {});

  10.    // Underscore 中生成id的方法
  11.     this.cid= _.uniqueId('c');
  12.     this.attributes= {};

  13.     // 返回 options 中属性存在于 modelOptions 的一个对象
  14.     _.extend(this, _.pick(options, modelOptions));
  15.     if (options.parse) attrs= this.parse(attrs,options) || {};
  16.     if (defaults= _.result(this,'defaults')){
  17.       attrs = _.defaults({}, attrs, defaults);
  18.     }
  19.     this.set(attrs,options);
  20.     this.changed= {};
  21.     this.initialize.apply(this,arguments);
  22.   };

模型的构造函数,用于创建各个模型。各个模型都会拥有唯一的 cid ,提供的  modelOptions  列表中的选项将直接连接到原型,其余 options 中的将使用 set 来处理,set是 Model 的核心操作。会触发模型"change"事件。具体属性的话可以在此了解, 拔了一张强大的图~





以下为 Model 模块的原型中的方法,其中的 set 为核心方法,是控制当有属性改变时触发改变对象的 change 事件。 下面为 Model模块的使用以及原型方法的源码。

首先我们在页面上画一个黑色的矩形,并且 id 为 side。 我们使用模型控制进行颜色更改。

  1. // 创建一个 有 changeColor 方法的模型,该方法会调用 set 更改 color 属性,set 会触发实例的 change:color 事件
  2. var Sidebar = Backbone.Model.extend({
  3.     changeColor:function(){
  4.         var cssColor = prompt("输入颜色");
  5.         this.set({color:cssColor});
  6.     }
  7. });

  8. // 建立一个模型的实例
  9. var side =new Sidebar();

  10. // 为该实例绑定 当属性 color 被 set (也就是触发change)后的事件
  11. side.bind("change:color",function(model,color){
  12.     $(".side").css({background:color});
  13.     alert("setted");
  14. });

  15. //然后我们运行 模型实例中的方法
  16. side.changeColor();
虽然感觉过程有点绕,中间有了 Model 这一概念来对事件进行控制,但是这样很好的使我们将结构分离开,容易控制整体以及之后的变更都会变得异常简单。

接下来就是 Model 原型中的方法:


点击(此处)折叠或打开

  1. _.extend(Model.prototype, Events,{

  2.     // 当前与之前值有变化的 属性散列(哈希).
  3.     changed: null,

  4.     // 最后一次验证失败的返回值.
  5.     validationError: null,

  6.     // JSON 默认名称 `id` 属性名为 `"id"`. MongoDB 和
  7.     // CouchDB 用户偏向于 `"_id"`.
  8.     idAttribute: 'id',

  9.     // 默认初始化空函数.
  10.     // 用自己的逻辑初始化重写函数.
  11.     initialize: function(){},

  12.     // 返回一个模型的属性对象的拷贝.
  13.     toJSON: function(options){
  14.       return _.clone(this.attributes);
  15.     },

  16.     // 默认使用 `Backbone.sync` 代理
  17.     // 如果需要可以自定义从写
  18.     sync: function(){
  19.       return Backbone.sync.apply(this,arguments);
  20.     },

  21.     // 获取属性值
  22.     get: function(attr){
  23.       return this.attributes[attr];
  24.     },

  25.     // 获取属性的 HTML-escaped 值.
  26.     escape:function(attr){
  27.       return _.escape(this.get(attr));
  28.     },

  29.     // 若属性值不为 null 或者 undefined 则返回 `true`
  30.     // or undefined.
  31.     has: function(attr){
  32.       return this.get(attr)!= null;
  33.     },

  34.     // 在对象上建立模型的属性哈希, 触发 `"change"`.
  35.     // 这是 模型 的核心操作, 更新数据, 通知那些需要知道 模型 状态变化的对象
  36.     // backbone 野兽的心脏.
  37.     set: function(key, val,options) {
  38.       var attr, attrs, unset, changes, silent, changing, prev,current;
  39.       if (key== null) returnthis;

  40.       // 处理 `"key", value` 和 `{key: value}` 2种参数形式的情况.
  41.       if (typeofkey ==='object') {
  42.         attrs = key;
  43.         options = val;
  44.       } else{
  45.         (attrs = {})[key]= val;
  46.       }

  47.       options || (options= {});

  48.       // 执行验证.
  49.       if (!this._validate(attrs,options))return false;

  50.       // 提取 属性 和 可选项.
  51.       unset = options.unset;
  52.       silent = options.silent;
  53.       changes = [];
  54.       changing = this._changing;
  55.       this._changing= true;

  56.       if (!changing){
  57.         this._previousAttributes= _.clone(this.attributes);
  58.         this.changed= {};
  59.       }
  60.       current = this.attributes, prev= this._previousAttributes;

  61.       // 检测 `id` 变化.
  62.       if (this.idAttributein attrs) this.id= attrs[this.idAttribute];

  63.       // 设置每一个属性, 更新或者删除当前值.
  64.       for (attrin attrs) {
  65.         val = attrs[attr];
  66.         if (!_.isEqual(current[attr], val)) changes.push(attr);
  67.         if (!_.isEqual(prev[attr], val)) {
  68.           this.changed[attr]= val;
  69.         } else{
  70.           delete this.changed[attr];
  71.         }
  72.         unset ? delete current[attr]: current[attr]= val;
  73.       }

  74.       // 触发所用相应的属性改变.
  75.       if (!silent){
  76.         if (changes.length)this._pending= true;
  77.         for (var i= 0, l = changes.length; i< l; i++){
  78.           this.trigger('change:'+ changes[i],this, current[changes[i]],options);
  79.         }
  80.       }

  81.       // while 循环 修改将被递归嵌套在'events'事件中
  82.       if (changing)return this;
  83.       if (!silent){
  84.         while (this._pending){
  85.           this._pending= false;
  86.           this.trigger('change',this, options);
  87.         }
  88.       }
  89.       this._pending= false;
  90.       this._changing= false;
  91.       return this;
  92.     },

  93.     // 从模型中移除一个属性, 触发 `"change"`.
  94.     // `unset` 如果属性不存在设置为空
  95.     unset: function(attr,options) {
  96.       return this.set(attr,void 0, _.extend({},options, {unset: true}));
  97.     },

  98.     // 清楚模型中的所有属性,触发 `"change"`.
  99.     clear:function(options){
  100.       var attrs = {};
  101.       for (varkey in this.attributes) attrs[key]= void 0;
  102.       return this.set(attrs, _.extend({},options, {unset: true}));
  103.     },

  104.     // 确保 模型 在上一次更改后再一次更改 `"change"` event.
  105.     // 如果指定了属性名称, 确定属性已经改变.
  106.     hasChanged: function(attr){
  107.       if (attr== null) return!_.isEmpty(this.changed);
  108.       return _.has(this.changed, attr);
  109.     },

  110.     // 返回一个包含所有改变的属性的对象, 当没有属性被更改,就返回 false.
  111.     // 常用来判断视图块是否需要更新或者那些属性需要保存到后端
  112.     // 未设置的 属性将设置为 undefined.
  113.     // 你也可以针对模型传递一个属性对象来改变,决定是否 *would be* 改变.
  114.     changedAttributes: function(diff){
  115.       if (!diff)return this.hasChanged()? _.clone(this.changed): false;
  116.       var val, changed= false;
  117.       var old = this._changing? this._previousAttributes: this.attributes;
  118.       for (var attrin diff) {
  119.         if (_.isEqual(old[attr],(val = diff[attr])))continue;
  120.         (changed || (changed= {}))[attr]= val;
  121.       }
  122.       return changed;
  123.     },

  124.     // 获取一个属性之前,在最后一次 'change' 事件触发时候的值
  125.     previous:function(attr){
  126.       if (attr== null ||!this._previousAttributes)return null;
  127.       return this._previousAttributes[attr];
  128.     },

  129.     // 获取上一次 `"change"` 事件时所有属性的值.
  130.     previousAttributes: function(){
  131.       return _.clone(this._previousAttributes);
  132.     },

  133.     // 从服务器端获取 模型 .
  134.     // 如果服务器端的显示的模型与当前属性有区别,那么覆盖并且触发事件"change"`.
  135.     fetch: function(options){
  136.       options = options ? _.clone(options): {};
  137.       if (options.parse===void 0) options.parse= true;
  138.       var model = this;
  139.       var success = options.success;
  140.       options.success= function(resp){
  141.         if (!model.set(model.parse(resp,options),options))return false;
  142.         if (success) success(model, resp,options);
  143.         model.trigger('sync', model, resp,options);
  144.       };
  145.       wrapError(this,options);
  146.       return this.sync('read',this, options);
  147.     },

  148.     // 设置属性的哈希, 同步模型到服务器.
  149.     // 如果服务器返回一个有区别的属性散列,那么模型的状态要重新设置
  150.     save:function(key, val,options) {
  151.       var attrs,method, xhr,attributes =this.attributes;

  152.       // 处理 `"key", value` and `{key: value}` 2种参数情况.
  153.       if (key== null ||typeof key === 'object') {
  154.         attrs = key;
  155.         options = val;
  156.       } else{
  157.         (attrs = {})[key]= val;
  158.       }

  159.       // 如果并不在等待队列中并且属性不存在, 保存行为以 `set(attr).save(null, opts)`格式.
  160.       if (attrs&& (!options|| !options.wait)&& !this.set(attrs,options))return false;

  161.       options = _.extend({validate:true},options);

  162.       // 舍弃无效的模型 .
  163.       if (!this._validate(attrs,options))return false;

  164.       // 如果 `{wait: true}` 设置临时属性.
  165.       if (attrs&& options.wait){
  166.         this.attributes= _.extend({},attributes, attrs);
  167.       }

  168.       // 在服务器端保存成功后, 客户端可以与服务器端一起更新(可选)
  169.       if (options.parse===void 0) options.parse= true;
  170.       var model = this;
  171.       var success = options.success;
  172.       options.success= function(resp){
  173.         // 确保属性在同步保存的时候可恢复.
  174.         model.attributes= attributes;
  175.         var serverAttrs = model.parse(resp,options);
  176.         if (options.wait) serverAttrs= _.extend(attrs|| {}, serverAttrs);
  177.         if (_.isObject(serverAttrs)&& !model.set(serverAttrs,options)){
  178.           return false;
  179.         }
  180.         if (success) success(model, resp,options);
  181.         model.trigger('sync', model, resp,options);
  182.       };
  183.       wrapError(this,options);

  184.       method =this.isNew()? 'create' : (options.patch? 'patch' : 'update');
  185.       if (method==='patch') options.attrs = attrs;
  186.       xhr = this.sync(method,this, options);

  187.       // 恢复属性.
  188.       if (attrs&& options.wait)this.attributes= attributes;

  189.       return xhr;
  190.     },

  191.     // 去除以及存在在服务器端的模型.
  192.     // 如果集合中原有一个模型,那么去掉就好.
  193.     // 如果 `wait: true` 传递过来, 在移除之前等待服务器响应.
  194.     destroy: function(options){
  195.       options = options ? _.clone(options): {};
  196.       var model = this;
  197.       var success = options.success;

  198.       var destroy = function(){
  199.         model.trigger('destroy', model, model.collection,options);
  200.       };

  201.       options.success= function(resp){
  202.         if (options.wait|| model.isNew()) destroy();
  203.         if (success) success(model, resp,options);
  204.         if (!model.isNew()) model.trigger('sync', model, resp,options);
  205.       };

  206.       if (this.isNew()){
  207.         options.success();
  208.         return false;
  209.       }
  210.       wrapError(this,options);

  211.       var xhr = this.sync('delete',this, options);
  212.       if (!options.wait) destroy();
  213.       return xhr;
  214.     },

  215.     // 模型在服务器端表示的默认的URL -- if you're
  216.     // 如果你在使用 Backbone's 静态方法, 重写此方法来改变将被调用的端点.
  217.     url:function(){
  218.       var base = _.result(this,'urlRoot') || _.result(this.collection,'url') || urlError();
  219.       if (this.isNew())return base;
  220.       return base + (base.charAt(base.length- 1) === '/' ? '': '/')+ encodeURIComponent(this.id);
  221.     },

  222.     // **parse** 响应转换成模型上的哈希
  223.     // 默认只是实现通过响应.
  224.     parse:function(resp,options) {
  225.       return resp;
  226.     },

  227.     // 创建一个新的 和当前模型有一样属性的模型.
  228.     clone: function(){
  229.       return newthis.constructor(this.attributes);
  230.     },

  231.     // 如果一个模型还没有存到服务器,那么他就是新的, 没有id.
  232.     isNew: function(){
  233.       return this.id== null;
  234.     },

  235.     // 检查模型当前是不是有效状态.
  236.     isValid: function(options){
  237.       return this._validate({}, _.extend(options|| {}, { validate: true }));
  238.     },

  239.     // 对下一个完全设置了模型属性的进行验证
  240.     // 返回'true'. 否则触发 `"invalid"` 事件.
  241.     _validate: function(attrs,options) {
  242.       if (!options.validate|| !this.validate)return true;
  243.       attrs = _.extend({},this.attributes, attrs);
  244.       var error= this.validationError= this.validate(attrs,options) || null;
  245.       if (!error)return true;
  246.       this.trigger('invalid',this, error, _.extend(options|| {}, {validationError: error}));
  247.       return false;
  248.     }

  249.   });
0 0