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

devtools/2025/2/2 11:09:32/

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

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

五、完整代码

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


http://www.ppmy.cn/devtools/155424.html

相关文章

【gRPC-gateway】初探grpc网关,插件安装,默认实现,go案例

grpc-gateway https://github.com/grpc-ecosystem/grpc-gateway 作用 通过反向代理的方式&#xff0c;将grpc server接口转为httpjson api 使用场景 向后兼容支持grpc不支持的语言或客户端 单纯用grpc实现的服务端代码&#xff0c;只能用grpc客户端调用&#xff0c;&#…

Vue组件开发-使用 html2canvas 和 jspdf 库实现PDF文件导出 设置页面大小及方向

在 Vue 项目中实现导出 PDF 文件、调整文件页面大小和页面方向的功能&#xff0c;使用 html2canvas 将 HTML 内容转换为图片&#xff0c;再使用 jspdf 把图片添加到 PDF 文件中。以下是详细的实现步骤和代码示例&#xff1a; 步骤 1&#xff1a;安装依赖 首先&#xff0c;在项…

计算机毕业设计Python+CNN卷积神经网络高考推荐系统 高考分数线预测 高考爬虫 协同过滤推荐算法 Vue.js Django Hadoop 大数据毕设

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

Kafka下载

一、Kafka下载 下载地址&#xff1a;https://kafka.apache.org/downloads 二、Kafka安装 因为选择下载的是 .zip 文件&#xff0c;直接跳过安装&#xff0c;一步到位。 选择在任一磁盘创建空文件夹&#xff08;不要使用中文路径&#xff09;&#xff0c;解压之后把文件夹内容…

electron typescript运行并设置eslint检测

目录 一、初始化package.json 二、安装依赖 三、项目结构 四、配置启动项 五、补充&#xff1a;ts转js别名问题 已整理好的开源代码&#xff1a;Type-Electron: 用typescript开发的electron项目脚手架&#xff0c;轻量级、支持一键配置网页转PC - Gitee.com 一、初始化pac…

github汉化

本文主要讲述了github如何汉化的方法。 目录 问题描述汉化步骤1.打开github&#xff0c;搜索github-chinese2.打开项目&#xff0c;打开README.md3.下载安装脚本管理器3.1 在README.md中往下滑动&#xff0c;找到浏览器与脚本管理器3.2 选择浏览器对应的脚本管理器3.2.1 点击去…

DeepSeek r1本地安装全指南

环境基本要求 硬件配置 需要本地跑模型&#xff0c;兼顾质量、性能、速度以及满足日常开发需要&#xff0c;我们需要准备以下硬件&#xff1a; CPU&#xff1a;I9内存&#xff1a;128GB硬盘&#xff1a;3-4TB 最新SSD&#xff0c;C盘确保有400GB&#xff0c;其它都可划成D盘…

[免费]微信小程序智能商城系统(uniapp+Springboot后端+vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序智能商城系统(uniappSpringboot后端vue管理端)&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序智能商城系统(uniappSpringboot后端vue管理端) Java毕业设计_哔哩哔哩_bilibili 项目介绍…