Ruby's Louvre

每天学习一点点算法

导航

javascript 超级数组对象

此类依赖于第一类工厂。

下面的东西基本给IE用的

 
 var dom = {},//命名空间
      slice = Array.prototype.slice;
      dom.mixin = function (obj, bag) {
        if (arguments.length === 1) {
          bag = obj;
          obj = this;
        };
        if (obj && bag && typeof bag === 'object') {
          for (var p in bag) {
            if(bag.hasOwnProperty(p))
              obj[p] = bag[p];
          }
        };
        if (!+"\v1") {//IE不能在for...in循环中遍历toString与valueOf属性,需要单独处理
          var t = bag.toString,v = bag.valueOf,op = Object.prototype;
          if (bag.hasOwnProperty("toString") && typeof t === "function" && t !== op.toString)
            obj.toString = t;
          if (bag.hasOwnProperty("valueOf") && typeof v === "function" && v !== op.valueOf)
            obj.valueOf = v;
        }
        return obj;
      };
      dom.factory = function(obj){//第一类工厂
        var init = obj.init,
        klass = function() {
          //如果传入参数与当前类是同一类型,则直接返回
          if(arguments.length === 1 && arguments[0] instanceof klass)
            return arguments[0];
          return new klass.fn.init(arguments);
        }
        klass.fn = klass.prototype = {
          init :init,
          constructor: klass
        };
        klass.fn.init.prototype = klass.fn;
        delete obj.klass;delete obj.init;
        dom.mixin(klass.fn, obj);
        //用于扩展原型方法
        klass.mixin = function(bag){
          dom.mixin(this.fn,bag);
          return this;
        };
        klass.alias = function(oldName, newName){
          var bag = {};
          if (dom.isString(oldName) && dom.isString(newName)){
            var method = this.fn[oldName]
            if (!!method){
              bag[newName] = method;
              return this.mixin(bag);
            };
          };
          //如果是一个属性包,如Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});
          bag = oldName;
          for (var name in bag)
            if(bag.hasOwnProperty(name))
              this.alias(name,bag[name]);
          return this;
        };
        klass.staticizeWithout = function(arr){
          var conditions = {},keys = arr || [],me = this;
          for(var i=0,n = keys.length;i<n;i++){
            conditions[keys[i]] = 1;
          }
          dom.each(me.fn,function(method, name){
            if(!conditions[name] && dom.isFunction(me.fn[name]) && dom.isUndefined(me[name])&&
              name !== 'init' && name !== 'toString' && name !== 'valueOf' ){
              me[name] = function () {
                var args = dom.toArray(arguments),
                caller = args.shift();
                method.name = name; //为其泛化方法添加一个name属性
                return method.apply(me(caller), args);
              }
            }
          });
          return me;
        }
        return klass;
      };
      dom.mixin(new function(){
        var _toString = Object.prototype.toString,
        _slice = Array.prototype.slice,
        _push = Array.prototype.push,
        is = function(obj,type) {
          return _toString.call(obj).match(/^\[object\s(.*)\]$/)[1] === type;
        }
        return {
          isArray: function (obj) {
            return is(obj,"Array");
          },
          isFunction: function (obj) {
            return is(obj,"Function") ;
          },
          isNumber: function (obj) {
            return is(obj,"Number") ;
          },
          isString: function (obj) {
            return is(obj,"String") ;
          },
          isUndefined: function (obj) {
            return  obj === void(0);
          },
          each: function (obj, fn, bind) {
            for (var key in obj) //只遍历本地属性
              if (obj.hasOwnProperty(key))
                fn.call(bind, obj[key], key, obj);
          },
          isArrayLike : function (obj) {//包括Array
            if(dom.isArray(obj) || obj.callee) return true;
            if(is(obj,'NodeList')) return true;
            if(is(obj,'HTMLCollection')) return true;
            //不能为字符串,不能为window,具有length属性
            if(dom.isNumber(obj.length) && !dom.isString(obj) && !obj.eval){
              if(obj.nextNode || obj.item)
                return true;
              var n = obj.length - 1 < 0 ? 0 : obj.length - 1 ;
              //如果是具有数字键或length属性的对象,如jQuery对象
              if(obj.hasOwnProperty(n) && obj.hasOwnProperty(0))
                return true
              return false;
            }
            return false;
          },
          toArray : function (arr) { //把普通对象变成原生数组对象
            if(arguments.length === 0 || arr === null){
              return [];
            }else if(arr.callee){//如果是Arguments对象
              return _slice.call(arr);
            }else if(dom.isArray(arr)){//如果Array对象返回一个克隆
              return arr.concat();
            }else if(dom.isArrayLike(arr)){
              try{//jQuery对象,dom对象,el.getElementsByTagName得到的HTMLCollection
                //与el.childNodes得到的NodeList
                return _slice.call(arr);
              }catch(e){//IE用slice处理元素或节点集合会出错,只能慢慢拷贝
                var ret = [], i = arr.length;
                while (i) ret[--i] = arr[i]; //Clone数组
                return ret;
              }
            }else {//普通函数,单个元素节点,字符串,数字,window对象
              return [arr];
            }
          },
          setArray: function (els) { //把普通对象变成类数组对象
            this.length = 0;
            _push.apply(this, els);
            return this;
          }
        }
      });

其核心函数:

 
     var array = dom.factory({
        init: function (obj) {
          //这里的obj总是为Arguments对象
          if (obj.length === 1) {
            var first = obj[0];
            if (dom.isArray(first)) {
              //将原生数组变成超级数组
              this.setArray(first);
            } else if (dom.isNumber(first)) {
              //用一个数字初始化成数组,再用此数组变成超级数组
              this.setArray(Array(first));
            } else if (dom.isArrayLike(first)) {
              //将类组数转化为原生数组,再将原生数组变成超级数组
              this.setArray(this.toArray(first));
            } else {//如果传入单个字符串,window,函数
              this.setArray([first]);
            }
          } else {//如果传入的是1,2,3,4,
            this.setArray(this.toArray(obj));
          }
        },
        toString: function () { //返回一个字符串
          return this.valueOf().toString();
        },
        valueOf: function () {//获得里面的数组对象
          return  slice.call(this);
        },
        get: function (index) {//获得里面的数组对象或数组元素
          return arguments.length ? this[index]: this.valueOf();
        },
        setArray: dom.setArray,
        toArray: dom.toArray
      });

其他方法

 
      //为超级数组对象添加原生数组对象的泛化方法
      dom.each("pop,push,reverse,shift,sort,unshift,join,indexOf, \
              lastIndexOf,forEach,every,some,reduce,reduceRight".match(/\w+/g),
      function (name) {
        if (dom.isFunction(Array.prototype[name]))
          array.fn[name] = Array.prototype[name];
      });
      dom.each({
        //为超级数组对象添加自定义方法与改造了的泛化方法
        concat: function () {
          var arr = slice.call(this),
          ret;
          if (arguments.length === 1) {
            var first = arguments[0]
            if (first instanceof dom.array) {
              ret = arr.concat(first.valueOf())
            } else {
              ret = arr.concat(first)
            }
          } else {
            var args = this.toArray(arguments);
            ret = arr.concat(args)
          }
          return array(ret);
        },
        splice: function () {//改变原对象
          var args = this.toArray(arguments),
          arr = this.valueOf(),
          ret = Array.prototype.splice.apply(arr, args);
          this.setArray(arr); //修正原数组
          return array(ret); //返回被删除元素组成的数组
        },
        slice: function () {//不会改变原对象
          var args = this.toArray(arguments),
          arr = this.valueOf(),
          ret = slice.apply(arr, args);
          return array(ret);
        },
        inArray: function (el) {
          if(Array.indexOf){
            return this.valueOf().indexOf(el) !== -1;
          }
          for (var i = 0, n = this.length; i < n; i++)
            if (this[i] === el) return true;
          return false;
        },
        indexOf: function (el, index) {
          var n = this.length,
          i = index == null ? 0 : index < 0 ? Math.max(0, n + index) : index;
          for (; i < n; i++)
            if (i in this && this[i] === el) return i;
          return -1
        },
        //返回在数组中搜索到的与给定参数相等的元素的最后(最大)索引。
        lastIndexOf: function (el, index) {
          var n = this.length,
          i = index == null ? n - 1 : index;
          if (i < 0) i = Math.max(0, n + i);
          for (; i >= 0; i--)
            if (i in this && this[i] === el) return i;
          return -1
        },
        //对数组中的每个元素都执行一次指定的函数(fn)
        forEach: function (fn, bind) {
          for (var i = 0, n = this.length; i < n; i++)
            i in this && fn.call(bind, this[i], i, this)
        },
        //对数组中的每个元素都执行一次指定的函数(f),并且创建一个新的超级数组,
        //该数组元素是所有回调函数执行时返回值为 true 的原数组元素。
        filter: function (fn, bind) {
          if (Array.filter) {
            return array(this.valueOf().filter(fn, bind));
          } else {
            var results = array();
            this.forEach( function(value, index) {
              fn.call(bind, value, index, this) && results.push(value);
            });
            return results;
          }
        },
        without:function(){//去掉与传入参数相同的元素
          var args = array(arguments)
          return this.filter(function (el) {
            return !args.contains(el);
          });
        },
        one : function(fn,bind){//类似上面,但只返回一个匹配者
          for (var i = 0, n = this.length; i < n; i++)
            if(i in this && fn.call(bind, this[i], i, this))
              return this[i]
          return null;
        },
        //对数组中的每个元素都执行一次指定的函数(f),将它们的返回值放到一个新的超级数组
        map: function (fn, bind) {
          if (Array.map) {
            return array(this.valueOf().map(fn, bind))
          } else {
            var results = array();
            this.forEach(function(value, index) {
              results.push(fn.call(bind, value, index, this));
            });
            return results;
          }
        },
        //如果数组中每一个元素都满足参数中提供的测试函数,则返回真。
        every: function (fn,bind) {
          for (var i = 0, n = this.length; i < n; i++)
            if (i in this && !fn.call(bind, this[i], i, this)) return false;
          return true
        },
        //如果数组中至少有一个元素满足参数函数的测试,则返回真。
        some: function (fn, bind) {
          for (var i = 0, n = this.length; i < n; i++)
            if (i in this && fn.call(bind, this[i], i, this)) return true;
          return false
        },
        // 用回调函数迭代地将数组简化为单一的值。
        // 如果指定了可选参数 initial,该参数将被当成是数组中的第一个值来处理,
        // 或者如果数组为空的话就作为最终返回值。如果数组为空并且没有传递
        // initial 参数,则返回 null。
        // 它回调函数的定义function(previousValue, currentValue, index, array){}
        // 相当于Prototype中的inject
        reduce: function (fn, initial, bind) {
          if (this.length == 0) return initial;
          var i = initial !== undefined ? 0 : 1;
          var ret = initial !== undefined ? initial : this[0];
          for (var n = this.length; i < n; i++)
            ret = fn.call(bind, ret, this[i], i, this);
          return ret;
        },
        reduceRight: function (fn, initial, bind) {
          if (this.length == 0) return initial;
          var i = initial !== undefined ? this.length - 1 : this.length - 2;
          var ret = initial !== undefined ? initial : this[this.length - 1];
          for (; i >= 0; i--) {
            ret = fn.call(bind, ret, this[i], i, this);
          }
          return ret;
        },
        isEmpty: function () {
          return this.length == 0;
        },
        //如果不指明位置,默认是插在最前面
        insertAt: function (item, position) {
          this.splice(position, 0, item);
          return this;
        },
        //http://msdn.microsoft.com/zh-cn/library/bb383786.aspx
        //移除 Array 对象中某个元素的第一个匹配项。
        remove: function (item) {
          var index = this.indexOf(item);
          if (index != -1) this.removeAt(index);
          return item;
        },
        //移除 Array 对象中指定位置的元素。
        removeAt: function (index) {
          return this.splice(index, 1)
        },
        //将数组对象变成普通对象,以传入数组的元素作为键,原来数组的元素作为值
        toObject: function (keys) {
          var obj = {},length,i;
          if(!!keys && dom.isArray(keys)){
            length = Math.min(this.length, keys.length);
            for(i = 0; i < length; i++)
              obj[keys[i]] = this[i];
          }else{
            for(i = 0,length=this.length;i<length;i++)
              obj[i] = this[i]
          }
          return obj;
        },
        first: function () {
          return this[0];
        },
        last: function () {
          return this[this.length - 1];
        },
        clear: function () {
          for (var i = this.length - 1; i >= 0; i--)
            delete this[i];
          this.length = 0;
          return this;
        },
        swap: function (index1, index2) {
          var arr = this;
          if (index1 < 0) index1 += arr.length; // starting from the end
          if (index2 < 0) index2 += arr.length;
          arr[index1] = arr[index1] ^ arr[index2];
          arr[index2] = arr[index2] ^ arr[index1];
          arr[index1] = arr[index1] ^ arr[index2];
          return this;
        },
        shuffle: function () {
          // Jonas Raoni Soares Silva
          //http://jsfromhell.com/array/shuffle [v1.0]
          for (var j, x, i = this.length; i;
          j = parseInt(Math.random() * i), x = this[--i], this[i] = this[j], this[j] = x);
          return this;
        },
        max: function () {
          return Math.max.apply({}, this.valueOf());
        },
        min: function () {
          return Math.min.apply({}, this.valueOf());
        },
        compact: function () {
          return this.filter(function (el) {
            return el != null;
          });
        },
        difference: function (other) {
          var len = Math.max(this.length, other.length),
          i, p, ret = array(),
          diff = {};
          for (i = 0; i < len; i++) {
            if (i < this.length) diff[this[i]] = diff[this[i]] ? 2 : 1;
            if (i < other.length) diff[other[i]] = diff[other[i]] ? 2 : 1;
          }
          for (p in diff)
            if (diff[p] == 1) ret.push(p);
          return ret;
        },
        flatten: function () {
          var _flatten = function () {
            for (var ret = [], i = 0, n = arguments.length; i < n; i++) {
              var el = arguments[i];
              dom.isArray(el) ? ret.push.apply(ret, _flatten.apply(null, el)) : ret.push(el)
            }
            return ret
          };
          var arr = _flatten(this.valueOf());
          this.setArray(arr);
          return this;
        },
        norepeat: function () {//返回数组中只出现过一次的元素
          var arr = this.valueOf(),results=[];
          for(var i=0,n=arr.length,tmp={};i<n;i++){
            tmp[arr[i]]?tmp[arr[i]]++:(tmp[arr[i]]=1);
          }
          for(var p in tmp){
            if(tmp[p]===1)
              results.push(p);
          }
          return array(results);
        },
        ensure: function (item) { //如果原来数组不存在就合并它
          if (!this.inArray(item)) this.push(item);
          return this;
        },
        combine: function (array) { //取两个数组的并集
          for (var i = 0, l = array.length; i < l; i++)
            this.ensure(array[i]);
          return this;
        },
        unique: function () { //取数组中唯一的元素并放进新数组中返回
          return array().combine(this);
        }
      },function(method,name){
        if(!dom.isFunction(array.fn[name])){
          array.fn[name] = method;
        }
      });
      array.alias({
        forEach:"each",
        inArray:"contains"
      });
      array.staticizeWithout(['toArray','setArray']);
      dom.array = array;

下面测试代码:

超级数组对象
pop 同Array.pop
push 同Array.push
reverse 同Array.reverse,不过返回的是超级数组
sort 同Array.sort,不过返回的是超级数组
splice 同Array.splice,不过返回的是超级数组
slice 同Array.slice,不过返回的是超级数组
join 同Array.join
unshift 同Array.unshift,将新加的参数添加到原对象的前面
shift 同Array.shift
indexOf 返回给定元素在超级数组中的索引值,没有则返回-1
lastIndexOf 返回给定元素在超级数组中的索引值,没有则返回-1,只不过这次是从后面数起
forEach或each 类似Array.forEach,迭代执行回调函数
map 类似Array.map,迭代执行回调函数,把执行结果放到一个新超级数组对象返回
filter 类似Array.filter,迭代执行回调函数,把符合条件的键值对放到一个新超级数组对象返回
every 类似Array.every,如果所有元素都满足回调函数,为真
some 类似Array.some,只要有一个满足回调函数就为真
one 类似Array.one,返回第一个满足回调函数的元素
without 过滤与传入参数相同的元素,放进一个新超级数组返回
reduce 用回调函数迭代地将数组简化为单一的值。 如果指定了可选参数 initial,该参数将被当成是数组中的第一个值来处理, 或者如果数组为空的话就作为最终返回值。如果数组为空并且没有传递 initial 参数,则返回 null。 回调函数的样子应该为function(previousValue, currentValue, index, array){}
reduceRight 同reduce,不过自右到左执行
isEmpty 判断对象是否为空
insertAt 在指定的索引值后插入新元素
remove 移除第一个与参数相同的元素
removeAt 移除指定位置上的元素
toObject 以传入数组的元素作为键,原超级数组的值作为值,构造一个普通对象
first 返回第一个元素
last 返回最后一个元素
clear 清空所有元素
swap 交换两个索引值上的元素
shuffle 对超级数组的元素进行乱序
max 取得数值最大的元素
min 取得数值最小的元素
compact 移除超级数组中的null元素与undefined元素
flatten 对超级数组进行平整化处理,确保是一维的超级数组
norepeat 以超级数组形式返回超级数组中所有只出现过一次的元素,
unique 去掉超级数组中的重复元素
ensure 只有原超级数组中没有此元素才允许加入它,返回新的超级对象
inArray与contains 检测超级数组是否已存在与参数相同的元素
combine 与传入(超级)数组取并集
difference 与传入(超级)数组取并集后再减去它的交集,换言之,取对方都没有的元素组成的一个新的超级对象

posted on 2009-11-17 15:58  司徒正美  阅读(3788)  评论(0编辑  收藏  举报