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

Dan*_*ard 20 javascript arrays

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

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

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

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

tim*_*-we 13

更新:

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

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

原答案:

几乎.正如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
Run Code Online (Sandbox Code Playgroud)

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

对于数字数组,当然有类型数组.

Object.freezeES2015的一部分,但大多数浏览器似乎都支持它,包括IE9.你当然可以对它进行功能测试:

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


Raj*_*eev 9

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

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

基本上,如果长度将大于 5,它将填充数组中的项目,直到长度达到 5。它会弹出 las 项目数组。所以它将保持长度始终为 5。


小智 6

实际上,要在大多数现代浏览器(包括 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
Run Code Online (Sandbox Code Playgroud)

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


Dan*_*ard 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');     

    });


});
Run Code Online (Sandbox Code Playgroud)

  • 我希望你的结论是清楚的:不要那样做. (3认同)

nul*_*ook 5

  1. 使用new Array构造函数

但是,创建的数组充满了undefined. 从而使其不可迭代。null您可以用或0值来填充它。

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