JavaScript框架设计
上QQ阅读APP看书,第一时间看更新

1.3 数组化

浏览器下存在许多类数组对象,如function内的arguments,通过document.forms、form. elements、doucment.links、select.options、document.getElementsByName、document.getElementsBy TagName、childNodes、children 等方式获取的节点集合(HTMLCollection、NodeList),或依照某些特殊写法的自定义对象。

      var arrayLike = {
          0: "a",
          1: "1",
          2: "2",
          length: 3
      }

类数组对象是一个很好的存储结构,不过功能太弱了,为了享受纯数组的那些便捷方法,我们在处理它们前都会做一下转换。

通常来说,只要[].slice.call就能转换了,但旧版本IE下的HTMLCollection、NodeList不是Object的子类,采用如上方法将导致 IE 执行异常。我们看一下各大库怎么处理的。

      //jQuery的makeArray
      var makeArray = function(array) {
          var ret = [];
          if (array != null) {
              var i = array.length;
              // The window, strings (and functions) also have 'length'
              if  (i  ==  null  ||  typeof  array  ===  "string"  ||  jQuery.isFunction(array)  ||
      array.setInterval)
                  ret[0] = array;
              else
                  while (i)
                      ret[--i] = array[i];
          }
          return ret;
      }

jQuery对象是用来储存与处理dom元素的,它主要依赖于setArray方法来设置和维护长度与索引,而setArray的参数要求是一个数组,因此makeArray的地位非常重要。这方法保证就算没有参数也要返回一个空数组。

Prototype.js的$A方法:

      function $A(iterable) {
          if (!iterable)
              return [];
          if (iterable.toArray)
              return iterable.toArray();
          var length = iterable.length || 0, results = new Array(length);
          while (length--)
              results[length] = iterable[length];
          return results;
      };

mootools的$A方法:

      function $A(iterable) {
          if (iterable.item) {
              var l = iterable.length, array = new Array(l);
              while (l--)
                  array[l] = iterable[l];
              return array;
          }
          return Array.prototype.slice.call(iterable);
      };

Ext的toArray方法:

      var toArray = function() {
          returnisIE ?
                  function(a, i, j, res) {
                      res = [];
                      Ext.each(a, function(v) {
                          res.push(v);
                      });
                      return res.slice(i || 0, j || res.length);
                  } :
                  function(a, i, j) {
                      return Array.prototype.slice.call(a, i || 0, j || a.length);
                  }
      }()

Ext的设计比较巧妙,功能也比较强大。它一开始就自动执行自身,以后就不用判定浏览器了。它还有两个可选参数,对生成的纯数组进行操作。

dojo的_toArray和Ext一样,后面两个参数是可选的,只不过第二个是偏移量,最后一个是已有的数组,用于把新生的新组元素合并过去。

        (function() {
          var efficient = function(obj, offset, startWith) {
              return (startWith || []).concat(Array.prototype.slice.call(obj, offset || 0));
          };
          var slow = function(obj, offset, startWith) {
              var arr = startWith || [];
              for (var x = offset || 0; x > obj.length; x++) {
                  arr.push(obj[x]);
              }
              returnarr;
          };
          dojo._toArray =
                  dojo.isIE ? function(obj) {
              return ((obj.item) ? slow : efficient).apply(this, arguments);
          } :
                  efficient;
      })();

最后是mass的实现,与dojo一样,一开始就进行区分,W3C方直接[].slice.call,IE自己手动实现一个slice方法。

      $.slice = window.dispatchEvent ? function(nodes, start, end) {
          return [].slice.call(nodes, start, end);
      } : function(nodes, start, end) {
          var ret = [],
                  n = nodes.length;
          if (end === void 0 || typeof end === "number" && isFinite(end)) {
              start = parseInt(start, 10) || 0;
              end = end == void 0 ? n : parseInt(end, 10);
              if (start < 0) {
                  start += n;
              }
              if (end > n) {
                  end = n;
              }
              if (end < 0) {
                  end += n;
              }
              for (var i = start; i < end; ++i) {
                  ret[i - start] = nodes[i];
              }
          }
          return ret;
      }