媒介
都因为 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._changing
和 this._pending
的运用场景,觉得是一个当多个 set 同时实行时刻的一个标记位,然则 JS 是单线程实行,内里又都是 for 语句,按理说能够不必这两个标记位。又或许是我的明白有误。
more
看到这,给种种Observer打补丁就有了可行性,支撑 Object.defineProperty
就用 Object.defineProperty
,不支撑的则降级到走 Backbone 的这类 for in
体式格局。