的JavaScript。创建类似Array的对象。是否有可能使.length自动增量?

时间:2015-01-29 09:05:23

标签: javascript arrays object

关于类似数组的对象的创建和继承有很多,但我的一个问题仍然没有答案:是否有可能以某种方式向对象添加自动递增.length属性?
示例脚本:

function ArrayLike(){
    this.length=0;
    this.splice=[].splice;
}
var Arr=new ArrayLike();
Arr[0]=0;
Arr[1]=1;
alert(Arr.length); //alerts "0" instead of "2"

当然,我可以添加一些方法,如:

function ArrayLike(){
    this.length=0;
    this.splice=[].splice;
    this.push=function(toadd){
        this[this.length++]=toadd;
    }
}

但这不是我想做的事。

当我以Arr[N]='blah-blah-blah'的方式向对象添加内容时,可以增加.length吗?

1 个答案:

答案 0 :(得分:3)

  

是否有可能以某种方式添加自动增量.length属性

不是真的;至少,你不能Array执行length和索引属性所做的所有事情。

在启用ES5的环境中,您可以使用getter和setter函数使length属性成为可能。但找出length应该是什么感到尴尬,因为那里(到目前为止)没有"赶上所有"在以下代码中设置它的方法:

arr[1] = "foo";

...运行。 (ES6很可能通过代理人拥有全能的财产制定者。)

所以你的length必须从对象内容中找出它,这不会非常有效,例如:

var explicitLength;
Object.defineProperty(this, "length", {
    get: function() {
        var length;

        // Find our length from our max index property
        length = Object.keys(this).reduce(function(maxIndex, key) {
            var index = key === "" ? NaN : +key;
            if (!isNaN(index) && index > maxIndex) {
                return index;
            }
            return maxIndex;
        }, -1) + 1;

        // If we have an explicitly-set length, and it's greater,
        // use that instead. Note that if explicitLength is
        // `undefined`, the condition will always be false.
        if (explicitLength > length) {
            length = explicitLength;
        }

        return length;
    },
    set: function(value) {
        explicitLength = value;
        // You might choose to have code here removing properties
        // defining indexes >= value, likes Array does
    }
});

请注意,即使我们有明确设定的长度,我们仍然需要检查自然长度是否更大,以便处理:

arr.length = 2;
arr[5] = "foo";
console.log(arr.length); // Should be 6, not 2

在属性检索方面做了那么多工作显然不是一件好事,所以我会避开这类事情。


上面我说" ...所以我会避开这类事情。"

嗯,这一切都非常好,但是你应该做些什么呢?

假设您有可用的功能niftyspiffy。您有两个现实的选择:

  1. 使用实际数组,并将自定义方法混合到每个数组实例

  2. 加强Array.prototype

  3. #1使用实际数组并混合您的方法

    您可以在方便的对象上定义您的方法,例如ArrayMixin

    var ArrayMixin = {
        nifty: function() {
            // ...do something nifty with `this`
        },
        spiffy: function() {
            // ...do something spiffy with `this`
        }
    };
    

    然后有一个用于创建数组的构建器函数:

    function createArray(arr) {
        arr = arr || []; // `arr` is optional
        extend(arr, ArrayMixin);
        return arr;
    }
    

    你问这个extend函数是什么?将属性从一个对象复制到另一个对象的东西。如果你使用任何库或框架(jQuery,Underscore,Angular,PrototypeJS,......),他们可能会有某种extend函数来执行此操作。通常看起来像这样的某事(这是非常不合适的)。

    function extend(target) {
        var arg, obj, key;
        for (arg = 1; arg < arguments.length; ++arg) {
            obj = arguments[arg];
            for (key in obj) {
                if (obj.hasOwnProperty(key)) {
                    target[key] = obj[key];
                }
            }
        }
        return target;
    }
    

    请注意,这是一个浅层副本,并且它不会尝试使其添加到target的属性不可枚举。根据需要调整。

    那么当你想要一个数组时:

    var a = createArray(['one', 'two', 'three']);
    

    ... a上有niftyspiffy

    示例:

    &#13;
    &#13;
    var ArrayMixin = {
      nifty: function() {
        this.forEach(function(entry, index) {
          this[index] = entry.toUpperCase();
        }, this);
        return this;
      },
      spiffy: function() {
        this.forEach(function(entry, index) {
          this[index] = entry.toLowerCase();
        }, this);
        return this;
      }
    };
    
    function createArray(arr) {
      arr = arr || []; // `arr` is optional
      extend(arr, ArrayMixin);
      return arr;
    }
    
    function extend(target) {
      var arg, obj, key;
      for (arg = 1; arg < arguments.length; ++arg) {
        obj = arguments[arg];
        for (key in obj) {
          if (obj.hasOwnProperty(key)) {
            target[key] = obj[key];
          }
        }
      }
      return target;
    }
    
    var a = createArray(['one', 'two', 'three']);
    a.nifty();
    snippet.log(a.join(", "));
    a.spiffy();
    snippet.log(a.join(", "));
    &#13;
    <!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
    <script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
    &#13;
    &#13;
    &#13;

    增强Array.prototype

    人们对增强Array.prototype感到愤怒,但它确实没有错。导致问题的唯一原因是,如果人们使用需要for-in的破坏代码来循环遍历数组索引,您必须增强原型而不使新方法不可枚举。但同样,那段代码被打破了; for-in 不是用于循环遍历数组索引,它用于循环遍历对象属性。

    所以这里有Array.prototype

    (function(aproto) {
        var add;
        if (Object.defineProperty) {
            // Function to add a non-enumerable property
            add = function(obj, prop, value) {
                Object.definePrperty(obj, prop, {
                    value: value
                });
            };
        } else {
            // Old JavaScript engine, oh well
            add = function(obj, prop, value) {
                obj[prop] = value;
            };
        }
    
        add(aproto, "nifty", function() {
            // ...do something nifty with `this`
        });
    
        add(aproto, "spiffy", function() {
            // ...do something spiffy with `this`
        });
    
    })(Array.prototype);
    

    现在,使用它真的很干净:

    var a = ['one', 'two', 'three'];
    

    ... a上有niftyspiffy

    示例:

    &#13;
    &#13;
    (function(aproto) {
      var add;
      if (Object.defineProperty) {
        // Function to add a non-enumerable property
        add = function(obj, prop, value) {
          Object.defineProperty(obj, prop, {
            value: value
          });
        };
      } else {
        // Old JavaScript engine, oh well
        add = function(obj, prop, value) {
          obj[prop] = value;
        };
      }
    
      add(aproto, "nifty", function() {
        this.forEach(function(entry, index) {
          this[index] = entry.toUpperCase();
        }, this);
      });
    
      add(aproto, "spiffy", function() {
        this.forEach(function(entry, index) {
          this[index] = entry.toLowerCase();
        }, this);
      });
    
    })(Array.prototype);
    
    
    var a = ['one', 'two', 'three'];
    a.nifty();
    snippet.log(a.join(", "));
    a.spiffy();
    snippet.log(a.join(", "));
    &#13;
    <!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
    <script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
    &#13;
    &#13;
    &#13;