Array.from() 你了解多少

3,110 阅读5分钟
  • 1、 基础说明及语法定义

    • 1-1、基础说明
        <!-- 示意:MDN原文 -->
      
        The Array.from() static method creates a new, 
        shallow-copied Array instance from an array-like or 
        iterable object.
      
      • 这句话,阐述两个含义:
        • 1、返回一个数组实例
        • 2、入参为:一个类数组或者一个可迭代对象
    • 1-2、 语法定义
        <!-- 示意:MDN原文 -->
        Array.from(arrayLike [, mapFn [, thisArg]])
      
      • 参数说明:
        • arrayLike 一个类数组或者一个可迭代对象 【必填】
        • mapFn 回调函数,对返回的数组中的每个元素在数组返回前,进行操作处理(Map function to call on every element of the array.) 【非必填】
        • thisArg 回调函数中的this指向
  • 2、 重点解析

    • 2-1、何为类数组?常见的那些?
      • 1、objects with a length property and indexed elements【有length属性;元素可索引】
      • 2、函数的arguments、Element NodeList等
    • 2-2、何为可迭代对象?常见的那些?
      • 1、对数组的泛化,几乎所有对象可以作为在for…of 循环中的对象【简单来说,数组就是特殊的对象,只不过key默认为index,不长显罢了】
      • 2、Map and Set等
    • 2-3、Array.from()的回调函数怎么理解?
      • 1、Array.from() has an optional parameter mapFn, which allows you to execute a map() function on each element of the array being created.
        • 意思就是:Array.from()的回调mapFn,功能类似map()函数,遍历逐个元素;于是乎:Array.from(obj, mapFn, thisArg) === Array.from(obj).map(mapFn, thisArg) 二者的区别是:Array.from does not create an intermediate array, and mapFn only receives two arguments (element, index)【Array.from不会创建中间数组,from(obj).map会创建中间数组,一直占着内存不会销毁】
      • 2、Map and Set等
  • 3、应用场景

    • 3-1、转换字符串为数组
        Array.from('foo');
        // [ "f", "o", "o" ]
      
      • 为什么这样可以,可以认为【字符串是类数组】< 字符串与数组一样,都有 length 属性以及 indexOf(…) 和 concat(…) 方法等>【关于此结论可作为额外文章进行拓展】
    • 3-2、将Set对象转换为数组
        const set = new Set(['foo', 'bar', 'baz', 'foo']);
        Array.from(set);
        // [ "foo", "bar", "baz" ]
      
        <!-- 当然你也可以用ES6的简单写法 -->
        [ ... new Set(['foo', 'bar', 'baz', 'foo'])]
      
    • 3-3、将Map对象转换为数组
        const map = new Map([[1, 2], [2, 4], [4, 8]]);
        Array.from(map);
        // [[1, 2], [2, 4], [4, 8]]
      
        const mapper = new Map([['1', 'a'], ['2', 'b']]);
        Array.from(mapper.values());
        // ['a', 'b'];
      
        Array.from(mapper.keys());
        // ['1', '2'];
      
    • 3-4、将Element NodeList转换为数组
        // Create an array based on a property of DOM Elements
        const images = document.getElementsByTagName('img');
        const sources = Array.from(images, image => image.src);
        const insecureSources = sources.filter(link => link.startsWith('http://'));
      
        //这里的getElementsByTagName得到的images就是个类数组,同样我们可以很自然的想到通过querySelectorAll()得到的集合也是类数组
      
    • 3-5、将函数arguments转换为数组
        function f() {
          return Array.from(arguments);
        }
        f(1, 2, 3);
      
        // [ 1, 2, 3 ]
      
    • 3-6、填充数组【这里不讨论Array.fill()】
      • 3-6-1、形式一:

          <!-- 用索引填充 -->
          Array.from({length: 5}, (v, i) => i);
        
          <!-- 用指定的值填充 -->
          Array.from({length: 5}, (v, i) => 6666);
        
        • {length: 5}得到的是一个可迭代的对象,Array.from处理后就是个数组【长度为5,单个元素每个都为undefined】,然后在经过Array.from的回调mapFn处理,此时v为undefined,i为索引
      • 3-6-2、形式二:【拓展】

        <!-- 用指定的值填充 -->
        Array(10).fill(666);
        
        <!-- map填充会失效 -->
        Array(3).map((item,index)=>index);
        // 这个不会得到[0,1,2],而会得到[empty,empty,empty]
      
      • 这里要要特别注意 Array(10)返回的是一个长度10且每个元素为undefined的数组,而不是[10] 【关于Array函数具体如何使用可自行拓展】

      • 3-6-3、将只有一个元素的组织快速复制n倍:【思考】

        var a = [3]
        var b = Array.from({length: 10}, (v, i) => a[0]);
      
    • 3-7、克隆数组
      • 代码实现:
          const arr = [1, 2, 3, 4, 5]
          const arrCopy = Array.from(arr)
        
        • 注意这个是浅拷贝,Why?【Ex如下:】
          <!-- 一维度操作 -->
          var a = [1, 2, 3, [4, 5, 6]]
          var b = a
          var c = Array.from(a)
          a.push(7)
          // a: [1, 2, 3, [4, 5, 6], 7]
          // b: [1, 2, 3, [4, 5, 6], 7]
          // c: [1, 2, 3, [4, 5, 6]] //这个没有7
        
          <!-- 二维度操作 -->
          var a = [1, 2, 3, [4, 5, 6]]
          var b = a
          var c = Array.from(a)
          a[3].push(7)
          // a: [1, 2, 3, [4, 5, 6, 7]]
          // b: [1, 2, 3, [4, 5, 6, 7]]
          // c: [1, 2, 3, [4, 5, 6, 7]]
        
    • 3-8、生产范围数组
      • 3-8-1 生产范围数字:

          // Sequence generator function (commonly referred to as "range", e.g. Clojure, PHP etc)
          const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
        
          // Generate numbers range 0..4
          range(0, 4, 1);
          // [0, 1, 2, 3, 4]
        
          // Generate numbers range 1..10 with step of 2
          range(1, 10, 2);
          // [1, 3, 5, 7, 9]
        
      • 3-8-2 生产范围字母:

          // Generate the alphabet using Array.from making use of it being ordered as a sequence
          range('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x));
          
          // ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
        
    • 3-9、结合第三个参数操作回调函数
      • 第三个参数可以指定第二个回调函数的this值。那么,Array.from既有类似 map的功能,也有类似 bind的功能,可以给定this
      • 代码实现:
          const arr = [1, 2, 3, 4, 5, 6];
        
          const newArr = Array.from(arr, function (item, i) {
            return item + this.value
          }, {
            value: 3
          })
        
          // [4, 5, 6, 7, 8, 9]
        
  • 4、思考:写了这些应用场景,有几点还是可以思考的:

    • 你能定这么多的应用场景,并不是这个功能有多复杂,而是这个api创建的数组实例来源于【类数组和可迭代两个方向】,这两个方向太广了,可罗列的也比较多
    • 在知识梳理中我们我遇到了几个比较有意思的点,可作为额外知识点,继续梳理拓展终结:
      • 字符串是类数组
      • Array()入参一个参数为什么得到的和预想的不一样,入参多个和预想的却又一样
      • Array(n).map()时,map在遍历empty时为什么不会生效
      • 深拷贝和浅拷贝如何区分【是不是可以简单的理解,深拷贝通过递归就能实现】
  • 5、 参考文献