• 我转过几个弯 绕过那个小雨楼
  • 拿着蒲扇摆着衣衫渡着紧箍咒
  • 不问天涯不停留 喝过几壶酒
  • 不过年少白头道义放胸口
  • 倘若明天之后 遥看前尘剑封侯
  • 似那天上神仙无所求
  • 朝朝暮暮君如梦醒十分不为何理由
  • 是真是假是惶恐是无休
  • 路过这风雨花满楼 片刻都不停留
  • 我本这书生进京赶考留下许多愁
  • 你问有没有时候 我叹这天道默悠悠
  • 能否与我一醉方休
  • 又过了几个弯 算尽天量道莫慌
  • 踏这田园闻这芳草香
  • 跌跌撞撞仗剑天涯折煞不枉无笔良
  • 是梦是幻是温柔是家乡
  • 路过这风雨花满楼 片刻都不停留
  • 我本这书生进京赶考留下许多愁
  • 你问有没有时候 我叹这天道默悠悠
  • 能否与我一醉方休
  • 路过这风雨花满楼 片刻都不停留
  • 我本这书生进京赶考留下许多愁
  • 你问有没有时候 我叹这天道默悠悠
  • 能否与我一醉方休
  • 谁能与我一醉方休

Array 数组扩展

287 0

扩展运算符

console.log(...[1,2,3]);

//1,2,3

扩展运算符会将后面跟着的数组转换为参数序列,这种效果一般用于函数参数,数组操作里面。

扩展运算符后面甚至可以接表达式,但是表达式一定要有数组抛出

const arr = [...(x>0?[1]:[]),2];

如果是一个空数组使用扩展运算符,则无任何效果

const a = [];
console.log([...a,1]);
//[1]

代替数组的apply方法

首先要知道apply除了改变this的指向外,第二个参数是一个数组,apply会将这个数组转为序列参数。

如:

function test(x,y,z){
  ...
};

const a = [1,2,3];
test.apply(null,a);

这样就可以将a数组转为参数传给函数了。

但是有了扩展运算符,就不用这样麻烦了。

function test(x,y,z){
  ...
};

const a = [1,2,3];
test(...a);

Math.max

//es5
Math.max.apply(null,[1,2,3]);  //3

//es6
Math.max(...[1,2,3]);  //3

数组push

在以前,如果一个a数组要push进来一个数组,这个push进来的数组要成为a数组的序列参数,那么只能这样

var a = [1,2,3];
var b = [4,5,6];

Array.prototype.push.apply(a,b);  //[1,2,3,4,5,6]

用扩展运算符就简单很多了。

var a = [1,2,3];
var b = [4,5,6];

a.push(...b);

扩展运算符的应用

合并数组

//es5
[1,2].concat(more);

//es6
[1,2,...more];

多个数组合并也是相同。

与解构结合

[a,...more] = [1,2,3];
console.log(a,more);
//1 
//[2,3]

数组的解构居然可以不用创建变量声明,不过还是建议给个const、let

[a,...more] = [];
console.log(a,more);
//undefined
//[]

可以看到,扩展运算符是创建一个数组,然后将对应的内容丢入,如果没有,他就是一个空数组。

如果我们将运算符放入解构中,则只能放入最后一位,因为他会将所有的内容都接收了,导致其他的接收不到。

const [...more,a] = [1,2,3];  //报错


const [a,...more,b] = [1,2,3];  //报错

函数返回值

函数只能返回一个值,如果有多个,则只能返回对象或者数组,如果是数组,我们就可以通过扩展运算符将其作为字符串参数了。

function test() {
  return [2020,2];
}

var time = new Date(...test());

字符串

扩展运算符可以将字符串转为数组

[..."hello"];
//["h","e","l","l","o"]

使用这种方式转换有一个重要的好处,就是他能正确识别unicode32位字符,一般情况下,一个unicode16位的会被识别为一个字符,32就是两位,但是32往往是一些特殊的字符,比如中文的一些不常用的,复杂的文字,一些表情字符。

所以,一个准确的获取字符串长度的方法可以这样写:

function stringLength(string) {
  return [...string].lenght;
}

为此我们还需要注意,但凡是操作32位的unicode字符所使用的字符串方法,都会将32位识别为两个字符,所以,我们要用扩展符进行改写,才能正确的操作。

let str = "x\Ud83D\uDE80y"; //一个32位字符

//错误的
str.split("").reverse().join("");
//"y\uDE80\uD83Dx"

//正确的
[...str]..reverse().join("");
//"x\Ud83D\uDE80y"

实现iterator对象

任何Iterator对象,都可以使用扩展运算符转为真正的数组对象。

let nodeList = document.querySelectorAll("div");
var array = [...nodeList];

nodeList 是一个类似数组的对象,这个对象有Iterator接口,所以扩展运算符就能将它转为真正的数组对象。

对于没有配置Iterator接口的对象,则无法进行转换

let arr = {
  "0":"1",
  "1":"2",
  "2":"3",
  length:3
}

let newArr = [...arr]; //报错
//TypeError: Cannot spread non-iterable object

arr是一个类似数组的对象,但是没有Iterator接口,我们无法通过扩展符转为数组,但是可以使用Array.from()进行转换。

Map和Set结构、Generator函数

map对象有Iterator接口,所以我们可以将map对象转换为数组

let map = new Map([
  [1,"a"],
  [2,"b"],
  [3,"c"],
])

let arr = [...map.keys()];  //["a","b","c"]

Generator函数

var go = function* () {
  yield 1;
  yield 2;
  yield 3;
};

[...go()]; //[1,2,3]

Array.from

Array.from用于将类似数组对象转为真正数组,类似数组对象(array-like object)、可遍历对象(iterable、Set结构和Map)

类似数组对象

什么是类似数组对象?

从本质特征来讲,就是含有length属性的对象。因此只要含有length属性,就可以通过Array.from进行转换,而这种没有Symbol.iterator接口的类似数组对象,扩展运算符就不能进行转换。

let arr = {
  "0":"1",
  "1":"2",
  "2":"3",
  length:3
}

//ES5写法
var arr1 = [].slice.call(arr);  //["1","2","3"]

//ES6写法
var arr2 = Array.from(arr); //["1","2","3"]

实际应用中,类似数组对象经常是dom操作返回的NodeList节点集合,以及函数内部arguments对象,这些都可以使用该方法转为真正的数组。

只有转为真正的数组,才能使用数组遍历的方法forEach这些。

let arr = {
  length:3
}

 Array.from(arr); //[undefined,undefined,undefined]

因为没有这个对象没有下标和下标对应的值,所以转换后得到的是三个undefined的数组

let arr = {
  "a":1,
  "b":2,
  "c":3
  length:3
}

 Array.from(arr); //[undefined,undefined,undefined]

这种也是一样,有值但是没有下标0123这些。

可遍历对象

只要转换的对象拥有Iterator接口,就可以进行转换,比如字符串和Set结构

Array.from("hello"); //["h","e","l","l","o"];

let nameSet = new Set(["a","b"]);
Array.from(nameSet); //["a","b"]

如果是一个真正的数组,Array.from会返回一个一模一样的新数组,这就是数组克隆。

let arr = [1,2];
Array.from(arr); // [1,2]

事实上扩展运算符也能做到这些,但是扩展运算符无法转换手动写的类似数组对象,因为扩展运算符实际上调用的是对象的遍历器接口Symbol.iterator,如果没有这个接口就无法转换,而Array.from就更强一些,他可以转换这些扩展运算符无法转换的对象。

兼容性

如果浏览器不支持,我们可以使用es5的方式

const toArray = (()=>{
  return Array.from ? Array.from : obj => [].slice.call(obj);
})();

Array.from的第二个参数

Array.from支持第二个参数,他是一个方法,效果等同于数组的map方法,他会将方法return出来的值传入数组保存,最后返回这个数组。

let arr = [1,2];
Array.from(arr,x=>x*x);

//等同于
arr.map(x=>x*x);

例子1:

let spans = document.querySelectorAll("span.name");

let names = Array.from(spans,s=>s.textContent);

拿到所有span的文本内容

例子2:

Array.from([1,"",2,"",3],n=>n||0);
//[1,0,2,0,3];

直接将空字符转换为0;

第三个参数 this关键字

如果在第二个参数,map函数中,使用到了this,我们可以使用第三个参数来指定this指向谁。

Array.from([1,"",2,"",3],n=>n||0,window);
//[1,0,2,0,3];

但这种情况比较少,目前也没啥例子。

其他

Array.from也可以识别32位的unicode字符,所以也可以拿来做一些操作,如:

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

一样可以用于获取文字的字符数。

Array.of

Array.of()方法用于将一组值转换为数组。因为Array()方法创建,他其实有一些行为差异,比如:

Array(2); //["",""]

Array(1,2);  //[1,2]

在只有一个参数的时候,表示的是创建对应length数的数组,只有存在两个值得时候,才会创建对应的数组,为了弥补这个不足,有了Array.of方法。

Array.of非常稳定,他永远会返回一个参数组成的数组,如果没有参数,则返回一个空数组。

Array.of(1);  //[1]

Array.of(1,2);  //[1,2]

Array.of(undefined);  //[undefined]

Array.copyWithin

Array.copyWithin是一个可以改变原数组的方法,他是对数组里面的内容进行copy覆盖的。

它支持三个参数:

  1. target,必填,从哪个位置开始,数组下标值
  2. start,从哪个位置开始进行复制,默认为0,如果是负值,就表示从后开始计算
  3. end,到该位置停止,默认等于数组的长度length,如果为负值,表示从后开始计算

截取的范围:从0开始计位,[1,2,3,4,5]数组可以理解为:[0位1,1位2,2位3,4位5,5位]

如图所示,一个箭头为一个位。

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

从0开始进行覆盖,从3位开始,也就是3后面起始,到4位结束,也就是5的左侧结束,中间选中的就是数字4,只有一个数字4,所以只有1被覆盖了,1成了4

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

负值情况

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

-2就是从4的左侧开始,-1就是5的左侧结束,那么中间只有4,从0开始替换,只替换了一个数字,1变成了4

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

-5就是1左侧,-1为5左侧,选中了1,2,3,4,然后0位开始替换,替换了4 位,最后结果值是没有变化的,和原来一样顺序。

find()和findIndex()

find()

find()是用于找到第一个符合条件的数组成员,他接收一个回调函数作为参数,回调函数接收三个参数,分别为:当前的值,当前值的index,原数组。

当第一个return出true的时候,停止数组的遍历,并返回return的值,如果没有符合条件的,则返回undefined

[1,2,3,4].find(i=>i>3);  //4
[1,2,3,4].find((item,index,arr)=>{
  return item>3;
});  //4

findIndex()

findIndex()和find差不多,都是找到第一个符合条件的数组成员,只是findIndex是返回数组成员的下标位置。

[1,2,3,4].findIndex(i=>i>3);  //3

如果没有则返回-1

[1,2,3,4].findIndex(i=>i>4);  //-1

findIndex的回调函数也有三个参数,分别为:当前的值,当前值的index,原数组。

find和findIndex的第二个参数

第一个参数是回调函数,第二个参数是绑定回调函数的this指向。

发现NaN

旧的数组无法通过indexOf来发现NaN,使用find和findIndex可以发现NaN对象

[NaN].indexOf(NaN);  //-1\

[NaN].findIndex(y=>{
  return Object.is(NaN,y);
}); //0

数组的fill()

fill()用于给一个固定值,然后填充一个数组。

[1,2,3,4].fill("a");  //["a","a","a","a"]

["","",""].fill(1);  //[1,1,1]

new Array(3).fill(2);  //[2,2,2]

fill常常用于空数组初始化,数组中已有的值会被全部抹去。

fill还可以接收第二个参数和第三个参数,用于指定填充的位置。

[1,2,3,4].fill("a",2,3);  //[1,2,"a",4]

位数可以参考copyWithin。

entries()、keys()、values()

es6新增了三个数组的遍历方法。

entries()

entries用于获取数组的键值对,也就是下标和值,他的结果类似下面这种:

["a","b","c"].entries();  //[[0,"a"],[1,"b"],[2,"c"]]

这个结果不是真正的显示结果,显示的是Array Iterator { },这个结果只能通过for--of的方法进行遍历处理

for(let arr of ["a","b","c"].entries()){
  console.log(arr);
};

//[0,"a"]
//[1,"b"]
//[2,"c"]

这样可以得到对应的值,因为是个数组我们可以进行解构

for(let [index,value] of ["a","b","c"].entries()){
  console.log(index,value);
};

//0,"a"
//1,"b"
//2,"c"

keys()

keys实际上获取的是数组的下标集合,但是他也不是直接返回一个数组,而是也要通过for--of方法遍历出来

for(let index of ["a","b","c"].keys()){
  console.log(index);
};

//0
//1
//2

values()

values获取是数组的值,当然返回的也不是一个数组,所以也要用for--of方法遍历出来

for(let value of ["a","b","c"].values()){
  console.log(value);
};

//"a"
//"b"
//"c"

手动遍历

如果我们不想使用for--of的方法,可以手动遍历

let values = ["a","b","c"].values();

console.log(values.next().value); //"a"
console.log(values.next().value); //"b"
console.log(values.next().value); //"c"

三种方法都可以使用这种手动遍历的方式。

includes()

includes用于判断数组里面是否包含给定的值,与字符串的includes方法相似。

[1,2,3].includes(2);  //true
[1,2,3].includes(4);  //false
[1,2,NaN].includes(NaN);  //true

includes用于代替indexOf方法,因为indexOf从语义上讲是找到参数第一个出现的位置下标,而不是判断是否存在,表达起来不够直观,而且indexOf内部使用的是===全等运算符,所以导致NaN无法正确判断是否存在。

includes则使用完全不一样的判断方法,就没有这个问题。

兼容性写法:

const contains = (()=>{
  return  Array.prototype.includes ? (arr,value)=> arr.includes(value) : (arr,value) => arr.some(el=>el===value)
})();

contains([1,2],2);  //true

数组的空位

数组的空位是指数组中某一个位置没有任何值。比如通过Array构造函数返回的数组都是空位。

Array(3);  //[, , ,]

空位不是undefined,undefined依然是有值的,空位是没有任何值。

0 in [undefined]  //true
0 in []  //false

第一个in表明数组第一个位置是有值得,第二个表明没有值。

在es5中,对于空值的处理很不一致,es6中,要么保留空值,要么统一为undefined。

es5

  1. forEach(),filter(),every(),some()都会跳过空位
  2. map()会跳过空位,但是输出的值依旧会保留空位
  3. join()和toString()会将空位视为undefined,而undefined和null会被处理为空字符串

es6

  1. Array.from()会将空位视为undefined
  2. 扩展运算符也是将空位视为undefined
  3. copyWithin()会保留空位,会连同空位一起复制
  4. fill()会将空位视为正常的数组位置
  5. for...of循环也会遍历空位
  6. entries(),keys(),values(),find(),findIndex()会将空位处理成undefined

因为空位的处理规则非常不统一,所以建议避免出现空位。

0
  • 本文分类:ES6
  • 本文标签:javascriptes6Array数组
  • 流行热度:已超过 287 人围观了本文
  • 最后更新:2020年12月31日 - 22时53分28秒
  • 发布日期:2020年12月31日 - 22时53分28秒
  • 版权申明:本文系作者@木灵鱼儿原创发布在木灵鱼儿 - 有梦就能远航站点。未经许可,禁止转载。

相关文章

微信收款码
微信收款码