在JS中,數組不像其他語言(java、python)中那樣安全,它具有動態性和弱類型性,切越界訪問沒有具體的報錯,而是返回空,為提升數組的安全性,我們可以自行定義一個安全數組。
一、增加報錯
與其他語言一樣,增加IndexError,繼承內置的Error對象。示例如下:
class IndexError extends Error {constructor(message) {super(message);this.name = "索引越界";}
}
這樣,我們就可以通過throw語句,拋出new IndexError()異常。
二、定義安全數組類SafeArray
這里,可以使用ES6語法來定義,結構比較簡單,也容易理解,示例如下:
class SafeArray {#_array;constructor(...initialArray) {// 約定的私有屬性this.#_array = [...initialArray];}
}
注意:上面代碼中的 # 表示定義一個私有屬性或方法(也就是說,只能在內部訪問,不能在類的外部進行訪問。),并不是所有的編譯器都支持。因為它是ECMAScript 2022新增的語法。
三、添加你想要的getter和setter
1、返回長度
// 獲取數組的長度get length() {return this.#_array.length;}
這樣,我們調用new SafeArray().length,就能得到安全數組的長度
2、可以添加sum屬性
// 求和get sum() {return this.#_array.reduce((s, elt) => s+=elt, 0);}
這樣,調用.sum,就能計算數組中各元素相加的和,壯大了內置數組Array的功能。照這個思路,還可以添加更多的聚合函數,如求平均、最值等等。
四、編寫安全數組的方法
確定好結構,與必要的屬性之后,我們需要為安全數組提供一些必要的方法,如安全的獲取元素,安全的添加元素,安全的查找元素等等。示例如下:
#_isValidIndex(index) {return Number.isInteger(index) && index >= 0 && index < this.#_array.length;}// 安全地獲取索引處的值,如果索引無效則返回undefinedgetItem(index) {if (this.#_isValidIndex(index)) {return this.#_array[index];}throw new IndexError("數組索引超出范圍");}// 安全地設置索引處的值,如果索引無效則不進行操作setItem(index, value) {if (this.#_isValidIndex(index)) {this.#_array[index] = value;} else {throw new IndexError("數組索引超出范圍");}}// 獲取指定元素的索引indexOf(value, start) {return this.#_array.indexOf(value, start); // 不存在返回 -1}// 判斷某個元素是否包含在數組中(適用于判斷對象數組或較為復雜的數組中的元素,但存在性能影響)contains(value) {let arr = this.#_array;for (let i = 0; i < arr.length; i++) {if (JSON.stringify(arr[i]) === JSON.stringify(value)) return true;}return false;}// 簡單的判斷某個元素是否包含在數組中includes(value) {return this.#_array.includes(value);}// 切片slice(start, end) {return this.#_array.slice(start, end);}
上述代碼中,如果數組索引超出范圍,就會拋出索引越界的錯誤,這是內置數組做不到的。
五、完整代碼
class IndexError extends Error {constructor(message) {super(message);this.name = "索引越界";}
}class SafeArray {constructor(...initialArray) {// 約定的私有屬性this.#_array = [...initialArray];}// 獲取數組的長度get length() {return this.#_array.length;}// 求和get sum() {return this.#_array.reduce((s, elt) => s+=elt, 0);}// 求平均get average() {if(this.length === 0) throw new Error("數組為空,無法計算");return this.sum / this.length;}// 最大值get max() {return Math.max(...this.#_array);}// 最小值get min() {return Math.min(...this.#_array);}// 返回數組的維度(復雜度較高)get dimension() {let r = 0, max = 0;let stack = [{ array: this.#_array, depth: 0 }];while (stack.length > 0) {let { array, depth } = stack.pop();if (Array.isArray(array)) {r = depth + 1;// 將當前數組的所有元素推入棧中,并增加深度for (let item of array) {stack.push({ array: item, depth: r });}// 更新最大維度max = Math.max(max, r);}}return max;}// 安全地獲取索引處的值,如果索引無效則返回undefinedgetItem(index) {if (this.#_isValidIndex(index)) {return this.#_array[index];}throw new IndexError("數組索引超出范圍");}// 安全地設置索引處的值,如果索引無效則不進行操作setItem(index, value) {if (this.#_isValidIndex(index)) {this.#_array[index] = value;} else {throw new IndexError("數組索引超出范圍");}}// 獲取指定元素的索引indexOf(value, start) {return this.#_array.indexOf(value, start); // 不存在返回 -1}// 判斷某個元素是否包含在數組中(適用于判斷對象數組或較為復雜的數組中的元素,但存在性能影響)contains(value) {let arr = this.#_array;for (let i = 0; i < arr.length; i++) {if (JSON.stringify(arr[i]) === JSON.stringify(value)) return true;}return false;}// 簡單的判斷某個元素是否包含在數組中includes(value) {return this.#_array.includes(value);}// 切片slice(start, end) {return this.#_array.slice(start, end);}// 添加到數組的開頭unshift(value) {this.#_array.unshift(value);}// 添加元素到數組末尾push(value) {this.#_array.push(value);}// 移除數組末尾的元素pop() {return this.#_array.pop();}// 移除數組開頭的元素shift() {return this.#_array.shift();}// joinjoin(delimiter) {return this.#_array.join(delimiter);}// concatconcat(...other) {return this.#_array.concat(...other);}// 在指定索引處插入元素,如果索引無效則插入到末尾insert(index, value) {if (this.#_isValidIndex(index)) {this.#_array.splice(index, 0, value);} else {this.#_array.push(value);}}// 移除指定索引的元素,如果索引無效則不進行操作remove(index) {if (this.#_isValidIndex(index)) {return this.#_array.splice(index, 1)[0];} else {throw new IndexError("數組索引超出范圍");}}// 返回數組的字符串表示toString() {return this.#_array.toString();}// 排序sort(callback) {if(callback === undefined) callback = function(){return undefined};this.#_notFuncError(callback, "callback");return this.#_array.sort(callback);}// reducereduce(callback, init) {if(callback === undefined) callback = function(){};this.#_notFuncError(callback, "callback");return this.#_array.reduce(callback, init);}// forEachforEach(callback) {if(callback === undefined) callback = function(){};this.#_notFuncError(callback, "callback");this.#_array.forEach(callback);}// Mapmap(callback) {if(callback === undefined) callback = function(){};this.#_notFuncError(callback, "callback");return this.#_array.map(callback);}// filterfilter(conditionFunction) {if(conditionFunction === undefined) conditionFunction = function(){return true};this.#_notFuncError(conditionFunction, "conditionFunction");return this.#_array.filter(conditionFunction);}// findfind(callback) {if(callback === undefined) callback = function(){};this.#_notFuncError(callback, "callback");return this.#_array.find(callback);}// findIndexfindIndex(callback) {if(callback === undefined) callback = function(){};this.#_notFuncError(callback, "callback");return this.#_array.findIndex(callback);}// everyevery(conditionFunction, context) {if(conditionFunction === undefined) conditionFunction = function(){return false};this.#_notFuncError(conditionFunction, "conditionFunction");return this.#_array.every(conditionFunction, context);}// somesome(conditionFunction, context) {if(conditionFunction === undefined) conditionFunction = function(){return false};this.#_notFuncError(conditionFunction, "conditionFunction");return this.#_array.some(conditionFunction, context);}// 檢查是不是數組static isArray(arr) {return Array.isArray(arr);}// 檢查是不是安全數組static isSafeArray(arr) {return arr instanceof SafeArray;}// 檢查索引是否有效#_isValidIndex(index) {return Number.isInteger(index) && index >= 0 && index < this.#_array.length;}// 不是函數的固定報錯#_notFuncError(fn, c) {if(typeof fn !== "function") throw new TypeError("參數" + c + "不是函數");}// 私有屬性#_array;}
上述是一個完整的SafeArray類是一個功能豐富且安全的數組實現,它通過封裝和私有化內部狀態,提供了對數組操作的更高層次的控制和安全性。盡管在某些方面可能存在性能開銷,但它為需要嚴格數據完整性和安全性的場景提供了有用的工具。