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


    一、应用场景
    为了改善酷版139邮箱的代码结构,引入backbone的事件机制,按照MVC的分层思想搭建酷版云邮局的代码框架。力求在保持酷版轻量级的基础上提高代码的可维护性。
     
    二、遗留问题
    1、backbone的升级问题,新的特性无法引入
    2、backbone中的潜在BUG,若官方已修复则无法同步更新
    解决办法:
    关注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);
  • 相关阅读:
    猜拳游戏GuessGame源码
    基于vitamio的网络电视直播源码
    EarthWarrior3D游戏ios源码
    类似material design的电影应用源码
    四季电台应用项目源码
    高仿一元云购IOS应用源码项目
    非常不错的点餐系统应用ios源码完整版
    仿哔哩哔哩应用客户端Android版源码项目
    《你最美》换发型应用源码
    最新win7系统64位和32位系统Ghost装机稳定版下载
  • 原文地址:https://www.cnblogs.com/hellohuman/p/3960102.html
Copyright © 2020-2023  润新知