Javascript数组研究02_手写实现_at_concat_copyWithin_entries_every

ops/2024/10/20 16:28:02/

目录

1 Array.at()

1.1基本介绍

1.2 手写实现

2 Array.concat()

2.1 基本介绍

2.2 手写实现-获取构造函数与concat实现

3 Array.copyWithin()

3.1 基本介绍

3.2 手写实现

4 Array.entries()

4.1 基本介绍

4.2 手写实现

4.2.1 手写实现返回迭代器对象

4.2.2 使用generator实现迭代器对象

5 Array.every()

5.1 基本介绍

5.2 手写实现


1 Array.at()

1.1基本介绍

        接收一个整数值,返回对应索引的元素,允许正数和负数。负数从-1为数组最后一个元素开始倒数,语法如下:

javascript">Array.at(index)

         边界条件与输入输出、注意事项如下所示:

输入:输入index,首先被转换为整数。

输出:如果index > length 或者 -index < -length 返回undefined,正数就返回对应下标元素,负数返回length +index对应下标元素。

注意事项:at()方法是通用的。

1.2 手写实现

        手写实现代码如下所示:

javascript">// 1. 手写at
// 处理空槽时返回undefined
class MyArray extends Array {
}MyArray.prototype.at = function(index){index = Number(index)const length = this.lengthif(index >= length || -index < -length) return undefinedif(index < 0) return this[index + length]else return this[index]
}const arr_1 = new MyArray(1,2,3)
console.log(arr_1) // output: [ 1, 2, 3 ]
console.log(arr_1.at(1)) // output: 2
console.log(arr_1.at(-1)) // output: 3
console.log(arr_1.at(3)) // output: undefined
console.log(arr_1.at(-4)) // output: undefined
console.log(arr_1.at("-1")) // output: 3
const arr_3 = new MyArray(4)
console.log(arr_3.at(1)) // output: undefined
const arr_2 = new Array(5)
console.log(arr_2.at(1)) // output: undefined
  1.  将索引转换为数组
  2.  判断索引范围的合法性
  3. 根据索引正负返回对应下标元素

2 Array.concat()

2.1 基本介绍

        concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

javascript">concat()
concat(value0)
concat(value0, value1)
concat(value0, value1, /* … ,*/ valueN)

输入: 数组/值,类数组(含有类数组索引和length属性)

输出:返回一个新的数组实例。

注意事项:通用的,是一种复制方法(不改变原数组),返回的是原始参数的浅拷贝。面对[Symbol.isConcatSpreadable]属性设置为真的数组或类数组对象,参数的每个元素将会独立的添加到最终数组当中。普通值将会直接放入新数组当中。如果原数组是稀疏数组将会保留空槽。

2.2 手写实现-获取构造函数与concat实现

        因为concat需要返回新的数组实例,所以编写一个函数专门用于获取数组的构造函数,其基本逻辑为下:

  1. 首先检查[Symbol.species]属性返回的构造函数是否满足条件,满足则返回,否则进入下一步判断。
  2. 其次检查默认的构造函数,满足则返回,否则进入下一步判断。
  3. 最后都不满足就返回默认的构造器Array。
javascript">const getConstructor = (obj) => {const defaultConstructor = Arrayif(obj[Symbol.species] !== undefined){if(typeof obj[Symbol.species] === "function"){return obj[Symbol.species]}else{throw new TypeError("Symbol.species must be a constructor")}}else if(obj.constructor !== undefined){if(typeof obj.constructor === "function"){return obj.constructor}else{return defaultConstructor}}
}

         实现了获取构造函数的函数之后就可以编写concat函数了,如下所示:

javascript">MyArray.prototype.concat = function(...args){// 通过Symbol.species获取当前类的构造函数const con = getConstructor(this)const res = new con([])// 将原始数组元素加入新数组for(let i = 0; i < this.length; i ++){if(i in this){res.push(this[i])}else{// 如果原始数组有空槽,新数组长度加1res.length ++}}// 遍历数组元素for(let i = 0; i < args.length; i ++){// 如果是concat可展开(类)数组对象或者数组if((typeof args[i] === "object" && args[i][Symbol.isConcatSpreadable]) || Array.isArray(args[i])){// 遍历展开添加元素for(let j = 0; j < args[i].length; j ++){if(j in args[i]){res.push(args[i][j])}else{res.length ++}}}else{res.push(args[i])}}return res
}const notArray = {0: "hello",1: "world",length: 2,[Symbol.isConcatSpreadable]: true
}
const arr_4 = new MyArray(1,2,3)
const arr_5 = new MyArray(4,5,6)
const arr_6 = arr_4.concat(arr_5, 7, [8, ,[9]], notArray)
console.log(arr_6) // output: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 'hello', 'world'
console.log(arr_6 instanceof MyArray) // output: true

        注意以下几点:

  1.  注意返回实例类型与调用实例类型的一致性,通过getConstructor函数实现。
  2. 空槽的处理,通过直接增加结果数组的长度来添加空槽。
  3.  需要展开处理的数组或者类数组的判断条件,如果目标是对象且[Symbol.isConca tSpreadable]设置为true或者是数组的条件下都要进行展开操作。

3 Array.copyWithin()

3.1 基本介绍

        copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置,并返回它。语法如下所示:

javascript">copyWithin(target)
copyWithin(target, start?)
copyWithin(target, start?, end?)

输入:target序列开始替换的下标位置。 start是要复制元素的其实位置,默认为0。end为要复制元素的结束位置(不包含end)。

输出:修改后的数组。

注意事项:

  1. copyWithin方法是修改方法(修改原数组内容,不改变长度),保留空槽通用的
  2. target的有效性,首先将被转换为整数,负数索引需要加上length,如果还是小于0那么使用0。如果正数索引大于length不会拷贝任何内容。复制不会扩展数组,即使target在start后面的情况下。
  3. start的有效性,首先将被转换为整数,默认为0。索引处理与target一致。
  4. end的有效性,首先将被转换为整数,负数索引的处理与target一致。如果end大于length或者省略默认为length。end在start之前不会拷贝任何内容。

3.2 手写实现

        如下所示是首先copyWithin的代码示例:

javascript">// 3. 手写copyWithin
MyArray.prototype.copyWithin = function(target, start = 0, end = this.length){// 索引转换为数字target = Number(target)start = Number(start)end = Number(end)const handleIndex = (index, length) => {if(index < 0) return Math.max(length + index, 0)else return Math.min(index, length)}const length = this.length// 统一处理索引target = handleIndex(target, length)start = handleIndex(start, length)end = handleIndex(end, length)// 处理不需要修改的边界情况if(target >= length || start >= length || end < start) return this// 开始进行copyWithin, 首先创建副本const copy = this.slice(start, end)for(let i = 0; i < copy.length && (target + i < length); i ++){this[target + i] = copy[i]}return this
}// 测试样例
const arr_7 = [1, 2, , , 5]
console.log(arr_7) // output: [ 1, 2, <2 empty items>, 5 ]
console.log(arr_7.copyWithin(0, 4)) // output: [ 5, 2, <2 empty items>, 5 ]
console.log(arr_7.copyWithin(1, 4)) // output: [ 5, 5, <2 empty items>, 5 ]
console.log(arr_7.copyWithin(2, "4", 5))  // output: [ 5, 5, 5, <1 empty item>, 5 ]
// 类数组对象copyWithin
const notArray_1 = {0: "hello",1: "world",length: 2
}
console.log(Array.prototype.copyWithin.call(notArray_1, 0, 1)) // output: { '0': 'world', '1': 'world', length: 2 }
// 边界条件测试
console.log(arr_7.copyWithin(0, 5)) // output: [ 5, 5, 5, <1 empty item>, 5 ]

需要注意:

  1.  三种索引都使用统一的处理方法
  2. 边界情况在处理索引完成后一次进行
  3. 在进行copy过程中需要使用slice方法来支持稀疏数组
  4. 判断条件还要包含是否原数组不越界

4 Array.entries()

4.1 基本介绍

        entries() 方法返回一个新的数组迭代器对象,该对象包含数组中每个索引的键/值对。语法如下所示:

javascript">entries()

输入值:空

输出值:一个新的包含可迭代器对象,该对象返回value为数组的键值对。

注意事项:

  1. 稀疏数组的空槽处理为undefined
  2. 通用的方法,非数组对象满足条件即可调用

4.2 手写实现

4.2.1 手写实现返回迭代器对象

        entries方法要求返沪一个迭代器对象,迭代器对象要求实现迭代器协议[Symbol.iterator],该属性下有迭代器方法,返回一个对象,该对象有next方法,每次调用返回一个对象,包含value和done属性,首先不使用generator实现,如下所示;

javascript">MyArray.prototype.entries = function(){const arr = thislet index = 0return {[Symbol.iterator]: ()=>{return {next(){if(index < arr.length){const res =  {value:[index, arr[index]], done:false}index ++return res}else{return {done:true}}}}}}
}
  1. 使用闭包计数,index是外层函数的变量。
  2. 迭代器协议下的方法需要返回一个对象,对象拥有next方法,每次调用但会含有value和done属性的对象。 

4.2.2 使用generator实现迭代器对象

        使用generator函数能够更简洁的实现上述功能,如下所示:

javascript">// 4. 手写entries-使用generator
MyArray.prototype.entries = function(){const arr = thisreturn {[Symbol.iterator]: () =>{function* gen(){for(let i = 0; i < arr.length; i ++){yield [i, arr[i]]}}return gen()}}
}// 测试样例
const a = new MyArray("a", "b", "c");for (const [index, element] of a.entries()) {console.log(index, element);
}
// 0 'a'
// 1 'b'
// 2 'c'// 空槽处理
let arr_8 = new MyArray("a")
arr_8 = arr_8.concat([,"b"])
for (const element of arr_8.entries()) {console.log(element);
}
// [ 0, 'a' ]
// [ 1, undefined ]
// [ 2, 'b' ]// 类数组对象entries
const arrayLike = {length: 3,0: "a",1: "b",2: "c",};
for (const entry of MyArray.prototype.entries.call(arrayLike)) {console.log(entry);
}
// [ 0, 'a' ]
// [ 1, 'b' ]
// [ 2, 'c' ]

 注意:

  1. 使用generator时done属性由generator管理不需要我们去返回一个含done和value属性的对象

5 Array.every()

5.1 基本介绍

        every() 方法测试一个数组内的所有元素是否都能通过指定函数的测试。它返回一个布尔值。语法如下所示:

javascript">every(callbackFn)
every(callbackFn, thisArg)

输入:callFn(element, index, array)用于测试元素的执行的函数,应当返回一个真值或假值。thisArg指定回调函数中的this行为。

输出:如果每个元素对象通过测试返回真值则为true,只要有一个不通过则为false。

注意事项: 

  1. 遇到一个测试对象为假值则停止测试
  2. 对于空数字的测试将始终为真
  3. 对于稀疏数组,空槽不会调用callFn
  4. callFn的操作会改变数组元素,并影响后续测试
  5. 这是一个通用方法

5.2 手写实现

        手写实现代码如下所示:

javascript">// 手写实现every
// 难点处理稀疏数组
// thisArg处理
MyArray.prototype.every = function(callFn, thisArg = this){if(typeof callFn !== "function"){throw new TypeError(callFn + ' is not a function');}for(let i = 0; i < this.length; i ++){// 检查当前索引是否存在于数组中,跳过空槽if (!(i in this)) continue;if(!callFn.call(thisArg, this[i], i, this)) return false}return true
}function isBigEnough(element, index, array) {return element >= 10;
}const arr_9 = new MyArray(12, 5, 8, 130, 44)
const arr_10 = new MyArray(12, 54, 18, 130, 44)
console.log(arr_9.every(isBigEnough)) // false
console.log(arr_10.every(isBigEnough)) // trueconsole.log(MyArray.prototype.every.call([1, , 3], (x) => x !== undefined)) //true
console.log(MyArray.prototype.every.call([2, , 2], (x) => x === 2)) //trueconst arrayLike_1 = {length: 3,0: "a",1: "b",2: "c",};console.log(MyArray.prototype.every.call(arrayLike_1, (x) => typeof x === "string"),); // true

        难点总结如下:

  1. thisArg理解为改变callFn(不能为箭头函数,箭头函数没有自己的this)中的this行为,所以使用call,apply都可以
  2. 理解如何处理空槽,使用 in 操作符来判断,如果为false则直接跳过该轮回调函数调用

http://www.ppmy.cn/ops/122003.html

相关文章

深度剖析音频剪辑免费工具的特色与优势

是热爱生活的伙伴或者想要记录美好声音的普通用户&#xff0c;都可能会需要对音频进行剪辑处理。而幸运的是&#xff0c;现在有许多优秀的音频剪辑软件提供了免费版本&#xff0c;让我们能够轻松地施展音频剪辑的魔法。接下来&#xff0c;就让我们一同深入了解这些音频剪辑免费…

探索基于基于人工智能进行的漏洞评估的前景

根据2023年的一份报告 网络安全企业据估计&#xff0c;到 10.5 年&#xff0c;网络犯罪每年将给世界造成 2025 万亿美元的损失。每年记录在案的网络犯罪数量都会创下新高。这要求对传统的安全测试流程进行重大改变。这就是漏洞评估发挥作用的地方。 漏洞评估对于识别系统中的弱…

模拟实现消息队列(基于SpringBoot实现)

提要&#xff1a;此处的消息队列是仿照RabbitMQ实现&#xff08;参数之类的&#xff09;&#xff0c;实现一些基本的操作&#xff1a;创建/销毁交互机&#xff08;exchangeDeclare&#xff0c;exchangeDelete&#xff09;&#xff0c;队列&#xff08;queueDeclare&#xff0c;…

GPT对话知识库——bootloader是什么?ymodel协议是什么?

目录 1&#xff0c;问&#xff1a; 1&#xff0c;答&#xff1a; Bootloader 的主要功能 Bootloader 的工作流程 Bootloader 的应用场景 典型 Bootloader 实现的例子 1. STM32 的 Bootloader 2. U-Boot (Universal Bootloader) 总结 2&#xff0c;问&#xff1a; 2&…

基于Spring Boot+Unipp的中考体测训练小程序(协同过滤算法、图形化分析)【原创】

&#x1f388;系统亮点&#xff1a;协同过滤算法、图形化分析&#xff1b; 一.系统开发工具与环境搭建 1.系统设计开发工具 后端使用Java编程语言的Spring boot框架 项目架构&#xff1a;B/S架构 运行环境&#xff1a;win10/win11、jdk17 前端&#xff1a; 技术&#xff1a;框…

k8s 中微服务之 MetailLB 搭配 ingress-nginx 实现七层负载

目录 1 MetailLB 搭建 1.1 MetalLB 的作用和原理 1.2 MetalLB功能 1.3 部署 MetalLB 1.3.1 创建deployment控制器和创建一个服务 1.3.2 下载MealLB清单文件 1.3.3 使用 docker 对镜像进行拉取 1.3.4 将镜像上传至私人仓库 1.3.5 将官方仓库地址修改为本地私人地址 1.3.6 运行清…

解决Windows远程桌面 “为安全考虑,已锁定该用户账户,原因是登录尝试或密码更改尝试过多,请稍后片刻再重试,或与系统管理员或技术支持联系“问题

根本原因就是当前主机被通过远程桌面输入了过多的错误密码&#xff0c;被系统锁定。这种情况多数是你的服务器远程桌面被人试图攻击了&#xff0c;不建议取消系统锁定策略。如果阿里云或者腾讯云主机&#xff0c;只需要在管理后台通过管理终端或者VNC登陆一次&#xff0c;锁定即…

SpringBoot的配置文件

&#xff08;一&#xff09;Spingboot配置文件的概念 1.配置文件的作用 配置文件主要是为了解决硬编码带来的问题&#xff0c;把可能会发生改变的信息&#xff0c;放在一个集中的地方&#xff0c;当我们启动程序时&#xff0c;应用程序会从配置文件中读取数据&#xff0c;加载并…