[英]javascript create array of objects with a fixed length based on another bigger array
[英]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
}
原答案:
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.freeze
是ES2015 的一部分, 但大多數瀏覽器似乎都支持它,包括 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。
目前的答案是肯定的,你可以。 有幾種方法可以做到這一點,但一些網絡瀏覽器有它自己的“解釋”。
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 的代碼在這里。
一個包裝了一個數組的類(最好不要拋出異常)
使用 ES2015 代碼應該可以使用以下解決方案,但不能:
var x = new Array(10).fill(0); Object.freeze( x.length ); x.push(3); console.log(x);
new Array
構造函數但是,創建的數組填充了undefined
。 因此使其不可迭代。 您可以改為使用null
或0
值填充它。
new Array(100).fill(null).map(() => ...);
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.