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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/67106.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

本地大模型编程实战(02)语义检索(2)

文章目录 准备按批次嵌入加载csv文件&#xff0c;分割文档并嵌入测试嵌入效果总结代码 上一篇文章&#xff1a; 本地大模型编程实战(02)语义检索(1) 详细介绍了如何使用 langchain 实现语义检索&#xff0c;为了演示方便&#xff0c;使用的是 langchain 提供的内存数据库。 在实…

windows平台intel-vpl编译

需要先在本机编译好opencl库 git clone --recursive https://github.com/KhronosGroup/OpenCL-SDK.git cmake -A x64 -T v143 -D OPENCL_SDK_BUILD_OPENGL_SAMPLESOFF -B OpenCL-SDK\build -S OpenCL-SDKcmake --build OpenCL-SDK\build --config Releasecmake --install O…

[C语言日寄]exit函数的使用及其拓展

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋&#xff1a;这是一个专注于C语言刷题的专栏&#xff0c;精选题目&#xff0c;搭配详细题解、拓展算法。从基础语法到复杂算法&#xff0c;题目涉及的知识点全面覆盖&#xff0c;助力你系统提升。无论你是初学者&#xff0c;还是…

激光雷达和相机早期融合

通过外参和内参的标定将激光雷达的点云投影到图像上。 • 传感器标定 首先需要对激光雷达和相机&#xff08;用于获取 2D 图像&#xff09;进行外参和内参标定。这是为了确定激光雷达坐标系和相机坐标系之间的转换关系&#xff0c;包括旋转和平移。通常采用棋盘格等标定工具&…

iic、spi以及uart

何为总线&#xff1f; 连接多个部件的信息传输线&#xff0c;是部件共享的传输介质 总线的作用&#xff1f; 实现数据传输&#xff0c;即模块之间的通信 总线如何分类&#xff1f; 根据总线连接的外设属于内部外设还是外部外设将总线可以分为片内总线和片外总线 可分为数…

DeepSeek明确学术研究方向效果如何?

明确学术研究方向 在学术写作中&#xff0c;选择一个出色的研究主题至关重要&#xff0c;因为它直接关系到论文是否能登上高级别的学术期刊。不少学者在这个过程中走入了误区&#xff0c;他们往往将大把的时间花在写作本身&#xff0c;而忽略了对选题的深入思考&#xff0c;这…

WPF实战案例 | C# WPF实现大学选课系统

WPF实战案例 | C# WPF实现大学选课系统 一、设计来源1.1 主界面1.2 登录界面1.3 新增课程界面1.4 修改密码界面 二、效果和源码2.1 界面设计&#xff08;XAML&#xff09;2.2 代码逻辑&#xff08;C#&#xff09; 源码下载更多优质源码分享 作者&#xff1a;xcLeigh 文章地址&a…

STM32 GPIO配置 点亮LED灯

本次是基于STM32F407ZET6做一个GPIO配置&#xff0c;实现点灯实验。 新建文件 LED.c、LED.h文件&#xff0c;将其封装到Driver文件中。 双击Driver文件将LED.c添加进来 编写头文件&#xff0c;这里注意需要将Driver头文件声明一下。 在LED.c、main.c里面引入头文件LED.h LED初…

window保存好看的桌面壁纸

1、按下【WINR】快捷键调出“运行”窗口&#xff0c;输入以下命令后回车。 %localappdata%\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets 2、依次点击【查看】【显示】&#xff0c;勾选【隐藏的项目】&#xff0c;然后按【CtrlA】全部…

TCP 三次握手四次挥手

目录 TCP 三次握手 1. SYN (Synchronize&#xff1a;同步) 2. SYN-ACK (Synchronize Acknowledge&#xff1a;同步确认) 3. ACK (Acknowledge&#xff1a;确认) 为什么是三次而不是两次或四次&#xff1f; 三次握手的作用 TCP 四次挥手 第一次挥手&#xff1a;客户端发送 FIN …

C语言初阶牛客网刷题—— HJ34 图片整理【难度:中等】

1. 题目描述 牛客网在线OJ链接 Lily上课时使用字母数字图片教小朋友们学习英语单词&#xff0c;每次都需要把这些图片按照大小&#xff08;ASCII码值从小到大&#xff09;排列收好。请大家给Lily帮忙&#xff0c;通过C语言解决。 输入描述&#xff1a;Lily使用的图片包括 “A…

MVCC底层原理实现

MVCC的实现原理 了解实现原理之前&#xff0c;先理解下面几个组件的内容 1、 当前读和快照读 先普及一下什么是当前读和快照读。 当前读&#xff1a;读取数据的最新版本&#xff0c;并对数据进行加锁。 例如&#xff1a;insert、update、delete、select for update、 sele…

python实现http文件服务器访问下载

//1.py import http.server import socketserver import os import threading import sys# 获取当前脚本所在的目录 DIRECTORY os.path.dirname(os.path.abspath(__file__))# 设置服务器的端口 PORT 8000# 自定义Handler&#xff0c;将根目录设置为脚本所在目录 class MyHTT…

Cpp::静态 动态的类型转换全解析(36)

文章目录 前言一、C语言中的类型转换二、为什么C会有四种类型转换&#xff1f;内置类型 -> 自定义类型自定义类型 -> 内置类型自定义类型 -> 自定义类型隐式类型转换的坑 三、C强制类型转换static_castreinterpret_castconst_castdynamic_cast 四、RTTI总结 前言 Hell…

2024年终总结:技术成长与突破之路

文章目录 前言一、技术成长&#xff1a;菜鸟成长之路1. 学习与实践的结合2. 技术分享与社区交流 二、生活与事业的平衡&#xff1a;技术之外的思考1. 时间管理与效率提升2. 技术对生活的积极影响 三、突破与展望&#xff1a;未来之路1. 技术领域的突破2. 未来规划与目标 四、结…

ComfyUI实现老照片修复——AI修复老照片(ComfyUI-ReActor / ReSwapper)解决天坑问题及加速pip下载

AI修复老照片&#xff0c;试试吧&#xff0c;不一定好~~哈哈 2023年4月曾用过ComfyUI&#xff0c;当时就感慨这个工具和虚幻的蓝图很像&#xff0c;以后肯定是专业人玩的。 2024年我写代码去了&#xff0c;AI做图没太关注&#xff0c;没想到&#xff0c;现在ComfyUI真的变成了工…

思科交换机telnet配置案例

目录 1.telnet简述2.网络拓扑3.设备说明4.网络配置4.1 电脑PC ip设置4.2 网络交换机telnet配置 5.小结 1.telnet简述 Telnet是远程登录服务的一个协议&#xff0c;该协议定义了远程登录用户与服务器交互的方式。它允许用户在一台联网的计算机上登录到一个远程分时系统中&#…

WPS数据分析000006

一、排序 开始→ 排序 同文件→选项→自定义序列→输入序列 二、筛选 高级筛选 条件区域要与列表区域一样。 三、条件格式

vofa++使用方法

控件区可以添加控件用来啊多样显示 点击一个控件然后右键可以选择要添加显示的数据&#xff0c;点all表全部显示&#xff0c; 点auto可以自动布局 要用控件需要选择协议&#xff0c;不知道协议具体格式可以点击问号看看&#xff0c;并且最好用printf重定义来实现 比如我要实现F…

达梦拷贝DM_HOME的复制安装

近期一个项目需求&#xff0c;需要在没有安装包的情况下&#xff0c;将达梦数据库安装到虚机上&#xff08;生产机上安装了达梦&#xff09;&#xff0c;故采用直接打包生产机DM_HOME的方式拷贝至虚机&#xff0c;再依次执行达梦的部分指令完成安装。以下为验证的步骤&#xff…