zoukankan      html  css  js  c++  java
  • 模拟jQuery实现类数组对象

    jQuery最令人惊赞的东西就是那个类数组对象,亦即俗话中的jQuery对象。注意,在jQuery类库中,jQuery是作为命名空间而存在的函数。它拥有许多静态方法,由于函数也是对象,对象就有原型,而jQuery的原型方法是异常频繁地调用它的静态方法。它的第一个原型方法叫init,这是受Prototype类库影响的结果。init方法也可以作为构造函数使用,它new出来的实例就是jQuery对象。在jQuery命名空间的原型中,也有一个叫jquery的属性,起着其他类库版本号的作用,由于名字特殊,也用作识别是否为jQuery对象的作用。换言之,这里有三处可以集中添加方法与属性的地方,jQuery命名空间,用于添加静态方法,jQuery的原型,用于添加原型方法,只要依赖extend来添加,jQuery.prototype.init实例(jQuery对象),它的属性比较少,只有length,prevObject,selector与context。至于jQuery.prototype.init实例的原型,都是往jQuery.prototype里搬。接着下来,我们沿着jQuery的思路,实现一个类数组对象吧。

    首先我们要定义一个类,我称之为eve,它其实是个工厂。

          var eve = function(){
            return new eve.prototype.init(arguments);
          }
    

    如果是数组,传入一个数字,会返回与数字值相等长度的空数组,如果传入几个参数,这几个参数会成为返回数组的元素,如果什么都不会就返回空数组(这种方式一定要使用new操作符)。嘛,由于我们的类数组,用不着百分之一百模拟,就按其方式二生成类数组。

    类数组要求拥有length属性与索引,length容易办,但索引我们要一个个赋值,如果在最前面添加新的元素时,就要重排索引,这很要命,效率很低。不过我们可以用数组的push方法实现。

            setArray : function(els) {
              this.length = 0;//设置length以及重排索引
              Array.prototype.push.apply(this, els);
              return this;
            },
    

    为了确保用得了setArray方法,我们必须保证els为数组,因为里面用到 Array.prototype.push.apply(this, els),看到Array没有?至于push方法,简单,保证调用者拥有length属性就行了。 下面的makeArray,就是将非数组对象转换为数组对象,就算为空,也要用[]包裹起来。

            makeArray : function( arr ) {//把传入参数变成数组
              var ret = [];
              if( arr != null ){    var i = arr.length;
                //单个元素,但window, string、 function有 'length'的属性,加其它的判断
                if( i == null || arr.split || arr.setInterval || arr.call ){
                  ret[0] = arr;
                }else{
                  try{
                    ret = Array.prototype.slice.call(arr)
                  }catch(e){
                    while( i ) ret[--i] = arr[i];//Clone数组
                  }
                }
              }
              return ret;
            },
    

    有了这两个方法,我们就可以写我们的构造方法了。

            init : function(obj){
              this.setArray(this.makeArray(obj));
              return this;
            },
    

    但仅仅是这样无法new实例的,因为这里的this出了些状况。前两个this为eve的实例,第三个返回的this为eve.prototype.init的实例。

    看到没有?报错找不到makeArray方法,换言之eve.prototype.init实例无法调用eve的原型方法。如果是jQuery,就是jQuery对象无法调用其jQuery命名空间对象的原型方法。解决方法很简单,直接把它们两个的原型重叠起来。为其命名空间对象的原型添加方法就是为其真身的原型添加方法。

      eve.prototype.init.prototype = eve.prototype;
    

    我们再向它添加一些方法,看到底能仿真到什么程序。其实上面已经能用索引取值了,即e[1],会弹出2。但toString却是[object Object],而是不数字的序列。在jQuery中有个get()方法,如果不加参数,能取出里面的数组,我们把它的toString方法赋其eve.prototype就是。

            toString : function(){//返回一个字符串
              var array = Array.prototype.slice.call( this );
              return array.toString();
            },
            get: function( num ) {
              return num === undefined ? Array.prototype.slice.call( this ) : this[ num ];
            }
    

    很好,现在如果不用instanceof与typeof判断,光凭感觉,是分辩不出它是数组与类数组了。我们再给添加一些数组方法。

            shift :[].shift,
            push: [].push,
            sort: [].sort,
            pop:  [].pop,
            splice: [].splice,
            concat: [].concat,
            slice: [].slice,
            constructor:eve,
    //********************
         eve.toString = function(){
            return "function Array(){\n    [variant code]\n}"
          }
    

    在jQuery中,它还实现eq方法与index方法来取得元素或元素的索引值,还实现javascript1.6的几个迭代器,each(forEach),map,filter。这样高度仿真的类数组对象让jQuery类库轻易实现了链式操作。但显然,它的filter与map方法比我们在网上找到的实现复杂多了,因为早在jQuery1.0.1中就搞了pushStack方法,用于保存执行push,pop等破坏性操作前的数组对象。

    可能有人对init方法感觉非常不爽,因为它的两种this导致了需要用到两个原型。那是jQuery的init除了有生成jQuery对象的能力,还能充当domReady用。如果是单纯的类数组,我们完全可以去掉它,但生成类数组对象时,我们就一定要用new操作符了。

          var eve = function(){
            this.setArray(this.makeArray(arguments));
            return this;
          }
          eve.prototype = {
            isArray : function( obj ) {
              return Object.prototype.toString.call(obj) === "[object Array]";
            },
            setArray : function(elems) {
              this.length = 0;//设置length以及重排索引
              Array.prototype.push.apply(this, elems);
              return this;
            },
            makeArray : function( arr ) {//把传入参数变成数组
              var ret = [];
              if( arr != null ){    var i = arr.length;
                //单个元素,但window, string、 function有 'length'的属性,加其它的判断
                if( i == null || arr.split || arr.setInterval || arr.call ){
                  ret[0] = arr;
                }else{
                  try{
                    ret = Array.prototype.slice.call(arr)
                  }catch(e){
                    while( i ) ret[--i] = arr[i];//Clone数组
                  }
                }
              }
              return ret;
            },
    
            inArray : function(elem, array) {
              for (var i = 0, length = array.length;i < length; i++)
              // Use === because on IE, window == document
                if (array[i] === elem)
                  return i;
              return -1;
            },
            index:function(el){return this.inArray(el,this)},
            toString : function(){//返回一个字符串
              var array = Array.prototype.slice.call( this );
              return array.toString();
            },
            valueOf:function(){return Array.prototype.slice.call( this );},
            shift :[].shift,
            push: [].push,
            sort: [].sort,
            pop:  [].pop,
            splice: [].splice,
            concat: [].concat,
            slice: [].slice,
            constructor:eve,
            get: function( num ) {
              return num === undefined ? Array.prototype.slice.call( this ) : this[ num ];
            }
          }
          eve.toString = function(){
            return "function Array(){\n    [variant code]\n}"
          }
    
  • 相关阅读:
    OpenCV——Skewing
    OpenCV——PS滤镜算法之Spherize 球面化(凸出效果)
    机器学习 scikit-learn 图谱
    机器视觉 Histogram of oriented gradients
    Python: scikit-image canny 边缘检测
    机器视觉 Local Binary Pattern (LBP)
    Ice php配置
    Windows7下的免费虚拟机(微软官方虚拟机)
    经常使用的webservice接口
    怎样衡量一个公司是否靠谱
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/1612865.html
Copyright © 2011-2022 走看看