Backbone源码研讨 - Backbone.Model

媒介

都因为 IE8 不支撑 Object.defineProperty,然则营业还不能离开 IE7 和 IE8,故研讨下 Backbone.Model 的完成机制,找机会给主流的 MVVM 框架补丁

伪代码

先来看看 Model 的组织函数

var Model = Backbone.Model = function(attributes, options) {
    var attrs = attributes || {};
    options || (options = {});
    
    // 钩子
    this.preinitialize.apply(this, arguments);
    
    // 每一个实例分派一个唯一的 cid
    this.cid = _.uniqueId(this.cidPrefix);
    // 数据对象
    this.attributes = {};
    
    // 不重要的内容
    if (options.collection) this.collection = options.collection;
    if (options.parse) attrs = this.parse(attrs, options) || {};
    
    // 猎取预设在 defaults 字段中的初始键值对或匿名函数
    // 这里运用 _.result() 来兼容函数和对象两种范例
    var defaults = _.result(this, 'defaults');
    
    // 防备 attrs 中的 undefined 值覆蓋掉 defaults 中的默许值
    attrs = _.defaults(_.extend({}, defaults, attrs), defaults);
    
    // 初始化赋值
    this.set(attrs, options);
    this.changed = {};
    
    // 钩子
    this.initialize.apply(this, arguments);
  };

很简单的代码,做了一些初始化赋值的事变。

用到了一个小技能 attrs = _.defaults(_.extend({}, defaults, attrs), defaults); 来防备误传入的 undefined 覆蓋掉默许的 defaults 值。

Backbone 的精炼都在 set(){} 这个函数内里。

set: function(key, val, options) {
  if (key == null) return this;

  // 一致 'key', 'val' 和 {key:val} 这两种情势
  // attrs 终究为更改的对象
  var attrs;
  if (typeof key === 'object') {
    attrs = key;
    options = val;
  } else {
    (attrs = {})[key] = val;
  }

  options || (options = {});

  // 划定规矩考证.
  if (!this._validate(attrs, options)) return false;

  var unset      = options.unset;
  var silent     = options.silent;
  var changes    = [];
  var changing   = this._changing;
  this._changing = true;

  // 预留上一次的值
  if (!changing) {
    this._previousAttributes = _.clone(this.attributes);
    this.changed = {};
  }

  // 备份一个当前的数据对象
  var current = this.attributes;
  var changed = this.changed;
  var prev    = this._previousAttributes;

  // 遍历传入的新数据对象
  for (var attr in attrs) {
    val = attrs[attr];
    
    // 假如新数据与当前不一致,则标记更改的键名
    if (!_.isEqual(current[attr], val)) changes.push(attr);
    
    // 假如新数据与旧数据不一致,则更新已更改的数据备份
    if (!_.isEqual(prev[attr], val)) {
      changed[attr] = val;
    } else {
      delete changed[attr];
    }
    
    // 真正干活的代码,更新数据或许删除数据
    unset ? delete current[attr] : current[attr] = val;
  }

  // 更新 id 字段
  if (this.idAttribute in attrs) this.id = this.get(this.idAttribute);

  if (!silent) {
    if (changes.length) this._pending = options;
    
    // 遍历更改清单,而且逐一触发 `change:` 事宜
    for (var i = 0; i < changes.length; i++) {
      this.trigger('change:' + changes[i], this, current[changes[i]], options);
    }
  }

  if (changing) return this;
  if (!silent) {

    // 触发一个总的 `change` 事宜
    // 解释说这里用 while 是确保嵌套场景也只触发一个 `change` 事宜
    while (this._pending) {
      options = this._pending;
      this._pending = false;
      this.trigger('change', this, options);
    }
  }
  this._pending = false;
  this._changing = false;
  return this;
},

全部 set 内里,现实干活的就是 unset ? delete current[attr] : current[attr] = val;

没看邃晓 this._changingthis._pending 的运用场景,觉得是一个当多个 set 同时实行时刻的一个标记位,然则 JS 是单线程实行,内里又都是 for 语句,按理说能够不必这两个标记位。又或许是我的明白有误。

more

看到这,给种种Observer打补丁就有了可行性,支撑 Object.defineProperty 就用 Object.defineProperty,不支撑的则降级到走 Backbone 的这类 for in 体式格局。

同步更新博客

https://www.mxgw.info/a/backb…

    原文作者:Libo
    原文地址: https://segmentfault.com/a/1190000008203471
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞