ECMAScript 6(11)——数组的扩展

0、一句话总结

  1. 可以将一个有Iterator接口的数据类型(arraylike)转为真正的数组;
  2. 将变量作为数组成员,返回一个新数组;
  3. 把数组内某些位置元素,复制在数组内的某些位置;
  4. 用某个值填充满整个数组;
  5. 遍历数组的key、value和key + value;
  6. 以函数为筛选条件,找到数组中第一个符合条件的元素;
  7. 告诉你数组中有没有某个元素;

1、Array扩展方法

以下方法只能通过Array这个全局对象来访问,而不能通过例如以下几个方法来调用

Array.prototype.from;    //undefined
[].from;    //undefined
var a = [1];
a.from;    //undefined
var a = new Array();
a.from;    //undefined
var a = [];
a.__proto__.from;    //undefined

正确方法是:

typeof Array.from;    //"function"
1.1、基于已有对象创建数组

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

效果是从一个类似数组或可迭代对象创建一个数组的实例;

参数1:被用于转换为数组的对象;不能
参数2:可选,处理函数,将数组的每个元素通过本函数处理后再返回。如果没有return内容,数组该位置元素的值将是undefined(而不是跳过这个元素);
参数3:参数2函数里this指向的目标,默认是window对象;

arrayLike

简单来说(不涉及极端和特殊情况),几种常见类型会如下处理:

  • 1. undefined和null会抛出TypeError;
  • 2. number会返回一个空数组;
  • 3. boolean类型返回空数组;
  • 4. 字符串会以单个字符为单位分拆为数组,例如”ab”变为[“a”, “b”];
  • 5. 数组还是数组(是一个新的数组,但是浅复制,而不是深度复制,即按引用传递的元素保持不变);
  • 6. 类似数组的对象,会被转为数组,比如Array.from({'0':"1",length:2})输出["1", undefined],注意,必须有length属性才成立。其他情况会返回空数组;
  • 7. 函数返回空数组(无论函数有没有返回值);
  • 8. NodeList转为数组,如代码:
  • var list = document.querySelectorAll(".a");
    console.log(Object.prototype.toString.call(list));  //[object NodeList]
    console.log(Object.prototype.toString.call(Array.from(list)));  //[object Array]

  • 9. 其他有Iterator接口的类型。
  • 在ES6中,有三类数据结构原生具备Iterator接口:数组、某些类似数组的对象、SetMap结构。

    所谓类似数组的对象,本质特征只有一点,即必须有length属性。
    因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。
    但转换后,填充的时候就需要看key了,key符合才会被填充到对应位置;如果该位置key为空,那么值就是undefined。

    更多内容等写了Iterator相关的内容后再回来补吧(如果我能想起来……)

    mapFn

  • 可选
  • 处理函数,有点类似数组的map,有两个参数,分别是item(数组当前元素)和index(从0开始),没有第三个参数;
  • 返回值将作为当前索引的值填充到数组中;
  • 没有return的话当前位置为undefined;
  • thisArg

  • 可选
  • mapFn里,this指向的目标
  • 如果你有一个可以转换为数组的数据结构,比如NodeList,又想使用数组的各种api,那么就可以通过这个方法将该数据结构先转为数组,就可以方便的调用数组的各种API了。

    1.2、生成数组

    Array.of(element0[, element1[, …[, elementN]]])

    说明:

    生成一个新数组,然后将参数依次添加到该数组中,返回该数组。

    如代码:

    Array.of(1);    //[1]
    Array.of("a", "c", "b");    //["a", "c", "b"]
    Array.of();    //[]

    注意:

    假如某个参数是一个按引用传递类型,那么将按引用传递给新数组(而不是创建一个全新的按引用传递对象)。如:

    var a = {};
    var b = Array.of(a)
    b;  //[{}]
    b[0] === a;   //true

    与new Array的区别:

    new Array();    //[]
    new Array(1);    //[undefined]
    new Array(2);    //[undefined, undefined]
    new Array(1, 2);    //[1, 2]
    new Array("1");    //["1"]

    也就是说,new Array假如参数为1个,并且参数类型为number,比如该参数为n时,他的效果是生成一个length为n,然后每个位置值都为undefined的数组。

    而Array.of()无论参数是什么,是几个,都会将参数依次添加到一个新数组中并返回该数组;

    2、数组实例的扩展方法

    与1中不同,这里的方法不能直接通过Array.copyWithin来访问,而是需要通过prototype来访问。例如:

    Array.copyWithin;    //undefined
    typeof Array.prototype.copyWithin;    //"function"
    2.1、数组元素在数组内复制

    arr.copyWithin(target)
    arr.copyWithin(target, start)
    arr.copyWithin(target, start, end)

    简单来说,这个api是这么做事的:

    为了方便理解,以[1, 2, 3, 4, 5].copyWithin(a, b, c)为例

    1. 准备复制调用这个方法的数组,即[1,2,3,4,5]
    2. 找到参数2,该参数决定了从数组哪个索引位置开始复制(包含该位置),默认值为0。比如b=2, c默认时,复制的数组内容就是[3,4,5]
    3. 找到参数3,该参数决定了从数组哪个索引位置结束复制(但不包含该索引位置)。默认值为数组的长度。上面数组中,c默认为5。假如b=1,c=2,那么复制数组内容就是[1]
    4. 参数2和参数3如果是负数,则从末尾开始数,例如-1则表示数组最后一个元素的索引,注意参数2包含,参数3不包含该位置
    5. 通过参数2和参数3,从数组中找到了要被复制的部分。然后现在要决定将数组复制在哪里。
    6. 查看参数1,确定将数组从哪个索引位置开始替换,默认值为0。替换对应位置的数组元素。
    7. 不会超出数组原有长度
    8. 返回被修改后的数组


    流程解释:

    a=1,b,c默认值。于是b=0,c=5,复制内容为[1,2,3,4,5],从下标1开始替换。于是数组变为[1,1,2,3,4]。注意,不会超出

    [1, 2, 3, 4, 5].copyWithin(1);    //[1, 1, 2, 3, 4]


    a=1,b=3,c默认。于是c=5,复制内容为]4,5]。从下标1开始替换,于是数组变为[1,4,5,4,5]

    [1, 2, 3, 4, 5].copyWithin(1,3);    //[1, 4, 5, 4, 5]


    a=1,b=2,c=-2。于是c=3,复制内容为[3]。从下标1开始替换,于是数组变为[1,3,3,4,5]

    [1, 2, 3, 4, 5].copyWithin(1,2,-2);    //[1, 3, 3, 4, 5]
    2.2、数组的填充

    arr.fill(value)
    arr.fill(value, start)
    arr.fill(value, start, end)

    效果是将数组用数值填充。

    参数1表示用于填充的值;
    参数2表示填充的起始索引(含);
    参数3表示填充的末尾索引(不含);
    如果数组原位置有值,会覆盖原位置的值。

    new Array(3).fill(3);    //[3,3,3],填充
    [1,2].fill(3);    //[3,3],会覆写
    [0, 0, 0].fill(1, 1);    //[0,1,1],可以设置起始填充位置
    [0, 0, 0].fill(1, 0, 1);    //[1,0,0],也可以设置终止填充位置

    如果按引用传递类型的话,填充的时候是同一个值。

    var a = {};
    var arr = new Array(3).fill(a);
    arr;    //[{}, {}, {}]
    arr[0] === arr[1];    //true
    2.3、数组的遍历

    arr.keys()
    arr.values()
    arr.entries()

    用于遍历数组,分别可以取index,value和index + value。

    注意:chrome、IE、火狐(至少53.0版本)不支持arr.values(),而edge支持

    用法是通过for…of方法来使用,比较类似for…in方法。

    如示例代码:

    var arr = ["a", "b", "c"];
    for (let key of arr.keys()) {
        console.log(key);
    }
    //0
    //1
    //2
    //注意兼容性问题
    var arr = ["a", "b", "c"];
    for (let val of arr.values()) {
        console.log(val);
    }
    //"a"
    //"b"
    //"c"
    var arr = ["a", "b", "c"];
    
    //let kv又可以写成let [key, val]这种形式
    for (let kv of arr.entries()) {
        console.log(kv);
    }
    //[0, "a"]
    //[1, "b"]
    //[2, "c"]

    另外,以上方法返回的是一个Iterator(遍历器/迭代器)对象,可以通过该对象来遍历整个数组。具体来说请看以下示例代码:

    var arr = ["a", "b", "c"];
    var obj = arr.entries();    //obj通过next方法来遍历,此时obj指向该数组的起始位置(数组的第一个元素之前)
    var first = obj.next();
    //first:{done:false, value:[0,"a]}
    //此时first是obj遍历到第一个对象后的结果,obj对象已经迭代到第一个元素
    
    var second = obj.next();
    //second:{done:false, value:[1,"b"]}
    var third = obj.next();
    //third:{done:false, value:[2,"c"]}
    
    var end = obj.next();
    //end:{done:true, value:undefined}
    //此时遍历结束,因此done为true,并且value为undefined

    另外,这个遍历器对象,只有next方法,而next方法会更新他自身状态。

    也只有通过next方法返回的值(而非遍历器对象),才能获知当前是否遍历结束,以及遍历到当前目标的值是什么。

    2.4、找到元素和元素的索引

    arr.find(callback[, thisArg])

    通过回调函数,查找到符合条件的第一个元素,并返回。

    参数1是一个函数,这个函数会遍历整个数组。它有三个参数,分别是:当前元素,当前元素的索引,整个数组。有些类似arr.forEach,当返回值为true的时候,筛选成功,终止其后继续执行的代码。

    参数2是回调函数中this指向的对象,参考其他此类api理解即可。

    示例代码。

    var a = {
        num: 1
    }
    var b = {
        num: 2
    }
    var c = {
        num: 3
    }
    var arr = [a, b, c];
    var result = arr.find(function (item, index, array) {
        console.log(item.num, index);   //会依次输出1 0和2 1
        if (item.num > 1) {
            return true;
        } else {
            return false;
        }
    })
    console.log(result);    //{num: 2}
    console.log(b === result);  //true,说明不是创建一个全新的元素并返回

    arr.findIndex(callback[, thisArg])

    和arr.find几乎一样,唯一区别是返回的是索引,而非数组元素。

    将上面代码修改为如下后,返回值为1

    .....
    var result = arr.find(function (item, index, array) {
        ...
    })
    console.log(result);    //1

    类似:

    ...
    [a, b, c].indexOf(b);    //1

    但相对来说,find和findIndex,扩展性更强(因为可以用回调函数),并且支持对NaN的查找(因为逻辑可以自己写)。

    2.5、查看数组是否包含

    arr.includes(searchElement)
    arr.includes(searchElement, fromIndex)

    2.4的方法,是通过作为参数的函数的返回值,来返回你需要的元素、或元素的索引,比较类似arr.indexOf()。

    而这个更加类似indexOf(),这个方法直接传你要查找的元素,他只告诉你有、或没有。

    indexOf()当有的时候返回非负整数,没有的时候返回-1。

    使用这个方法需要注意以下几点:

    1. 可以判断NaN是否包含(indexOf()不行);
    2. 永远记住按引用传递类型,查看的是引用的对象是否一致。例如:[{}].includes({}); //false
    3. 虽然兼容性对于现代浏览器还可以,但小心某些低版本的现代浏览器不支持;
    2.6、数组的空位

    具体去查看阮一峰的博客吧。

    总而言之,良好的代码习惯是不要写类似

    var arr = [,,];
    //或
    var arr = new Array(10);

    这样的声明代码。而是写

    var arr = [undefined, undefined, undefined];
    //或
    var arr = new Array(10);
    arr.fill(undefined);

    可以避免很多莫名其妙出现的bug。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值