Array.apply(null,{length: 99}) 逻辑解析

前言

本文的主要目的源于Vue中一个很有意思的案例:就是通过 lodash-es 打乱下图中文字的顺序,并让其变化过程中呈现动画效果。
里面涉及到了 vue3 的 move 动画,shuffle 打乱数组数据的函数、页面的 grid 布局,而本文的主要目的是讲解快速生成所需数组的方式,里面涉及到一些 ES6 的数组知识,如果不够了解,最好去看一下。这里简单列举一下相关的知识:

  • apply/call 函数
    每个函数都包含两个非继承而来的方法:call()方法和apply()方法
    call和apply可以用来重新定义函数的执行环境,也就是this的指向;call和apply都是为了改变某个函数运行时的context,即上下文而存在的,换句话说,就是为了改变函数体内部this的指向。

    • apply()方法接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
    • call()方法不一定接受两个参数,第一个参数也是函数运行的作用域(this),但是传递给函数的参数必须列举出来。
  • Array.from 函数

    • 将类数组对象转换成数组

      function sumNum() {
          return Array.from(arguments).reduce((sum, num) => sum + num);
      }
      
      sumNum(1, 2, 3); 
      
    • 克隆数组(浅拷贝)

      const numbers = [3, 6, 9];
      const numbersCopy = Array.from(numbers);
      numbers === numbersCopy; // => false 浅拷贝
      
    • 填充数组

      const length = 3;
      const init   = 0;
      const result = Array.from({ length }, () => init);
      result; // => [0, 0, 0]
      
    • 数组去重

      function unique(array) {
        return Array.from(new Set(array));
      }
      unique([1, 1, 2, 3, 3]); // => [1, 2, 3]
      
  • map 函数,用于处理数组

  • fill 函数,用于填充数组
    在这里插入图片描述
    数字化管理平台
    Vue3+Vite+VueRouter+Pinia+Axios+ElementPlus
    权限系统-商城
    个人博客地址

一、基础引入

vue 教程中有一段 demo code,如下:

render: function (createElement) {
  return createElement('div',
    Array.apply(null, { length: 20 }).map(function () {
      return createElement('p', 'hi')
    })
  )
}

这个表达式Array.apply(null, { length: 20 })有点让人费解。第一感觉这个表达式就是为了创建一个长度为20的数组,但表达式Array(20)也可以实现这个功能啊,为啥非要写那么复杂呢?

于是乎就想,如果是这样子,那么我把这一段代码换成 Array(20) ,变成下面这样:

render: function (createElement) {
  return createElement('ul',
    Array(20).map(function () {
      return createElement('li', 'list item')
    })
  )
}

演化成 JS 代码如下:

# apply 模式
const list = Array.apply(null, {
    length: 99
})
console.log("list:",list)

# Array 参数模式
const list2 = Array(99)
console.log("list2:",list2)

控制台输入:
在这里插入图片描述
按照刚刚的理解,把代码换成这个样子应该是没有问题的。然而打印结果告诉我们刚刚那样子地理解应该是不对的, Array.apply(null, { length: 99 })和Array(99) 这两句代码还是有区别的,那么区别是什么?

二、解析

2.1 apply 函数方式

ES5开始 apply 函数的第二个参数除了可以是数组外,还可以是类数组对象(即包含length属性,且length属性值是个数字的对象)。对象{length: 2}就是一个类数组对象,因为没有初始化下标0,1的值,所以获取0,1下标的值得到的都是undefined。

console.log(list[0]); // undefined
console.log(list[1]); // undefined

// 类数组可以转成真正的数组
var arr = Array.prototype.slice.apply({length: 2});
console.log(Array.isArray(arr)) // true

这样表达式 Array.apply(null, { length: 2}) 就等价于如下代码:

// { length: 2 } 作为 Array.apply 第二个参数等同于 [undefined, undefined] 作为 Array.apply 第二个参数
Array.apply(null, [undefined, undefined]); 

// 等价于 apply 方法的执行结果
Array(undefined, undefined); 

// Array 属于安全的构造函数,上面直接调用和 new 方式调用等价
new Array(undefined, undefined); 

这样就很容易知道该表达式的值是一个长度为2,且每个元素值都被初赋值为 undefined 的数组(注意此时不是数组元素没有初始化,而是初始化成undefined,这就是跟 Array(2) 的区别)

2.2 为啥非要写那么复杂呢?

map函数并不会遍历数组中没有初始化或者被 delete 的元素(有相同限制还有 forEach, reduce 方法)。所以写这么“复杂”就是为了实现:创建一个长度为 99,且每个元素都被初始化的数组。这样 map 方法就可以循环 99 次了。

// 被初始化的数组
let apply = Array.apply(null, { length: 99 }).map(function (item, index) {
   return {
     index: index, // 循环99次
   };
});
 
// 未被初始化的数组
let data = Array(99).map(function (item, index) {
   return {
     index: index, // 不会被执行
   };
});

该表达式还可以修改成:

Array.apply(null, Array(99)); // 第二个参数用 Array(99) 代替 {length: 99}

使用 ES6 API 创建初始化数组:

# 方式一
Array.from({length: 99})

# 方式二
Array(99).fill(null)

三、 构造函数 + 一个数字参数

直接调用Array函数跟new方式调用是等价的,即

let arr = Array(99)

# 等价于
let arr = new Array(99);

表示创建一个长度为 99 的数组,注意该数组的元素并没有被初始化([empty ×99]),即

console.log(0 in a); // false
console.log(1 in a); // false, 因为数组下标0,1还未初始化
console.log(a[0]); // undefined, 因为数组下标0还未初始化,访问不存在的属性返回undefined

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MagnumHou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值