前端如何优雅处理类数组对象?

简介: 前端如何优雅处理类数组对象?


网络异常,图片无法展示
|

一、背景介绍

Leo 部门最近来了位前端实习生 Robin,作为师傅,Leo 认真的为 Robin 介绍了公司业务、部门工作等情况,还有前端的新人学习地图。 接下来 Robin 开始一周愉快的学习啦~ 一周后,Leo 为 Robin 同学布置了考试作业,开发一个人员搜索选择的页面,效果大致如下:

网络异常,图片无法展示
|

Robin 看完这个效果图后,一脸得意的样子,这确实不难呀~

过几天后,Robin 带着自己写的代码,给 Leo 展示了她的代码,并疑惑的问到:

网络异常,图片无法展示
|

她将这个“数组”输出到控制台:

网络异常,图片无法展示
|

Leo 看了看代码:

getUserList(){
   const memberList = $('#MemberList li');
   memberList.map(item => { console.log(item) });
   console.log(memberList);
}

Leo 又问到:

网络异常,图片无法展示
|

Robin 一脸疑惑,然后 Leo 再原来代码上,加了个 Array.from 方法如下:

getUserList(){
    const memberList = Array.from($('#MemberList li'));
    memberList.map(item => {
        console.log(item)
    })
    console.log(memberList)
}

然后重新执行代码,输出下面结果:

网络异常,图片无法展示
|

Leo 输出的结果,跟 Robin 说到:

网络异常,图片无法展示
|

Robin 满脸期待望着师傅,对类数组对象更加充满期待。

二、类数组对象介绍

2.1 概念介绍

所谓 类数组对象,即格式与数组结构类似,拥有 length 属性,可以通过索引来访问或设置里面的元素,但是不能使用数组的方法,就可以归类为类数组对象

举个例子🌰:

const arrLike = {
  0: 'name',
  1: 'age',
  2: 'job',
  length: 3
}

2.2 常见类数组对象

  • arguments 对象;
function f() {
  return arguments;
}
f(1,2,3)
// Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
  • NodeList(比如 document.getElementsByClassName('a') 得到的结果;
document.getElementsByTagName('img')
// HTMLCollection(3) [img, img, img]
  • typedArray(比如 Int32Array);

typedArray 即 类型化数组对象 是一种类似数组的对象,它提供了一种用于访问原始二进制数据的机制。JavaScript引擎会做一些内部优化,以便对数组的操作可以很快。然而,随着Web应用程序变得越来越强大,尤其一些新增加的功能例如:音频视频编辑,访问WebSockets的原始数据等,很明显有些时候如果使用JavaScript代码可以快速方便地通过类型化数组来操作原始的二进制数据将会非常有帮助。 —— 《MDN 类型化数组》

const typedArray = new Uint8Array([1, 2, 3, 4])
// Uint8Array(4) [1, 2, 3, 4]

另外使用 jQuery 获取元素,会被 jQuery 做特殊处理成为 init 类型:

$('img')
// init(3) [img, img, img, prevObject: init(1), context: document, selector: "img"]

当然还有一些不常见的类数组对象,比如“Storage API 返回的结果”,这里就不一一列出。

三、类数组对象属性

下面通过 Robin 代码作为示例,介绍类数组对象的属性:

const memberList = $('#MemberList li');

3.1 读写

// 读取
memberList[0];
// Node: <li>...</li>
// 写入
memberList[0] = document.createElement("div")
memberList[0];
//  Node: <div>...</div>

3.2 长度

memberList.length; 
// 10

3.3 遍历

for (let i = 0;i < memberList.length; i++){
    console.log(memberList[i]);
}
/*
  Node: <li>...</li>
  Node: <li>...</li>
  ... 共10个,省略其他
*/
memberList.map(item => console.log(item));
/*
  0
  ... 共10个,省略其他
*/

但如果是 HTMLCollection 就不能使用 map 咯:

const img = document.getElementsByTagName("img");
img.map(item => console.log(item));
// Uncaught TypeError: img.map is not a function

四、类数组对象处理

Leo 看了看 Robin 处理这个列表的代码:

getUserList(){
    const memberList = $('#MemberList li');
    const result = {
        text: [],
        dom : [],
    };
    memberList.map(item => {
        item = memberList[item]
        // 判断当前节点是否有 checked 类名
    })
    console.log(result)
    this.showToast(`选中成员:${result.text}`);
}

很明显,Robin 并没有对 jQuery 获取到的 memberList 做处理,直接使用,通过索引来获取对应值。 Leo 继续和 Robin 介绍到:

网络异常,图片无法展示
|

4.1 Array.from

使用 Array.from 来将类数组对象转为数组对象,操作起来非常简单:

getUserList(){
    const memberList = Array.from($('#MemberList li'));
    // 省略其他代码
}

语法如下:

Array.from(arrayLike[, mapFn[, thisArg]])

参数

  1. arrayLike 想要转换成数组的伪数组对象或可迭代对象。
  2. mapFn 可选如果指定了该参数,新数组中的每个元素会执行该回调函数。
  3. thisArg 可选可选参数,执行回调函数 mapFnthis 对象。

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

更多Array.from介绍可以查看文档。

4.2 Array.prototype.slice.call()

slice() 方法返回一个新的数组对象,这一对象是一个由 beginend 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变

实现代码:

getUserList(){
    const memberList = Array.prototype.slice.call($('#MemberList li'));
    // 省略其他代码
}

更多 Array.prototype.slice 介绍可以查看文档。

4.3 ES6展开运算符

展开语法(Spread syntax), 可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;还可以在构造字面量对象时, 将对象表达式按key-value的方式展开。

实现代码:

getUserList(){
    const memberList = [...document.getElementsByTagName("li")];
    // 省略其他代码
}

更多 ES6展开运算符 介绍可以查看文档。

4.4 利用concat+apply

getUserList(){
    const memberList = Array.prototype.concat.apply([], $('#MemberList li'));
    // 省略其他代码
}

五、案例小结

Leo 介绍完这些知识后,Robin 又优化了下自己的代码,涉及到类数组对象操作的核心 js 代码如下:

class SelectMember {
    constructor(){
        this.MockUsers = window.MockUsers;
        this.init();
    }
    init(){
        this.initMemberList('#MemberList', this.MockUsers);
        this.initBindEvent();
    }
    // ... 省略部分代码,保留核心代码
    submitSelect(){
        const memberList = Array.from($('#MemberList li'));
        const result = {
            text: [],
            dom : [],
        };
        memberList.map(item => {
            const hasClass = $(item).children('.round-checkbox').children('span').hasClass(this.selectClassName);
            if(hasClass){
                result.text.push($(item).children('.user-data').children('h4').text());
                result.dom.push(item);
            }
        })
        this.showToast(`选中成员:${result.text}`);
    }
}
let newMember = new SelectMember();

很明显,使用正确方式来处理类数组对象,不仅能使我们代码更加少,减少转换处理,还能提高代码质量。

整个项目的完整代码,可以在我的 github 查看

github.com/pingan8787/…

六、总结

本文我们通过一个实际场景,详细介绍了类数组对象在实际开发中的使用,对于常见的类数组对象,我们还介绍了处理方式,能很大程度减少我们处理类数组对象的操作,将类数组统一转成数组,更加方便对数据的操作。 希望看完本文的你,以后再遇到类数组对象,不会再一脸懵逼咯~~~


目录
相关文章
|
3月前
|
前端开发 开发者
new操作符背后的秘密:揭开Web前端对象创建的神秘面纱!
【8月更文挑战第23天】在Web前端开发中,`new`操作符是创建对象实例的核心。本文以`Person`构造函数为例,通过四个步骤解析`new`操作符的工作原理:创建空对象、设置新对象原型、执行构造函数并调整`this`指向、判断并返回最终对象。了解这些有助于开发者更好地理解对象实例化过程,从而编写出更规范、易维护的代码。
39 0
|
3月前
|
前端开发 JavaScript 开发者
【前端开发者的福音】彻底改变你编码习惯的神奇数组迭代技巧——从基础到进阶,解锁 JavaScript 数组迭代的N种姿势!
【8月更文挑战第23天】在Web前端开发中,数组是JavaScript中最常用的数据结构之一,掌握高效的数组迭代方法至关重要。本文详细介绍了多种数组迭代技巧:从基础的`for`循环到ES6的`for...of`循环,再到高阶方法如`forEach`、`map`、`filter`、`reduce`及`some`/`every`等。这些方法不仅能提高代码的可读性和维护性,还能有效优化程序性能。通过具体的示例代码,帮助开发者更好地理解和运用这些迭代技术。
39 0
|
23天前
|
JavaScript 前端开发 算法
前端优化之超大数组更新:深入分析Vue/React/Svelte的更新渲染策略
本文对比了 Vue、React 和 Svelte 在数组渲染方面的实现方式和优缺点,探讨了它们与直接操作 DOM 的差异及 Web Components 的实现方式。Vue 通过响应式系统自动管理数据变化,React 利用虚拟 DOM 和 `diffing` 算法优化更新,Svelte 通过编译时优化提升性能。文章还介绍了数组更新的优化策略,如使用 `key`、分片渲染、虚拟滚动等,帮助开发者在处理大型数组时提升性能。总结指出,选择合适的框架应根据项目复杂度和性能需求来决定。
|
24天前
|
JSON 前端开发 数据格式
前端的全栈之路Meteor篇(五):自定义对象序列化的EJSON介绍 - 跨设备的对象传输
EJSON是Meteor框架中扩展了标准JSON的库,支持更多数据类型如`Date`、`Binary`等。它提供了序列化和反序列化功能,使客户端和服务器之间的复杂数据传输更加便捷高效。EJSON还支持自定义对象的定义和传输,通过`EJSON.addType`注册自定义类型,确保数据在两端无缝传递。
|
30天前
|
人工智能 前端开发 JavaScript
拿下奇怪的前端报错(一):报错信息是一个看不懂的数字数组Buffer(475) [Uint8Array],让AI大模型帮忙解析
本文介绍了前端开发中遇到的奇怪报错问题,特别是当错误信息不明确时的处理方法。作者分享了自己通过还原代码、试错等方式解决问题的经验,并以一个Vue3+TypeScript项目的构建失败为例,详细解析了如何从错误信息中定位问题,最终通过解读错误信息中的ASCII码找到了具体的错误文件。文章强调了基础知识的重要性,并鼓励读者遇到类似问题时不要慌张,耐心分析。
|
2月前
|
前端开发 JavaScript
前端基础(十四)_Math对象
本文介绍了JavaScript中`Math`对象的常用方法,包括: 1. `Math.floor()`:向下取整,去掉小数部分。 2. `Math.ceil()`:向上取整,向上进一。 3. `Math.round()`:四舍五入,针对小数点后面第一位数字。 4. `Math.max()`:获取数字序列中的最大值。 5. `Math.min()`:获取数字序列中的最小值。 6. `Math.pow()`:计算某个数字的次方数。 7. `Math.sqrt()`:开根号。 8. `Math.random()`:生成一个0到1之间的随机数。
21 1
前端基础(十四)_Math对象
|
1月前
|
JSON 缓存 前端开发
SpringBoot的 ResponseEntity类讲解(具体讲解返回给前端的一些事情)
本文讲解了SpringBoot中的`ResponseEntity`类,展示了如何使用它来自定义HTTP响应,包括状态码、响应头和响应体,以及如何将图片从MinIO读取并返回给前端。
61 3
|
1月前
|
JSON 前端开发 数据格式
@RequestMapping运用举例(有源码) 前后端如何传递参数?后端如何接收前端传过来的参数,传递单个参数,多个参数,对象,数组/集合(有源码)
文章详细讲解了在SpringMVC中如何使用`@RequestMapping`进行路由映射,并介绍了前后端参数传递的多种方式,包括传递单个参数、多个参数、对象、数组、集合以及JSON数据,并且涵盖了参数重命名和从URL中获取参数的方法。
73 0
@RequestMapping运用举例(有源码) 前后端如何传递参数?后端如何接收前端传过来的参数,传递单个参数,多个参数,对象,数组/集合(有源码)
|
2月前
|
前端开发 JavaScript
前端基础(十五)_时间对象、字符串对象
本文介绍了JavaScript中时间对象的操作方法,包括获取和设置年、月、日、小时、分钟、秒等,以及如何格式化时间显示,同时提及了字符串对象的常用方法。
31 0
前端基础(十五)_时间对象、字符串对象
|
2月前
|
前端开发 JavaScript
前端基础(十六)_数组对象
本文详细介绍了JavaScript中数组对象的创建和操作方法,包括数组的增删改查、排序、去重、迭代等常用操作。
18 0