在给对象设置属性时, 如果对象不存在很容易报错.

有些场景, 在对对象数组处理时, 设置对象属性前判断对象是否存在. 与其这样, 还不如直接初始化为空对象数组.

本文主要涉及到一些容易忽略的知识点:

  1. Array.prototype.fill() 的使用.
  2. 简单类型和复杂类型赋值/复制、传参的区别.
  3. 空单元数组的弊端.
  4. 箭头函数中的 returnthis.
  5. Function.prototype.apply() 的非常规使用.

9个考生就来了6个


考试时, 每个考生都有自己位置. 考生对照着可以很容易在考场里找到自己的座位.
秉着公平、公正、公开的原则, 考生被稀疏地散布在考场的各个角落.
复制代码

假设考场 3 ✖️3 排列, 考生的信息:

[{"row":1,"col":1,"name":"Ada"},{"row":3,"col":3,"name":"Aaron"},{"row":1,"col":2,"name":"Aditi"},{"row":3,"col":2,"name":"Aditi"},{"row":1,"col":3,"name":"Aditi"},{"row":3,"col":1,"name":"Abbott"}]
复制代码

将考场位置做成一个表格, 对考生位置按排统计, 来标注考生出勤情况.

[{"row":1,"col_1":"Ada","col_2":"Aditi","col_3":"Aditi"},{},{"row":3,"col_3":"Aaron","col_2":"Aditi","col_1":"Abbott"}]
复制代码

(为嘛没有第二排? 自知考不过, 缺考了呗?) 开发中, 对原始数据进行处理是一件很平常的事. so, 这个数据的处理应该很简单...吧?

Array(3).fill({}) 试一波


如何初始化空对象数组?

原始数据是以学生个体的信息存储展示的, 现在则按排为单位对数据进行处理. 理所当然的会想到先初始化三个空对象数组.

let studentRow = Array(3).fill({})
// > studentRow
// [ {}, {}, {} ]
复制代码

动作很快姿势很帅. 不过, 这样真的可以么? 长得倒是像那么一回事, 可实际上完全行不通. Array.prototype.fill() 的用法是, 指定某个值来填充数组.

也就是说, {} 在 studentRow 里复制了三次. 如果是简单类型值倒也罢了, 但是换做复杂类型值, 修改每一个 {} , 都会影响其它的 {}. 因为它们都是对同一个对象的引用.

let studentRow = Array(3).fill({});
studentRow[0].name = 'tony';
// > studentRow
// [ { name: 'tony' },
//   { name: 'tony' },
//   { name: 'tony' } ]// 等同于
let obj = {};
let studentRow = Array(3).fill(obj);
// > studentRow
// {obj, obj, obj}
studentRow[0].name = 'tony';
// > studentRow
// [ { name: 'tony' },
//   { name: 'tony' },
//   { name: 'tony' } ]
复制代码

知识点:

  • 将一个值赋予变量时, 解析器必须确定这个值是基本类型值还是复杂类型值.
  • 当是复杂类型值时, 变量里保存的是该复杂类型值在堆中的一个指针. 复制的是变量的指针, 操作的却是实际的对象.

Array(3)map(() => {}) 结合有问题


Array(3).fill({}) 行不通. 那么, Array(3).map(() => {})?

如果说 Array(3).fill({}) 不可行, 是因为三个空对象是对同一个对象的引用. 那么我们就设法返回三个不同的空对象.

let studentRow = Array(3).map(() => {});
// > studentRow
// [ <3 empty items> ]
复制代码

结果很失望, 这个表达式就干了两件事, Array(3)map(() => {}). 所以问题很好排查.

let arr = Array(3);
// > arr
// [ <3 empty items> ]
复制代码

对于数组中并不存在的单元, map() 也是束手无策.

我说: 肚里要有货?

肚里没货, 我们就造一些. Array.prototype.fill() 又有出头之日了.

let studentRow = Array(3).fill(undefined);
// > studentRow
// [ undefined, undefined, undefined ]
复制代码

警告:

  • 如若一个数组没有任何单元, 但它的 length 属性中却显示有单元数量, 这样奇特的数据结构会导致一些怪异的行为. 我们将包含至少一个 “空单元” 的数组称之为 “稀疏数组”. undefined 单元非 “空单元”.

  • 永远不要创建和使用空单元数组.

箭头函数中的 return


你以为 Array(3).fill(undefined).map(() => {}) 就完事了? 图样图森破 ?

let studentRow = Array(3).fill(undefined).map(() => {});
// > studentRow
// [ undefined, undefined, undefined ]
复制代码

哦, 我知道了, 你没有 return 啊

额, 这和 return 没有关系. 不信你可以加一个试试? 其实, {} 在这里被视作语法块了, 没有任何意义. 可恨就可恨在, 它和空对象长得一摸一样. 既然这样, 那我们就不用字面量定义一个空对象了.

let studentRow = Array(3).fill(undefined).map(() => Object.create(null));
studentRow[0].name = 'tony';
// > studentRow
// [ { name: 'tony' }, {}, {} ]
复制代码

这样就达到初始化对象数组的目的了. 可是, Array(3).fill(undefined).map(() => {}) 为什么行不通, 如何补救?

规避问题在某种意义上不等于解决问题.

{...} 里面的代码会被解析为一系列语句. {} 也因此不能达到我们预期的结果. 所以, 我们可以用 (...){} 包装成表达式, 即 ({}).

let studentRow = Array(3).fill(undefined).map(() => ({}));
studentRow[0].name = 'tony';
// > studentRow
// [ { name: 'tony' }, {}, {} ]
复制代码

知识点:

  • 若函数体的表达式个数多于一个, 或者函数题包含非表达式语句的时候才需要用 {...} 包裹.

  • 如果只有一个表达式, 并且省略了 {...} 的话, 则附加一个隐式 return. 若在块体内需要指定返回值, 则需要明确的 return.

  • 箭头函数提供了简练的语法, 但不是普通函数的替代品. 箭头函数的主要设计目的是改变 this 的行为. 普通函数内的 this 是动态绑定, this 指向谁取决于调用者. 而箭头函数里的 this 是基于作用域的, 是可预测的.(可参考与作用域相关的闭包、内存泄漏、this).

令人绝望的Array.prototype.fill()


你以为结束了, 其实才刚刚开始

这是真正的开始, 没看错, 是的, 我们之前所做的可能都是无用功.

是的, IE 是魔鬼. 费尽了周折, 才发现一切都是徒劳. 难道就这么放弃了?

'放弃'能吃么? 能吃就吃了它, 啥? 不能吃?!? 提它作甚!!!

Array.prototype.fill() 方便之处就是能够简便填充数组. 此法不行, 另寻他法.

Function.prototype.apply() 了解一下


Function.prototype.apply() 入参有两个. 第一个参数是 函数方法 的调用者, 第二个参数是 函数方法 的入参(要区分入参和入参的不同). 函数方法 的入参可以是数组也可以是类数组. 我们的目的就是填充数组, 所以我们要在类数组上做文章. 就拿 console.log 做例子?. (直接复制我之前的博客内容?).

function log_1(arg) {
console.log(arg)
}
log_1(1);
log_1(1,2,3);
// 1
// 1// 改造下
function log_2() {const log = console.log;log.apply(null, arguments)
}
log_2(1);
log_2(1, 2, 3)
// 1
// 1 2 3
复制代码

这是 Function.prototype.apply() 使用的方法. 如果我们把 log_2 里的 arguments 换成 {length: 3}

function log_2() {const log = console.log;log.apply(null, {length: 3})
}
log_2()
// undefined undefined undefined
复制代码

{length: 3}[undefined, undefined, undefined] 在传入 apply(null;...) 后, 在参数的处理上, 最后的结果是一样的. 那么, Array(3).fill(undefined).map(() => ({})) 可改造成

let studentRow = Array.apply(null, {length: 3}).map(() => ({}));
studentRow[0].name = 'tony';
// > studentRow
// [ { name: 'tony' }, {}, {} ]
复制代码

在这里 Array 作为普通函数调用, 以上等同于

let studentRow = Array(undefined, undefined, undefined);
// > studentRow
// [ undefined, undefined, undefined ]
复制代码

收尾


只是初始化一个空对象数组, 结果整出这么多幺蛾子. 处理数据其实就那么几行代码. 大致长这模样

function handleData(params) {const studentRow = Array.apply(null, {length: 3}).map(() => ({}));params.forEach(item => {studentRow[item.row-1][`row`] = item.row;studentRow[item.row-1][`col_${item.col}`] = item.name;})return studentRow;
}
复制代码

创建包含N个空对象的数组相关推荐

  1. js 对象及空对象或数组及空数组的判断与比较

    工作中经常会使用到,这里记录一下 判断是不是对象: let obj = {}; Object.prototype.toString.call(obj) == "[object Object] ...

  2. NumPy — 创建全零、全1、空、arange 数组,array 对象类型,astype 转换数据类型,数组和标量以及数组之间的运算,NumPy 数组共享内存

    NumPy 简介 一个用 python 实现的科学计算包.包括: 1.一个强大的 N 维数组对象 Array : 2.比较成熟的(广播)函数库: 3.用于整合 C/C++ 和 Fortran 代码的工 ...

  3. 如何检查数组是否包含JavaScript中的对象?

    In this article, we will look at various methods to check if an array includes an object in JavaScri ...

  4. JavaScript判断对象是否为空对象或空数组

    1. 判断一个变量是对象还是数组 首先判断一个变量是对象还是数组,不能使用typeof来判断,因为不管是对象还是数组,使用typeof得到的都是"object". 可以使用Obje ...

  5. JavaScript数组对象教程–如何使用JS数组方法创建,更新和遍历对象

    On average I work with JSON data 18 times a week. And I still need to google for specific ways to ma ...

  6. 常用创建空对象创建对象方法var obj = {};其他几种方法你知道吗

    (1)var obj = {}; //创建空对象 (2)var obj = Object.create(null); obj.toString //undefined var obj = Object ...

  7. js中判断空数组和空对象的方法

    复习的时候做一些学习的笔记总结. javascript的值的类型: 原始值:number.string.boolean.undefined.null.symbol(ES6).bigint(ES10) ...

  8. “空对象 {}” 与 “空数组 []” 的相加问题(详解)

    本文旨在让读者能更加清晰地了解 "空对象 {}" 与 "空数组 ()" 相加(eg:"[] + []" ."[] + {}&quo ...

  9. js 判断是否为空对象,或者空数组

    以下例子考虑这个变量为undefine和null的情况. 空对象: if(obj && Object.keys(obj).length>0){ console.log('空对象' ...

最新文章

  1. SQL批量修改字段值
  2. 【面试】2018大厂高级前端面试题汇总
  3. 【正一专栏】今晚国足能上演奇迹吗?
  4. React Native实现一个带筛选功能的搜房列表(2)
  5. 如何看待NLP领域的内卷:我不配找工作?
  6. 抓包工具mitmproxy环境配置使用(一)
  7. sersync+rsync+inotify-tools集中日志,实时同步
  8. mysql5.1 utf8_编译安装mysql 5.1使用utf8解决乱码问题
  9. dataframe数组做元素_数组 array 矩阵 list 数据框 dataframe
  10. linux生成随机密码的十种方法
  11. 什么是波导的简并波,矩形波导和圆波导中的简并有何异同
  12. 多数据点拟合曲线,最小二乘法,矩阵
  13. configure: line syntax error near unexpected token `win32-dll'
  14. 【Ansys Workbench】—响应面优化操作步骤
  15. 可汗学院统计学课程总结
  16. ceph RBD块存储常规操作
  17. JAVA学习导图、思维导图
  18. 访问html 403 iis,IIS 403 错误详细原因 及解决办法总结
  19. 华为服务器SNMP协议怎么修改,华为S5700交换机开启SNMP协议
  20. 写一个函数,实现 n 的阶乘

热门文章

  1. 级差公排php如何_专业级差陷阱重重?
  2. 【论文阅读 - AAAI 2020】Order Matters:Semantic-Aware Neural Networks for Binary Code Similarity Detection
  3. java点餐小程序_Java点餐小程序之黑心商人
  4. JAVA多线程实现的三种方式
  5. Java多线程之线程同步机制(锁,线程池等等)
  6. VBA中如何判断数组为空
  7. 显存优化 | Pytorch的显存机制torch.cuda.empty_cache及周边概念
  8. android 黑科技软件,安卓手机必备的5个黑科技APP 每个都强大到没有朋友 要低调使用...
  9. sql多表联查(内连接、外连接)、实验八表联查
  10. 汇编 nasm 打字板