08.数组的扩展

    科技2025-01-11  5

    1.扩展运算符

    含义

    扩展运算符是三个点“…”,如图rest参数的逆运算,能将数组转换为用逗号分隔的参数序列;该运算符主要用于函数调用: // 用于函数调用 function fn1(x,y) { console.log(x + y); } const numbers = [1,2]; fn1(...numbers);// 3 可以和正常参数结合使用: // 可以和正常的函数参数结合使用 function fn2(v,w,x,y,z){ console.log(v,w,x,y,z); } const array = [1,2,3,4,5]; fn2(...array); // 1 2 3 4 5 扩展运算符后面可以放置表达式: // 扩展运算符后面也可以放置表达式 let x = 1; const arr = [...(x > 0 ? ['a'] : ['b'])]; console.log(arr); // ['a']; 如果扩展运算符后面是一个空数组,此时不产生任何效果;

    可以替代数组的apply方法

    由于扩展运算符可以展开数组,所以不再需要apply方法将数组转化为函数的参数: // ES5写法 function f1(x,y,z) {}; const aa = [1,2,3]; f1.apply(null,aa); // 需要通过apply方法调用函数 // ES6写法 function f2(x,y,z) {}; const bb = [4,5,6]; f2(...bb);

    扩展运算符的应用

    1.合并数组

    // 1.合并数组 const arr1 = [1,2,3]; const arr2 = [4,5,6]; const arr3 = [7,8,9]; // ES5写法 let res1 = arr1.concat(arr2,arr3); // [1, 2, 3, 4, 5, 6, 7, 8, 9] // ES6 let res2 = [...arr1,...arr2,...arr3];

    2.复制数组:数组属于复杂数据类型,直接复制的话只是复制了指向底层数据结构的指针,而不是复制产生全新的数组;

    const arr4 = [1,2]; const arr5 = arr4; arr5[0] = 2; console.log(arr4); // [2,2] 直接复制不产生新数组 // 扩展运算符实现复制 // 方法1 const arr6 = [...arr4]; arr6[0] = 2; console.log(arr4); // [1,2] arr6 = [1,2] // 方法2 const [...arr7] = arr4; console.log(arr7); // [1,2]

    3.与解构赋值结合:可以和解构赋值结合,用于生成数组;

    将扩展运算符用于解构赋值时,只能将其放在参数的最后一位,否则会报错;

    const [first,...rest] = [1,2,3,4,5,6]; console.log(first); // 1 console.log(rest); // [2,3,4,5,6];

    4.字符串

    扩展运算符还可以将字符串转化为真正的数组:

    const str1 = [...'hello']; console.log(str1); // ["h", "e", "l", "l", "o"] 使用扩展字符转换字符串还有一个好处就是能够正确识别32位的Unicode字符: 'x\uD83D\uDE80y'.length // 4 [...'x\uD83D\uDE80y'].length // 3 凡是涉及32位unicode字符的操作都会存在这样的问题,最好使用扩展运算符改写:

    5.将实现了Iterator接口的对象转换为数组

    可以将实现了Iterator接口的伪数组转换为真数组:

    <div></div> <div></div> <div></div> <script> let nodeList = document.querySelectorAll("div"); let nodeArr = [...nodeList]; console.log(nodeArr); </script>

    对于没有实现Iterator接口的伪数组,无法使用扩展运算符将其转换为真数组,但是可以使用Array.from()方法转换:

    // 自定义一个伪数组 const arrayLike = { 0 : "peanut", 1 : 33, length : 2 } let r = [...arrayLike]; // object is not iterable // 此时可以使用Array.from方法进行转换 let rr = Array.from(arrayLike); console.log(rr); // ["peanut", 33]

    6.小结

    扩展运算符内部调用的是数据结构的Iterator接口,因此只要是具有Iterator接口的对象,都可以使用扩展运算符,比如Map、Set结构,Generator函数…对于没有Iterator接口的对象,使用扩展运算符会报错;

    2.Array.from()

    作用:用于将两类对象转换为真正的数组:伪数组、可遍历的对象(包括Map和Set结构);语法: /* obj:要转换的对象 callback:对每一个元素进行处理的回调函数,类似于map方法的回调函数,将处理后的值放入返回的数组中 */ Array.from(obj[,callback]); 只要是部署了Iterator接口的对象,该方法都能将其转换为数组;扩展运算符将对象转换为数组的前提是对象需要由Iterator接口;Array.from()将对象转换为数组的前提是对象具有length属性且属性名为数值(仅仅具有length属性而不包含其他属性的对象除外); // Array.from方法可以将具有length属性的对象转换为数组 const arrLike = { 0 : "peanut", 1 : "study", length : 2, } // 传入第二个参数,返回函数处理后的新数组 let res1 = Array.from(arrLike,(value,index) => { return (index + " " + value); }) console.log(res1); // ["0 peanut", "1 study"] 如果map函数中使用到this关键字,这时可以传入第三个参数用于绑定this指向;

    应用

    Array.from({length : n},()=>{}):只要有一个原始的数据结构,就可以对它的值进行处理,转成规范的数组结构,就可以使用数组的所有方法;这里n是回调函数执行的次数,回调函数的返回值会变成数组中的元素; let res3 = Array.from({length : 3},()=> 'peanut'); console.log(res3); // ["peanut", "peanut", "peanut"] 将字符串转换为数组,然后返回字符串长度;

    因为该方法能识别Unicode字符,可以避免大于0xFFFF的字符被识别为2个字符的bug;

    function countStrLength(string){ return Array.from(string).length; }

    3.Array.of()

    作用:将一组数值转换为数组;Array对象由于参数原因可能会出现如下的情况: // Array.of()可以弥补Array对象以下的几个缺陷 // Array对象创建数组: const arr1 = new Array(); // [] const arr2 = new Array(3); // [空 × 3] 一个参数时其实也只能说是指定了数组的长度 const arr3 = new Array(1,2); // [1, 2]

    使用Array.of()能解决该问题:

    // 使用Array.of不会出现这样的问题 const arr4 = Array.of(); // [] const arr5 = Array.of(3); // [3] const arr6 = Array.of(1,2); // [1,2] Array.of()基本上可以代替Array和new Array,它的行为非常统一,不穿传入多少数值参数都会转换为数组返回;Array.of()方法的代码模拟实现: function ArrayOf(){ return [].splice.call(arguments); // slice:返回从指定位置到指定结尾截取的数组,不指定参数时返回原数组 }

    4.Array.prototype.copyWithin():内部元素复制并替换方法

    作用:在当前数组内部将指定位置的成员复制到数组的其他位置(覆盖原有元素),并返回当前的数组;语法: /* target(必选):从该位置开始替换元素 start:从该位置开始读取要复制的元素,默认0,负数表示倒着数 end:到该位置停止读取元素,默认等于数组长度,负数表示倒数 */ Array.prototype.copyWithin(target[,start[,end]]); // Array.prototype.copyWithin(target[,start[,end]]); const arr1 = [1,2,3,4,5,6] // 将第4位开始的元素复制到从0开始的位置 let res1 = arr1.copyWithin(0,4); // [5, 6, 3, 4, 5, 6] // 参数也可以是负数 let res2 = arr1.copyWithin(0,-1,-2); // [5, 6, 3, 4, 5, 6]

    5.Array.prototype.find()和Array.prototype.findIndex():查找元素

    Array.prototype.find()方法

    作用:找出第一个符合条件(符合回调函数中的条件)的数组成员,回调函数返回true时返回传入回调函数中的那个元素;找到则返回该元素,未找到返回undefined;语法: /* callback:在数组每一项上执行的函数,接受三个参数 value:当前的值 index:当前的位置 arr:原数组 thisArg:指定执行回调函数时的this对象 */ array.find(callback[,thisArg]);

    Array.prototype.findIndex()方法

    作用:返回满足条件的第一个元素的位置;找到则返回该元素的位置,未找到返回-1;语法: /* callback:在数组每一项上执行的函数,接受三个参数 value:当前的值 index:当前的位置 arr:原数组 thisArg:指定执行回调函数时的this对象 */ array.findIndex(callback[,thisArg]); 代码示例: // 1.find方法,找出第一个符合条件的数组元素 const arr1 = [1,0,2,5,-5,-6]; // 找出小于0的元素 let res1 = arr1.find(value => { return value < 0; }) // 2.findIndex() 与find类似,所有元素都不符合条件时返回-1 const arr2 = [1,2,3]; // 找到等于2的元素 let res2 = arr2.findIndex(value => value === 2); // 1 返回符合条件的元素的位置 // 找出数组中的第一个负数 let res3 = arr2.findIndex(value => value < 0); // -1 没找到返回-1

    与indexOf()比较:

    find():返回的是元素本身,而不是元素位置,未找到返回undefined,而不是-1;findIndex():与indexOf()类似,但是findIndex()能识别数组的NaN元素;

    相比于indexOf()的优点

    这两个方法能识别数组中的NaN元素,indexOf()无法做到这一点:

    在查找NaN元素时,需要借助 Object.is(arg1,arg2)方法(该方法用于判断两个参数是否相等);

    // 相比于indexOf的优点:能识别NaN元素 const arr3 = [NaN]; // 数组的indexOf方法 let res4 = arr3.indexOf(NaN); // -1 未找到 但是数组中存在NaN元素 // find和findIndex方法都能找到NaN元素 let res5 = arr3.find(value => Object.is(NaN,value)); // NaN let res6 = arr3.findIndex(value => Object.is(NaN,value)); // 0

    Array.prototype.fill()

    作用:用指定的值填充一个数组;数组被填充位置的原先的元素都会被抹去;返回的是修改后的数组;语法: /* value:用于填充用的值 start:填充开始的位置,默认值为0 end:填充结束的位置(不包括该位置的元素),默认值为数组长度 */ arr.fill(value[,start[,end]]); fill()方法对于初始化空数组十分方便;代码示例: // 只有一个参数 不指定填充位置时 数组中所有元素都被替换为指定的元素 const arr1 = [1,2,3]; let res1 = arr1.fill(7); // [7, 7, 7] // 指定填充的位置 let res2 = arr1.fill(6,1,2); // [7, 6, 7]

    7.遍历数组的新方法:entries()、keys()、values()

    作用:都用于遍历数组,返回一个遍历器对象,可用for…of遍历;唯一的不同:entries()==>遍历键值对、keys()==>遍历键、values()==>遍历值,对应保存的内容分别为键值对、键、值;代码示例: // 1.entries方法:遍历键值对 const arr1 = ["peamut","coder"]; for(let [key,value] of arr1.entri console.log(key,value); }; // 0 "peamut" 1 "coder" // 2.keys()方法 遍历键 for(let key of arr1.keys()){ console.log(key); } // 0 1 // 3.values()遍历值 for(let value of arr1.values()){ console.log(value); } // peamut coder 如果不使用循环,也可以手动调用遍历器提供的next方法手动遍历元素; // 使用遍历器提供的next方法遍历元素 let person = arr1.entries(); // 将返回的遍历器赋值给person let res1 = person.next().value; // [0, "peamut"]

    8.Array.prototype.includes()

    作用:检测某个给定的值是否包含在数组中,返回布尔值,与字符串的includes()方法类似;语法: /* value:要查找的值 formIndex:查找开始的位置,可以是负值,如果负数表示的长度大于数组长度,则从0开始查找 */ arr.prototype.includes(value[,fromIndex]); 代码示例: // includes方法 检测给定值是否包含于数组中 返回布尔值 const arr1 = [1,2,NaN,4]; let res1 = arr1.includes(NaN); // true // 可以指定开始查找的位置 let res2 = arr1.includes(NaN,3); // false 从第三位开始 所以找不到啦啦啦

    在该方法之前,使用indexOf()检查数组是否包含某个值,但是indexOf()方法有两个缺点:

    不够语义化,indexOf的返回值需要判断是否等于-1才能确定其是否包含;

    indexOf()方法内部使用"==="进行判断,对NaN判断结果不准确;

    Map和Set数据结构还有一个has()方法,需要与includes()方法区分:

    Map的has方法用于检查是否包含指定的键;Set的has方法用于检查是否包含指定的值;

    数组的空位

    数组的空位是指数组的某一个位置没有任何值;空位是没有任何值的,undefined也算有值; let arr1 = new Array(3); // [,,] 3个空位 let arr2 = [undefined,undefined,undefined]; // undefined不算空位

    ES5数组方法对空位的处理方式:

    foreach()、filter()、every()和some()都会跳过空位;map()会跳过空位,但会保留这个值;jion()和toString()会将空位视为undefined,将undefined和null视为空字符串;

    ES6:明确将空位转为undefined;

    Array.from():将数组的空位转为undefined;扩展运算符:转为undefined;copyWithin():连同空位一起复制;fill():将空位视为正常的数组位置;for…of:会遍历空位;
    Processed: 0.012, SQL: 8