Is it possible to create a fixed length array in javascript?

后端 未结 9 1100
春和景丽
春和景丽 2020-12-03 00:59

Is it possible, in Javascript, to create an array whose length is guaranteed to remain the same?

For example, the array A is created with length 2. Subs

相关标签:
9条回答
  • 2020-12-03 01:52

    Update:

    Object.seal (which is part of ES2015) will do just that:

    // create array with 42 empty slots
    let a = new Array(42);
    
    if(Object.seal) {
      // fill array with some value because
      // empty slots can not be changed after calling Object.seal
      a.fill(undefined);
    
      Object.seal(a);
      // now a is a fixed-size array with mutable entries
    }
    

    Original Answer:

    Almost. As was suggested by titusfx you can freeze the object:

    let 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
    

    However you are unable to change the values of a freezed object. If you have an array of objects this may not be a problem since you can still change the values of the objects.

    For arrays of numbers there are of course typed arrays.

    Object.freeze is part of ES2015 but most browsers seem to support it, including IE9. You could of course feature-test it:

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

    0 讨论(0)
  • 2020-12-03 01:54

    Update:

    The accepted answer shows how this issue can now be solved using Object.seal which wasn't available at the time.

    Original Answer:

    So, it seems that the answer to the original question is simply 'No'. It is not possible to create a native javascript Array with a fixed length.

    But, you can create an object which will behave like a fixed-length Array. Following the suggestions in the comments, I've come up with 2 possible implementations, both with pros and cons.

    I haven't figured out which of the 2 I'm going to use in my project yet. I'm not 100% satisfied with either. Please let me know if you have any ideas for improving them (I am keen to make these objects as fast and efficient as possible because I'm going to need lots of them).

    Code for both implementations below, together with QUnit tests illustrating usage.

    // 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');     
    
        });
    
    
    });
    
    0 讨论(0)
  • 2020-12-03 02:00

    Actually to create a fully optimized true c like fixed array in js on most modern browsers (including IE 11) you could use: TypedArray or ArrayBuffer like so:

    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

    0 讨论(0)
提交回复
热议问题