backbone源码学习笔记(一)
backbone源码学习笔记(一)
一、总体结构
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
root.Backbone = factory(root, exports, _, $);
});
} else if (typeof exports !== 'undefined') {
var _ = require('underscore');
factory(root, exports, _);
} else {
root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));
}
}(this, function(root, Backbone, _, $) {
... //Backbone代码
return Backbone;
}));
主要是用立即执行函数形成单独的作用域,避免污染全局空间,传入了作用域和Backbone的函数,闭包内部首先对amd和cmd模块化进行查询和处理,如果不支持模块化,就把该函数返回给root.Backbone,该函数接受四个参数,分别是root,root指向上下文(一般是window对象);Backbone(传入空的对象);underscore;及类jQuery的库。函数的返回值是Backbone对象,这样执行root.backbone时就会返回Backbone对象,从而可以使用root.backbone.Model等等Backbone内部的对象和方法。
二、extend函数
Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
Backbone的所有模块都有extend静态方法,该方法用来创建一个继承对应模块的所有默认方法和属性的构造函数,并支持传入自己的原型属性和静态属性。
var extend = function(protoProps, staticProps) {
// parent指向this,也就是对应的模块构函数
var parent = this;
var child;
// 如果传进来的原型属性具有构造函数,就把其赋给child,否则新建构造函数
if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor;
} else {
// 继承父元素的本身的属性
child = function(){ return parent.apply(this, arguments); };
}
// 用underscore的extend方法将父元素的静态属性及传进来的静态属性克隆给child的静态属性
_.extend(child, parent, staticProps);
// 借用surrogate一个空的构造函数来继承父元素的原型属性,这样子元素的原型中不会继承父元素本身的属性
var Surrogate = function(){ this.constructor = child; };
Surrogate.prototype = parent.prototype;
child.prototype = new Surrogate;
// 将新传进的原型属性添加到子元素的原型上
if (protoProps) _.extend(child.prototype, protoProps);
// 存储父元素的原型
child.__super__ = parent.prototype;
// 返回子类
return child;
};
三、Model模块
- Model 模块主要用来对数据进行增删查换、保存上传等操作,封装了一些常用的属性和方法。
// 省略部分代码
var Model = Backbone.Model = function(attributes, options) {
// 数据对象存放的对象
this.attributes = {};
// 确定模型的集合对象
if (options.collection) this.collection = options.collection;
if (options.parse) attrs = this.parse(attrs, options) || {};
// 把传进来的attributes及默认属性合并
attrs = _.defaults({}, attrs, _.result(this, 'defaults'));
// 设置属性
this.set(attrs, options);
// 存放改变过的数据对象
this.changed = {};
// 初始化函数(需要用自定义覆盖该函数)
this.initialize.apply(this, arguments);
};
- 主要方法
_.extend(Model.prototype, Events, {
// 主要方法...
});
// 服务器上传数据
sync: function() {
return Backbone.sync.apply(this, arguments);
},
// 根据属性名取出数据对象里的属性值
get: function(attr) {
return this.attributes[attr];
},
// 删除属性(删除属性是通过将属性设置为void 0,并传入Unset:true,通过set方法来删除的)
unset: function(attr, options) {
return this.set(attr, void 0, _.extend({}, options, {unset: true}));
},
// 删除该模型中的所有属性,返回this
clear: function(options) {
var attrs = {};
for (var key in this.attributes) attrs[key] = void 0;
return this.set(attrs, _.extend({}, options, {unset: true}));
},
// 返回changed对象中是否有该属性的布尔值,判断一个属性是否被更改过
hasChanged: function(attr) {
if (attr == null) return !_.isEmpty(this.changed);
return _.has(this.changed, attr);
},
// 返回某属性改变之前的值
previous: function(attr) {
if (attr == null || !this._previousAttributes) return null;
return this._previousAttributes[attr];
},
// 返回某属性改变之前的值的副本
previousAttributes: function() {
return _.clone(this._previousAttributes);
},
// 返回该模型的具有相同属性的新实例。
clone: function() {
return new this.constructor(this.attributes);
},
- 最重要的set方法:删除属性,保存属性,保存更改的属性,触发事件都是通过set方法来完成 的
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 || (options = {});
// 验证参数是否是有效的属性键值对
if (!this._validate(attrs, options)) return false;
// 从options中提取配置参数
unset = options.unset;
// 判断是否需要触发change事件,值为true时不触发
silent = options.silent;
// 改变的数组
changes = [];
// 布尔值,是否正在改变(默认为false)
changing = this._changing;
// 开始设置. _changing对象设置为ture
this._changing = true;
if (!changing) {
// 如果改动完成,则将当前的属性保存到previous对象中
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 (attr in attrs) {
val = attrs[attr];
// 如果传入的值和数据库中的值不相等,则向changes(需要改变)数组传入该属性名
if (!_.isEqual(current[attr], val)) changes.push(attr);
// 如果传入的值和previousAttributes中的值不相等,则向changed(已改变)对象传入该属性,否则则删除changed对象中的该属性
if (!_.isEqual(prev[attr], val)) {
this.changed[attr] = val;
} else {
delete this.changed[attr];
}
// 如果unset(删除属性)为true,则是删除属性,否则则设置新的属性值
unset ? delete current[attr] : current[attr] = val;
}
// 触发事件
if (!silent) {
if (changes.length) this._pending = options;
for (var i = 0, length = changes.length; i < length; i++) {
this.trigger('change:' + changes[i], this, current[changes[i]], options);
}
}
return this;
},