簡體   English   中英

是否可以在 javascript 中創建一個固定長度的數組?

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

是否可以在 Javascript 中創建一個長度保證保持不變的數組?

例如,創建的數組A長度為 2。隨后,任何調用A.push()A.pop()或設置A[5]值的嘗試都將失敗。 A.length總是 2。

這是類型化數組(例如Float32Array )已經工作的方式。 它們有固定的尺寸。 但我想要一種在常規數組上獲得相同行為的方法。

對於我的特定情況,我想創建一個固定長度的數組,其中每個條目都是一個對象。 但我仍然想知道一般問題的答案。

更新:

Object.seal (這是 ES2015 的一部分)將做到這一點:

// 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
}

原答案:

幾乎。 正如titusfx所建議的那樣,您可以凍結對象:

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

但是,您無法更改凍結對象的值。 如果您有一個對象數組,這可能不是問題,因為您仍然可以更改對象的值。

對於數字數組,當然有類型數組

Object.freezeES2015 的一部分, 但大多數瀏覽器似乎都支持它,包括 IE9 您當然可以對其進行功能測試:

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

更新:

接受的答案顯示了這一問題,現在可以使用來解決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');     

    });


});

實際上,要在大多數現代瀏覽器(包括 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

你可以像這樣簡單地使用。

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

基本上它會填充數組中的項目,直到長度變為 5,如果長度大於 5。它彈出 las 項目數組。 因此它將始終保持長度為 5。

目前的答案是肯定的,你可以。 有幾種方法可以做到這一點,但一些網絡瀏覽器有它自己的“解釋”。

  1. 使用 FireFox Mozzila 控制台測試的解決方案

 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 ]

需要注意的是,如果數組是對象,則需要執行深度凍結。 deepfreeze 的代碼在這里

  1. 一個包裝了一個數組的類(最好不要拋出異常)

  2. 使用 ES2015 代碼應該可以使用以下解決方案,但不能:

 var x = new Array(10).fill(0); Object.freeze( x.length ); x.push(3); console.log(x);
在注意部分檢查此頁面

  1. 使用new Array構造函數

但是,創建的數組填充了undefined 因此使其不可迭代。 您可以改為使用null0值填充它。

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

我寫了一個數組固定的https://github.com/MatrixAI/js-array-fixed這是一個庫,為您提供固定長度的數組和固定長度的密集數組(數組的元素總是向左折疊或向右折疊)。

它支持許多標准的數組操作,例如 splice 和 slice。 但是將來可以添加更多操作。

push的概念沒有意義,而是有caret*方法可以插入一個元素並將已經存在的元素推送到空槽中。

我們可以對這類問題使用閉包。 我們只是固定數組大小並從函數返回一個函數。

 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);

您可以實現一個具有容量的類。 假設您希望在推入數組時長度保持為 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)

如果數組為空,則 Array.pop 已經失敗。 如果推送會違反固定大小,您希望推送失敗 - 所以不要使用 Array.push 而是使用函數:

function arrayPush(array,size,value){
    if(array.length==size) return false;
    else {
       array.push(value);
       return true;
    }
}

我使用不同類型的固定長度數組來保存諸如最近文件之類的內容。 在這種情況下,您可以繼續推送,數組將僅存儲最后固定數量的項目。 記住 Array.push 添加到數組的末尾,以便推送另一個項目,您使用 splice(0,1) 刪除數組的第一個項目。

function arrayPush2(array,size,value){
    if(array.length==size){
        array.splice(0,1);
    }
    array.push(value);
}

我知道這是一個老問題,但現在有一個節點模塊可以做到這一點,稱為固定數組

當使用 shift 和 push 時,必須在固定長度數組所需的長度控制之前或之后,在添加或拒絕新項目之前選擇從數組的開頭或結尾刪除項目..我最快的解決方案是。 如果只使用鍵來訪問數組可以很容易控制並期望固定大小的行為。 對象和數組都可以使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM