您的位置:首页 > 其它

Backbone事件机制核心源码(仅包含Events、Model模块)

2014-09-07 08:50 519 查看
一、应用场景 为了改善酷版139邮箱的代码结构,引入backbone的事件机制,按照MVC的分层思想搭建酷版云邮局的代码框架。力求在保持酷版轻量级的基础上提高代码的可维护性。   二、遗留问题 1、backbone的升级问题,新的特性无法引入 2、backbone中的潜在BUG,若官方已修复则无法同步更新 解决办法: 关注backbone官网的更新记录。 http://github.com/documentcloud/backbone/   三、核心源码 
/**
* @裁剪版backbone,仅包含Events、Model模块,适用于轻量级的移动终端APP
* window.Minibone
*/
(function(){
var root = this;

var array = [];
var push = array.push;
var slice = array.slice;
var splice = array.splice;

var ArrayProto = Array.prototype;
var nativeIsArray = Array.isArray;
var nativeForEach = ArrayProto.forEach;

var ObjProto = Object.prototype;
var toString = ObjProto.toString;
var hasOwnProperty   = ObjProto.hasOwnProperty;

var Minibone = root.Minibone = {};
var _ = root._ = {};

var breaker = {};

_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};

// Fill in a given object with default properties.
_.defaults = function(obj) {
_.each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
if (obj[prop] == null) obj[prop] = source[prop];
}
});
return obj;
};

_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) == '[object Array]';
};

// Internal recursive comparison function.
function eq(a, b, stack) {
if(typeof a === 'object' || typeof b === 'object'){
throw new Error('Function eq only support basic data types:number,string,boolean');
}
var className = toString.call(a);
if (className != toString.call(b)) return false;
switch (className) {
case '[object String]':
return a == String(b);
case '[object Number]':
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
case '[object Boolean]':
return +a == +b;
}
return true;
}

// only support basic data types:number,string,boolean
_.isEqual = function(a, b) {
return eq(a, b, []);
};

_.isEmpty = function(obj) {
if (obj == null) return true;
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
};

_.isObject = function(obj) {
return obj === Object(obj);
};

_.isFunction = function(obj) {
return toString.call(obj) == '[object Function]';
};

_.isString = function(obj) {
return toString.call(obj) == '[object String]';
};

_.result = function(object, property) {
if (object == null) return null;
var value = object[property];
return _.isFunction(value) ? value.call(object) : value;
};

_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};

// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
_.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
for (var key in obj) {
if (_.has(obj, key)) {
if (iterator.call(context, obj[key], key, obj) === breaker) return;
}
}
}
};

_.extend = function(obj) {
_.each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
obj[prop] = source[prop];
}
});
return obj;
};

var idCounter = 0;
_.uniqueId = function(prefix) {
var id = idCounter++;
return prefix ? prefix + id : id;
};

// Minibone.Events
var triggerEvents = function(obj, events, args) {
var ev, i = -1, l = events.length;
switch (args.length) {
case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx);
return;
case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0]);
return;
case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1]);
return;
case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1], args[2]);
return;
default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args);
}
};

var Events = Minibone.Events = {
on: function(name, callback, context) {
this._events || (this._events = {});
var list = this._events[name] || (this._events[name] = []);
list.push({callback: callback, context: context, ctx: context || this});
return this;
},

off: function(name, callback, context) {
var list, ev, events, names, i, l, j, k;
if (!this._events) return this;
if (!name && !callback && !context) {
this._events = {};
return this;
}

names = name ? [name] : _.keys(this._events);
for (i = 0, l = names.length; i < l; i++) {
name = names[i];
if (list = this._events[name]) {
events = [];
if (callback || context) {
for (j = 0, k = list.length; j < k; j++) {
ev = list[j];
if ((callback && callback !== (ev.callback._callback || ev.callback)) ||
(context && context !== ev.context)) {
events.push(ev);
}
}
}
this._events[name] = events;
}
}

return this;
},

trigger: function(name) {
if (!this._events) return this;
var args = slice.call(arguments, 1);
var events = this._events[name];
var allEvents = this._events.all;
if (events) triggerEvents(this, events, args);
if (allEvents) triggerEvents(this, allEvents, arguments);
return this;
}
};

_.extend(Minibone, Events);

// Minibone.Model
var Model = Minibone.Model = function(attributes, options) {
var defaults;
var attrs = attributes || {};
this.cid = _.uniqueId('c');
this.changed = {};
this.attributes = {};
this._changes = [];
if (options && options.collection) this.collection = options.collection;
if (options && options.parse) attrs = this.parse(attrs);
if (defaults = _.result(this, 'defaults')) _.defaults(attrs, defaults);
this.set(attrs, {silent: true});
this._currentAttributes = _.clone(this.attributes);
this._previousAttributes = _.clone(this.attributes);
this.initialize.apply(this, arguments);
};

_.extend(Model.prototype, Events, {

changed: null,
idAttribute: 'id',
initialize: function(){},

get: function(attr) {
return this.attributes[attr];
},

// 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.
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 = {});
if (!this._validate(attrs, options)) return false;
unset           = options.unset;
silent          = options.silent;
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;
for (var i = 0, l = changes.length; i < l; i++) {
this.trigger('change:' + changes[i], this, current[changes[i]], options);
}
}

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;
},

_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;
}

});

// Helpers
var extend = function(protoProps, staticProps) {
var parent = this;
var child;

if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor;
} else {
child = function(){ parent.apply(this, arguments); };
}

_.extend(child, parent, staticProps);

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.extend = extend;
}).call(this);

 

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: