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

一、应用场景

为了改善酷版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);
时间: 09-04

Backbone事件机制核心源码(仅包含Events、Model模块)的相关文章

1小时手写SpringMVC T5大牛带你解读Spring核心源码(附详细视频教程)

SpringMVC简介 SpringMVC是当前最优秀的MVC框架,自从Spring 2.5版本发布后,由于支持注解配置,易用性有了大幅度的提高.Spring 3.0更加完善,实现了对Struts 2的超越.现在越来越多的开发团队选择了Spring MVC. Spring为展现层提供的基于MVC设计理念的优秀的Web框架,是目前最主流的MVC框架之一 Spring3.0后全面超越Struts2,成为最优秀的MVC框架 Spring MVC通过一套MVC注解,让POJO成为处理请求的控制器,而无须

1 手写ArrayList核心源码

手写ArrayList核心源码 ArrayList是Java中常用的数据结构,不光有ArrayList,还有LinkedList,HashMap,LinkedHashMap,HashSet,Queue,PriorityQueue等等,我们将手写这些常用的数据结构的核心源码,用尽量少的代码来揭示核心原理. 下面我们来手写ArrayList的核心源码 首先我们定义一个QArrayList,不要问为什么叫QArrayList,因为我之前写过Qt,仅此而已.源码 public class<T> QAr

Cocos2d-X3.0 刨根问底(七)----- 事件机制Event源码分析

这一章,我们来分析Cocos2d-x 事件机制相关的源码, 根据Cocos2d-x的工程目录,我们可以找到所有关于事件的源码都存在放在下图所示的目录中. 从这个event_dispatcher目录中的文件命名上分析 cocos2d-x与事件相关的类一共有四种, Event, EventListener,EventDispatcher, Touch分别为 事件,事件侦听器,事件分发器,触摸 我们先从Event类开始. 打开CCEvent.h文件 /** * Base class of all ki

迷你版jQuery——zepto核心源码分析

前言 zepto号称迷你版jQuery,并且成为移动端dom操作库的首选 事实上zepto很多时候只是借用了jQuery的名气,保持了与其基本一致的API,其内部实现早已面目全非! 艾伦分析了jQuery,小钗暂时没有那个本事分析jQuery,这里就恬不知耻说说自己对zepto的源码理解,希望对各位有用 首先zepto的出现其实还是很讨巧的,他看见了巨人jQuery在移动浪潮来临时的转身慢.牵挂多的问题 马上搞出了一套轻量级类jQuery框架代码,核心代码1000行不到,快速占领了移动端的市场,

[李景山php]thinkphp核心源码注释|Driver.class.php

<?php // +---------------------------------------------------------------------- // | ThinkPHP [ WE CAN DO IT JUST THINK IT ] // +---------------------------------------------------------------------- // | Copyright (c) 2006-2014 http://thinkphp.cn A

# IT明星不是梦 #图解kubernetes调度器SchedulingQueue核心源码实现

chedulingQueue是kubernetes scheduler中负责进行等待调度pod存储的对,Scheduler通过SchedulingQueue来获取当前系统中等待调度的Pod,本文主要讨论SchedulingQueue的设计与实现的各种实现, 了解探究其内部实现与底层源码,本系列代码基于kubernets1.1.6分析而来 SchedulingQueue设计 队列与优先级 队列与场景 类型 描述 通常实现 队列 普通队列是一个FIFO的数据结构,根据元素入队的次序依次出队 数组或者

java并发之线程池Executor 核心源码解析

1.什么是线程池 定义:线程池是指管理一组同构工作线程的资源池 组成部分: 线程管理器(ThreadPool):用于创建并管理线程池.包括创建线程池,销毁线程池,添加新任务 工作线程(PoolWorker):线程池中的线程 任务接口(Task):每个任务必须实现的接口,一共工作线程调度任务的执行 任务队列:用于存放没有处理的任务,提供一种缓冲机制 2.为什么要使用线程池 通过重用现有的线程而不是创建新线程,从而减少了线程创建 和 销毁过程中的巨大开销 当请求到达时,工作线程已经存在,不用再等待线

Java HashMap 核心源码解读

本篇对HashMap实现的源码进行简单的分析. 所使用的HashMap源码的版本信息如下: /* * @(#)HashMap.java 1.73 07/03/13 * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ 一.概述 在Java中每一个对象都有一个哈希码,这个值可以通过hashCo

JDK核心源码(2)

Java的基础知识有很多,但是我认为最基础的知识应该要属jdk的基础代码, jdk的基础代码里面,有分了很多基础模块,其中又属jdk包下面的lang包最为基础. 我们下面将总结和分析一下lang包下面最为基础和常用的几个部分. 1:常用的对象类型包 (1)其中最基础的类为Object,所有的对象都是继承于这个类的.我们知道所有的类如果没有显示的声明继承于哪个类的话,默认都会继承于Object类,这个类里面也有很多我们常用的方法的默认实现. (2)然后就是字符串类了,我们常用的有String.St