Ruby's Louvre

每天学习一点点算法

导航

解读rightjs的继承机制3

佛经里有句话,叫功不唐捐。意思是所有努力的效果不一定产生在当下,可能产生在未来,但你只要做了,它一定就有意义。我们心态平和一点,逐步去推进它,逐步去改良它,对未来必定很有意义。

有人总很狭隘地支持某一类库或框架,其实好的东西都是互相渗透的。像jQuery,它提供了对DOM的操作,但仅仅这样是不够的。请不要用后台的增删改查的目光来看前台,前台的展现是非常丰富多彩的。后台也不是那么枯燥,当然对无能的人怎么也一样。他们总是有借口逃避学习,总是问人来解决项目上的问题。这样的IT生涯真的只能撑到30岁。我们专注一个东西,其他同类型的产品都是非常有参考价值,能扩宽我们的思维。比如最早称霸javascript界的Prototype.js,它是作为rails的一个辅助项目诞生。借鉴于真正的一切都是对象的ruby的设计,Prototype.js当年横扫天下,无人可敌。我们可以看到其许多方法名都是取自ruby标准库的。像Base2这样强大的库,我觉得它也从Prototype.js吸取了不少灵感。mootools号称是Prototype.js,框架是Prototype,但许多实现却改自Base2。这三者都拥有强大的继承机制,反正学了其中一个,对其他两个就很容易上手。换言之,功不唐捐,你的智慧投资不会白费的。

西方的开源世界非常活跃,新兴类库都是有着浓厚的已知框架的影子。rightjs就是一个例子。今天讲解其两个模块,不由得感概万分。

Options模块,专门来处理参数。在Prototype著名的特效库中,已经有这种迹象了。mootools干脆交由Object.reset来做这粗活。为什么要这样做呢?因为通常我们要传入一个属性包来配置新生成的类,为它添加类成员,实例成员什么的,在mootools2的蓝图中,甚至计划配置友元成员。这个其实在mootools1.2.4中已用protect方法实现了,不过它想搞得更smart一些。

    Options = {
    
      setOptions: function(options) {
        //整合两个对象的options属性,一个是配置用的属性包,一个是新类,注意Options模块是不能独立运作的,
        //它肯定用于其他模块或类中。
        var options = this.options = Object.merge(Class.findSet(this, 'options'), options), match, key;
        //如果它的on属性为函数,执行方法,如this.on("click",function(){})
        if (isFunction(this.on)) {
          for (key in options) {
            if (match = key.match(/on([A-Z][A-Za-z]+)/)) {
              this.on(match[1].toLowerCase(), options[key]);
              delete(options[key]);
            }
          }
        }

        return this;
      },

      //由于rightjs的类工厂最多只有两个参数,父类与作为属性包的对象,现在的目的是取得那个属性包
      cutOptions: function(args) {
        var args = $A(args);
        this.setOptions(isHash(args.last()) ? args.pop() : {});
        return args;
      }
    };

下面是观察者模块,它的许多方法都支持多种传参形式,像jQuery一样,把逻辑搞得很复杂,实在恶心。

    Observer = new Class({
      include: Options,

      /**
       * general constructor
       *
       * @param Object options
       */
      initialize: function(options) {
        this.setOptions(options);
        Observer.createShortcuts(this, Class.findSet(this, 'events'));
      },


      //this.on("click", fnCallback)
      //添加回调函数与其事件名,额外参数
      on: function() {
        var args = $A(arguments), event = args.shift();

        if (isString(event)) {
          //创建一个$listeners存放一个个特别的对象
          if (!defined(this.$listeners)) this.$listeners = [];

          var callback = args.shift(), name;
          switch (typeof callback) {
            case "string":
              name     = callback;
              callback = this[callback];

            case "function":
              var hash = {};

              // DON'T move it in the one-line hash variable definition,
              // it causes problems with the Konqueror 3 later on
              hash.e = event;
              hash.f = callback;
              hash.a = args;
              hash.r = name;

              this.$listeners.push(hash);
              break;

            default:
              if (isArray(callback)) {
                for (var i=0; i < callback.length; i++) {
                  //看它是否为二维数组
                  this.on.apply(this, [event].concat(
                  isArray(callback[i]) ? callback[i] : [callback[i]]
                ).concat(args));
                }
              }
          }

        } else {
          //处理参数只有一个属性包的情形
          for (var name in event) {
            this.on.apply(this, [name].concat(
            isArray(event[name]) ? event[name] : [event[name]]
          ).concat(args));
          }
        }



        return this;
      },


      //没有好说的,与Prototype的observes差不多
      observes: function(event, callback) {
        if (!isString(event)) { callback = event; event = null; }
        if (isString(callback)) callback = this[callback];

        return (this.$listeners || []).some(function(i) {
          return (event && callback) ? i.e === event && i.f === callback :
            event ? i.e === event : i.f === callback;
        });
      },
      
      stopObserving: function(event, callback) {
        if (isHash(event)) {
          for (var key in event) {
            this.stopObserving(key, event[key]);
          }
        } else {
          if (!isString(event)) { callback = event; event = null; }
          if (isString(callback)) callback = this[callback];

          this.$listeners = (this.$listeners || []).filter(function(i) {
            return (event && callback) ? (i.e !== event || i.f !== callback) :
              (event ? i.e !== event : i.f !== callback);
          }, this);
        }

        return this;
      },

      //用于返回不重复回调函数,如果不指明事件名则返回全部
      listeners: function(event) {
        return (this.$listeners || []).filter(function(i) {
          return !event || i.e === event;
        }).map(function(i) { return i.f; }).uniq();
      },
      //强制执行其对象上的事件
      fire: function() {
        var args = $A(arguments), event = args.shift();

        (this.$listeners || []).each(function(i) {
          if (i.e === event) i.f.apply(this, i.a.concat(args));
        }, this);

        return this;
      },

      extend: {

        //让所有对象都具有观察者模式
        create: function(object, events) {
          $ext(object, Object.without(this.prototype, 'initialize', 'setOptions'), true);
          return this.createShortcuts(object, events || Class.findSet(object, 'events'));
        },

        createShortcuts: function(object, names) {
          //为目标对象object扩展像onLayoutChange的自定义事件
          (names || []).each(function(name) {

            var method_name = 'on'+name.replace(/:/g, '_').camelize().capitalize();
            if (!defined(object[method_name])) {
              object[method_name] = function() {
                return this.on.apply(this, [name].concat($A(arguments)));
              };
            }
          });

          return object;
        }
      }
    });
    //对observe进行别名
    $alias(Observer.prototype, { observe: 'on' });

观察者模式让自定类也拥有像事件系统的关联操作,一变大家跟着变,非常有利于解耦。从最初的Class到Options到Observer,它把能拆解的东西都拆解了。这种模块化设计非常有利于我们学习。像jQuery,它走的是另一条路,通过大量的插件来弥补其核心库的功能。当然,人多好办事,它也不时吸纳一些优秀的方法加入核心库了。但无论怎么折腾,类库的能力还是很有限,与这种全面走扩展道路的框架没得比。与对于学习javascript来说,像Prototype这样的框架能让你收获更大。

posted on 2010-04-02 17:03  司徒正美  阅读(786)  评论(0编辑  收藏  举报