是否可以在javascript中创建一个固定长度的数组?

时间:2014-02-24 13:15:18

标签: javascript arrays

在Javascript中,是否有可能创建一个长度保证保持不变的数组?

例如,创建长度为2的数组A.随后,任何调用A.push()或A.pop()或设置A [5]的值的尝试都将失败。 A.length总是2。

这是键入数组(例如Float32Array)的工作方式。它们有固定的尺寸。但是我想要一种在常规数组上获得相同行为的方法。

对于我的具体情况,我想创建一个固定长度的数组,其中每个条目都是一个对象。但我仍然想知道一般问题的答案。

11 个答案:

答案 0 :(得分:8)

<强>更新

Object.seal(这是ES2015的一部分)就是这样做的:

var a = new Array(42);
if(Object.seal) { 
  Object.seal(a);
  // now a is a fixed-size array with mutable entries
}

原始答案:

几乎。正如titusfx所建议的那样,您可以冻结对象:

var a = new Array(2);

// set values, e.g.
a[0] = { b: 0; }
a[1] = 0;

Object.freeze(a);

a.push(); // error
a.pop(); // error
a[1] = 42; // will be ignored
a[0].b = 42; // still works

但是,您无法更改冻结对象的值。 如果你有一个对象数组,这可能不是问题,因为你仍然可以 更改对象的值。

对于数字数组,当然有typed arrays

Object.freezeES2015 but most browsers seem to support it, including IE9的一部分。你当然可以对它进行功能测试:

if(Object.freeze) { Object.freeze(obj); }

答案 1 :(得分:5)

<强>更新

接受的答案显示现在可以使用Object.seal来解决此问题 的问题。

原始答案:

所以,原来问题的答案似乎只是“不”。无法创建具有固定长度的本机javascript数组。

但是,您可以创建一个行为类似于固定长度数组的对象。根据评论中的建议,我提出了两种可能的实现方式,包括优点和缺点。

我还没想出我将在我的项目中使用哪2个。我对这两者都不是100%满意。如果您对改进它们有任何想法,请告诉我(我希望尽可能快速有效地制作这些物品,因为我将需要大量的物品)。

以下两种实现的代码,以及说明使用情况的QUnit测试。

// Version 1
var FixedLengthArrayV1 = function(size) {
    // create real array to store values, hidden from outside by closure
    var arr = new Array(size);
    // for each array entry, create a getter and setter method
    for (var i=0; i<size; i++) {FixedLengthArrayV1.injectArrayGetterSetter(this,arr,i);}
    // define the length property - can't be changed
    Object.defineProperty(this,'length',{enumerable:false,configurable:false,value:size,writable:false});
    // Could seal it at this point to stop any other properties being added... but I think there's no need - 'length' won't change, so loops won't change 
    // Object.seal(this);
};
// Helper function for defining getter and setter for the array elements
FixedLengthArrayV1.injectArrayGetterSetter = function(obj,arr,i) {
    Object.defineProperty(obj,i,{enumerable:true,configurable:false,get:function(){return arr[i];},set:function(val){arr[i]=val;}});
};
// Pros:  Can use square bracket syntax for accessing array members, just like a regular array, Can loop just like a regular array
// Cons:  Each entry in each FixedLengthArrayV1 has it's own unique getter and setter function - so I'm worried this isn't very scalable - 100 arrays of length 100 means 20,000 accessor functions in memory


// Version 2
var FixedLengthArrayV2 = function(size) {
    // create real array to store values, hidden from outside by closure
    var arr = new Array(size);
    this.get = function(i) {return arr[i];}
    this.set = function(i,val) {
        i = parseInt(i,10);
        if (i>=0 && i<size) {arr[i]=val;}
        return this;
    }
    // Convenient function for looping over the values
    this.each = function(callback) {
        for (var i=0; i<this.length; i++) {callback(arr[i],i);}
    };
    // define the length property - can't be changed
    Object.defineProperty(this,'length',{enumerable:false,configurable:false,value:size,writable:false});
};
// Pros:  each array has a single get and set function to handle getting and setting at any array index - so much fewer functions in memory than V1
// Cons:  Can't use square bracket syntax.  Need to type out get(i) and set(i,val) every time you access any array member - much clumsier syntax, Can't do a normal array loop (need to rely on each() helper function)



// QUnit tests illustrating usage
jQuery(function($){

    test("FixedLengthArray Version 1",function(){

        // create a FixedLengthArrayV2 and set some values
        var a = new FixedLengthArrayV1(2);
        a[0] = 'first';
        a[1] = 'second';

        // Helper function to loop through values and put them into a single string
        var arrayContents = function(arr) {
            var out = '';
            // Can loop through values just like a regular array
            for (var i=0; i<arr.length; i++) {out += (i==0?'':',')+arr[i];}
            return out;
        };

        equal(a.length,2);
        equal(a[0],'first');
        equal(a[1],'second');
        equal(a[2],null);
        equal(arrayContents(a),'first,second');

        // Can set a property called '2' but it doesn't affect length, and won't be looped over
        a[2] = 'third';
        equal(a.length,2);
        equal(a[2],'third');
        equal(arrayContents(a),'first,second');

        // Can't delete an array entry
        delete a[1];
        equal(a.length,2);
        equal(arrayContents(a),'first,second');

        // Can't change the length value
        a.length = 1;
        equal(a.length,2);
        equal(arrayContents(a),'first,second');

        // No native array methods like push are exposed which could let the array change size
        var errorMessage;
        try {a.push('third');} catch (e) {errorMessage = e.message;}
        equal(errorMessage,"Object [object Object] has no method 'push'");
        equal(a.length,2);
        equal(arrayContents(a),'first,second');     

    });

    test("FixedLengthArray Version 2",function(){


        // create a FixedLengthArrayV1 and set some values
        var a = new FixedLengthArrayV2(2);
        a.set(0,'first');
        a.set(1,'second');

        // Helper function to loop through values and put them into a single string
        var arrayContents = function(arr) {
            var out = '';
            // Can't use a normal array loop, need to use 'each' function instead
            arr.each(function(val,i){out += (i==0?'':',')+val;});
            return out;
        };

        equal(a.length,2);
        equal(a.get(0),'first');
        equal(a.get(1),'second');
        equal(a.get(2),null);
        equal(arrayContents(a),'first,second');

        // Can't set array value at index 2
        a.set(2,'third');
        equal(a.length,2);
        equal(a.get(2),null);
        equal(arrayContents(a),'first,second');

        // Can't change the length value
        a.length = 1;
        equal(a.length,2);
        equal(arrayContents(a),'first,second');

        // No native array methods like push are exposed which could let the array change size      
        var errorMessage;
        try {a.push('third');} catch (e) {errorMessage = e.message;}
        equal(errorMessage,"Object [object Object] has no method 'push'");
        equal(a.length,2);
        equal(arrayContents(a),'first,second');     

    });


});

答案 2 :(得分:1)

目前的答案是肯定的,你可以。有几种方法可以做到这一点,但有些网络浏览器有它自己的解释&#34;。

  1. 使用FireFox Mozzila控制台测试解决方案
  2. &#13;
    &#13;
    var x = new Array(10).fill(0);
    // Output: undefined
    Object.freeze(x);
    // Output: Array [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
    x.push(11)
    // Output: TypeError: can't define array index property past the end of an array with non-writable length
    x.pop()
    // Output: TypeError: property 9 is non-configurable and can't be deleted [Learn More]
    x[0]=10
    // Output: 10 // You don't throw an error but you don't modify the array
    x
    // Output: Array [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]   
    &#13;
    &#13;
    &#13;

    重要的是要注意,如果数组是对象,则需要进行深度冻结。 Deepfreeze的代码是here

    1. 包装数组的类(如果您不想抛出异常,情况会更好)

    2. 使用ES2015代码应该可以使用以下解决方案,但它没有:

    3. &#13;
      &#13;
      var x = new Array(10).fill(0);
      Object.freeze( x.length );
      x.push(3);
      console.log(x);
      &#13;
      &#13;
      &#13; 检查此page in the section Note

答案 3 :(得分:1)

实际上要在大多数现代浏览器(包括IE 11)中的js中创建一个完全优化的真实c类固定数组,你可以使用:TypedArray或ArrayBuffer,如下所示:

var int16 = new Int16Array(1); // or Float32Array(2)
int16[0] = 42;
console.log(int16[0]); // 42
int16[1] = 44;
console.log(int16[1]); // undefined

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray

答案 4 :(得分:1)

您可以像这样简单地使用。

let myArray = [];
function setItem (array, item, length) {
  array.unshift(item) > length ?  array.pop() : null
}
// Use Like this
setItem(myArray, 'item', 5);

基本上,它将填充数组中的项目,直到长度变为5(如果长度可以磨碎5)。它将弹出las item数组。因此它将始终保持长度为5。

答案 5 :(得分:1)

  1. 使用 new Array 构造函数

然而,创建的数组是用 undefined 填充的。因此使其不可迭代。您可以改为使用 null0 值填充它。

new Array(100).fill(null).map(() => ...);
  1. 使用Array.from方法
Array.from({ length: n }, (_,i) => i) 

答案 6 :(得分:0)

我编写了一个数组固定的https://github.com/MatrixAI/js-array-fixed,它是一个为您提供固定长度数组和固定长度密集数组的数组(数组总是将其元素向左折叠或向右折叠)。

它支持许多标准阵列操作,例如拼接和切片。但是将来可以增加更多的操作。

push的概念没有意义,而是有caret*个方法可用于插入元素并将已存在的元素推出到空插槽中。

答案 7 :(得分:0)

我们可以对此类问题使用闭包。我们只是固定了数组大小,然后从一个函数返回一个函数。

    function setArraySize(size){
   return function(arr, val) {
      if(arr.length == size) {
          return arr;    
       } 
   arr.push(val);
   return arr;
   }
}
let arr = [];
let sizeArr = setArraySize(5); // fixed value for fixed array size.
sizeArr(arr, 1);
sizeArr(arr, 2);
sizeArr(arr, 3);
sizeArr(arr, 4);
sizeArr(arr, 5);
sizeArr(arr, 6);
console.log('arr value', arr);

答案 8 :(得分:0)

您可以实现具有能力的类。假设您要在推入数组时将长度保持为5。如果您运行代码片段,您将看到6未被压入阵列,因为已经满足容量要求。最好的问候。

class capArray{
    constructor(capacity){
    this.capacity = capacity;
    this.arr = [];
}

}

capArray.prototype.push = function(val){
    if(this.arr.length < this.capacity) {
this.arr.push(val);
}
}

var newArray = new capArray(5);
newArray.push(1)
newArray.push(2)
newArray.push(3)
newArray.push(4)
newArray.push(5)
newArray.push(6)
console.log(newArray)
console.log(newArray.arr)

答案 9 :(得分:-1)

我知道这是一个老问题,但现在有一个节点模块就是这样做的fixed-array

答案 10 :(得分:-1)

当使用 shift 和 push 时,必须有固定长度数组所需的长度控制之前或之后,可以选择在添加或拒绝新项目之前从数组的开头或结尾删除项目..我最快的解决方案就是它。 如果仅使用键来访问数组 很容易控制并期望固定大小的行为。对象和数组都可以使用。