当前位置: 首页 > article >正文

JS面相对象小案例:自定义安全数组

在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;
    }

    // 安全地获取索引处的值,如果索引无效则返回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);
    }

上述代码中,如果数组索引超出范围,就会抛出索引越界的错误,这是内置数组做不到的。

五、完整代码

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类是一个功能丰富且安全的数组实现,它通过封装和私有化内部状态,提供了对数组操作的更高层次的控制和安全性。尽管在某些方面可能存在性能开销,但它为需要严格数据完整性和安全性的场景提供了有用的工具。


http://www.kler.cn/a/521629.html

相关文章:

  • [JavaWeb]搜索表单区域
  • Swing使用MVC模型架构
  • 【python】subprocess.Popen执行adb shell指令进入linux系统后连续使用指令,出现cmd窗口阻塞问题
  • 小白爬虫冒险之反“反爬”:无限debugger、禁用开发者工具、干扰控制台...(持续更新)
  • spring中解决循环依赖的方法
  • 洛谷U525322 优美区间
  • vue项目中,如何获取某一部分的宽高
  • XSS 漏洞全面解析:原理、危害与防范
  • vector二分
  • docker如何查看容器启动命令(已运行的容器)
  • 2025蓝桥杯JAVA编程题练习Day1
  • Linux的基本指令(下)
  • PPT教程:怎样在PPT中嵌入视频或音频文件?
  • 使用openwrt搭建ipsec隧道
  • ML基础3-sklearn中的1个简单的分类器例子
  • 【自然语言处理(NLP)】深度循环神经网络(Deep Recurrent Neural Network,DRNN)原理和实现
  • Redis 教程
  • WebAssembly(Wasm)详解
  • 【Elasticsearch】中数据流需要配置索引模板吗?
  • 警用地理信息平台(PGIS 2.0)建设方案
  • 1.1第1章DC/DC变换器的动态建模-1.1状态平均的概念--电力电子系统建模及控制 (徐德鸿)--读书笔记
  • AI常见的算法
  • 《AI芯片:如何让硬件与AI计算需求完美契合》
  • Unity Essentialsials练习题
  • 基于 STM32 的动态 LED 灯光控制系统项目
  • 2239. 找到最接近 0 的数字