在JS中,数组不像其他语言(java、python)中那样安全,它具有动态性和弱类型性,切越界访问没有具体的报错,而是返回空,为提升数组的安全性,我们可以自行定义一个安全数组。
一、增加报错
与其他语言一样,增加IndexError,继承内置的Error对象。示例如下:
javascript
class IndexError extends Error {
constructor(message) {
super(message);
this.name = "索引越界";
}
}
这样,我们就可以通过throw语句,抛出new IndexError()异常。
二、定义安全数组类SafeArray
这里,可以使用ES6语法来定义,结构比较简单,也容易理解,示例如下:
javascript
class SafeArray {
#_array;
constructor(...initialArray) {
// 约定的私有属性
this.#_array = [...initialArray];
}
}
注意:上面代码中的 # 表示定义一个私有属性或方法(也就是说,只能在内部访问,不能在类的外部进行访问。),并不是所有的编译器都支持。因为它是ECMAScript 2022新增的语法。
三、添加你想要的getter和setter
1、返回长度
javascript
// 获取数组的长度
get length() {
return this.#_array.length;
}
这样,我们调用new SafeArray().length,就能得到安全数组的长度
2、可以添加sum属性
javascript
// 求和
get sum() {
return this.#_array.reduce((s, elt) => s+=elt, 0);
}
这样,调用.sum,就能计算数组中各元素相加的和,壮大了内置数组Array的功能。照这个思路,还可以添加更多的聚合函数,如求平均、最值等等。
四、编写安全数组的方法
确定好结构,与必要的属性之后,我们需要为安全数组提供一些必要的方法,如安全的获取元素,安全的添加元素,安全的查找元素等等。示例如下:
javascript
#_isValidIndex(index) {
return Number.isInteger(index) && index >= 0 && index < this.#_array.length;
}
// 安全地获取索引处的值,如果索引无效则返回undefined
getItem(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);
}
上述代码中,如果数组索引超出范围,就会抛出索引越界的错误,这是内置数组做不到的。
五、完整代码
javascript
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;
}
// 安全地获取索引处的值,如果索引无效则返回undefined
getItem(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();
}
// join
join(delimiter) {
return this.#_array.join(delimiter);
}
// concat
concat(...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);
}
// reduce
reduce(callback, init) {
if(callback === undefined) callback = function(){};
this.#_notFuncError(callback, "callback");
return this.#_array.reduce(callback, init);
}
// forEach
forEach(callback) {
if(callback === undefined) callback = function(){};
this.#_notFuncError(callback, "callback");
this.#_array.forEach(callback);
}
// Map
map(callback) {
if(callback === undefined) callback = function(){};
this.#_notFuncError(callback, "callback");
return this.#_array.map(callback);
}
// filter
filter(conditionFunction) {
if(conditionFunction === undefined) conditionFunction = function(){return true};
this.#_notFuncError(conditionFunction, "conditionFunction");
return this.#_array.filter(conditionFunction);
}
// find
find(callback) {
if(callback === undefined) callback = function(){};
this.#_notFuncError(callback, "callback");
return this.#_array.find(callback);
}
// findIndex
findIndex(callback) {
if(callback === undefined) callback = function(){};
this.#_notFuncError(callback, "callback");
return this.#_array.findIndex(callback);
}
// every
every(conditionFunction, context) {
if(conditionFunction === undefined) conditionFunction = function(){return false};
this.#_notFuncError(conditionFunction, "conditionFunction");
return this.#_array.every(conditionFunction, context);
}
// some
some(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类是一个功能丰富且安全的数组实现,它通过封装和私有化内部状态,提供了对数组操作的更高层次的控制和安全性。尽管在某些方面可能存在性能开销,但它为需要严格数据完整性和安全性的场景提供了有用的工具。