【JavaScript 进阶】面向对象编程与 jQuery 式 API 的实现

JavaScript 简洁、灵活,jQuery 优雅、强大 —— 它们共同开拓了新的思维方式,影响了一代程序员和他们的 Web 前端开源项目,这是毋庸置疑的~

要想让它们的简洁、灵活、优雅、强大为我所用,内化为编程上的精神法宝,亲手“临摹”一下既有趣又有力。一番死磕之后,jQuery 中蕴含的 JavaScript 面向对象的思想方法 自然就很容易理解了~

山寨版 jQuery 半成品

目前仅简单地实现了最常用的选择器(标签、ID、类、first/last/only/nth-child 伪类)、选择器上下文最常用的 jQuery 对象方法(each、find、on、off 等)、连缀语法等 ——

// ----------- xDOM  Core Object ----------- //
function DOMxx(Selector, Context) {
  this.xDOM = '0.5';
  this.jquery = '1.9.0'; //  Work with the version of jQuery API

  // ----------- Atom Functions (Private Method) ----------- //
  function Each(Object, CallBack) {
    for (var key in Object)
      if (key != 'length') {
        var _RV_ = CallBack.call(
          Object[key],
          isNaN(Number(key)) ? key : Number(key)
        );
        if (_RV_ === false) break;
      }
  }
  function inArray(Value, iArray) {
    for (var i = 0; i < iArray.length; i++) if (iArray[i] === Value) return i;
    return false;
  }
  function Child_PC(EOS, PCS) {
    var PES = [],
      CES = [];
    Each(EOS, function() {
      if (inArray(this.parentNode, PES) === false) PES.push(this.parentNode);
    });
    var PCS_KW = PCS.match(/^(\\w+)-child/i)[1];
    if (PCS_KW == 'nth') var PCS_EC = PCS.match(/^nth-child\\((.+)\\)/i)[1];
    Each(PES, function() {
      EOS = this.children;
      if (!EOS) return;
      switch (PCS_KW) {
        case 'first': {
          CES.push(EOS[0]);
          return;
        }
        case 'last': {
          CES.push(EOS[EOS.length - 1]);
          return;
        }
        case 'only': {
          if (EOS.length == 1) CES.push(EOS[0]);
          return;
        }
        case 'nth': {
          PCS_EC = PCS_EC.replace(/(\d+)(\w|\()/g, '$1 * $2');
          for (var n = 1, N = 0; ; n++) {
            N = eval(PCS_EC);
            if (N >= EOS.length) return;
            CES.push(EOS[N]);
          }
        }
      }
    });
    return CES;
  }
  function $_Atom(REO, SE) {
    SE = SE.split(':');
    if (SE.length > 1) var PC = SE[1];
    SE = SE[0];
    switch (SE.slice(0, 1)) {
      case '#': {
        REO = self.document;
        MN = 'getElementById';
        SE = SE.slice(1);
        break;
      }
      case '.': {
        MN = 'getElementsByClassName';
        SE = SE.slice(1);
        break;
      }
      case '*':
      default:
        MN = 'getElementsByTagName';
    }
    REO = REO[MN](SE);
    REO = PC ? Child_PC(REO, PC) : REO;
    return !REO.length
      ? [REO]
      : REO instanceof Array
      ? REO
      : [].slice.apply(REO);
  }
  function Select(Selector, Context) {
    var Selector_Group = Selector.split(' '),
      Level = 0,
      Select_Result = [];
    Each($_Atom(Context, Selector_Group[Level]), function(Index) {
      if (!Index) Level++;
      while (Selector_Group[Level] == '') Level++;
      if (!Selector_Group[Level]) {
        Select_Result.push(this);
        return;
      }
      if (!this.childNodes.length) return;
      Each($_Atom(this, Selector_Group[Level]), arguments.callee);
    });
    return Select_Result;
  }
  function Event_Bind(_Element, _Type, _CallBack, _unBind) {
    var _CB_ = function(Event) {
      EO = self.event || Event;
      EO.target = EO.srcElement || EO.target;
      if (!_CallBack.call(_Element, EO))
        if (EO.returnValue) {
          EO.returnValue = false;
          EO.cancelBubble = true;
        } else {
          EO.preventDefault();
          EO.stopPropagation();
        }
    };
    if (_Element.attachEvent)
      _Element[(_unBind ? 'de' : 'at') + 'tachEvent'](
        'on' + (_Type == 'input' ? 'propertychange' : _Type),
        _CB_
      );
    else
      _Element[(_unBind ? 'remove' : 'add') + 'EventListener'](
        _Type,
        _CB_,
        false
      );
  }

  // ----------- Powerful API (Public Method / Data) ----------- //
  this.selector = Selector;
  this.context = Context || self.document;
  this.DOM = Select(Selector, this.context);
  if (this.DOM.length == 1) this.context = this.DOM[0];

  this.get = function(Index) {
    return this.DOM[Index];
  };
  this.each = function(CallBack) {
    Each(this.DOM, CallBack);
    return this;
  };
  this.find = function(Selector) {
    return new this.constructor(
      [this.selector, Selector].join(' '),
      this.context
    );
  };
  this.on = function(Event_Type, CallBack) {
    return this.each(function() {
      Event_Bind(this, Event_Type, CallBack);
    });
  };
  this.off = function(Event_Type, CallBack) {
    return this.each(function() {
      Event_Bind(this, Event_Type, CallBack, true);
    });
  };
}
// ----------- xDOM  Shortcut Function ----------- //
function xDOM(CSS_Selector, DOM_Context) {
  return new DOMxx(CSS_Selector, DOM_Context);
}

有木有一种步 Zepto.js 后尘的感觉……?

【参考文献】

  1. http://www.cnblogs.com/newyorker/archive/2013/02/14/2891298.html

上一篇
【网页重构】CSS 规则清理(浏览器 BookMarkLet、FireBug 插件) 【网页重构】CSS 规则清理(浏览器 BookMarkLet、FireBug 插件)
很多程序猿认为 —— 最蛋疼的工作不在于重写或创新,而是“修改前任程序猿留下的代码”…… 对前端工程师来说,若不进行彻底的网页重构,而只是修补现有网页的代码(多是之前由 后端工程师 乃至 只会点儿 DreamWeaver 的运维网管 写的)
2014-09-30
下一篇
【浏览器小工具】网页浮层广告/锁屏一键清除(完善重发) 【浏览器小工具】网页浮层广告/锁屏一键清除(完善重发)
我之前写的【网页浮动广告一键清除工具】虽然不断完善至 v0.3,但对各种实现方式的浮层广告 适配得不全面,自己在使用过程中发现有些网站的浮层广告依然无法彻底清除(某个很亮堂的党报的官网 新闻阅读页的右下角广告 竟然比某些盗版资源站的还顽固)
2014-09-25