我转过几个弯 绕过那个小雨楼
拿着蒲扇摆着衣衫渡着紧箍咒
不问天涯不停留 喝过几壶酒
不过年少白头道义放胸口
倘若明天之后 遥看前尘剑封侯
似那天上神仙无所求
朝朝暮暮君如梦醒十分不为何理由
是真是假是惶恐是无休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
又过了几个弯 算尽天量道莫慌
踏这田园闻这芳草香
跌跌撞撞仗剑天涯折煞不枉无笔良
是梦是幻是温柔是家乡
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
路过这风雨花满楼 片刻都不停留
我本这书生进京赶考留下许多愁
你问有没有时候 我叹这天道默悠悠
能否与我一醉方休
谁能与我一醉方休
Array 数组扩展
扩展运算符
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覆盖的。
它支持三个参数:
- target,必填,从哪个位置开始,数组下标值
- start,从哪个位置开始进行复制,默认为0,如果是负值,就表示从后开始计算
- 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
- forEach(),filter(),every(),some()都会跳过空位
- map()会跳过空位,但是输出的值依旧会保留空位
- join()和toString()会将空位视为undefined,而undefined和null会被处理为空字符串
es6
- Array.from()会将空位视为undefined
- 扩展运算符也是将空位视为undefined
- copyWithin()会保留空位,会连同空位一起复制
- fill()会将空位视为正常的数组位置
- for...of循环也会遍历空位
- entries(),keys(),values(),find(),findIndex()会将空位处理成undefined
因为空位的处理规则非常不统一,所以建议避免出现空位。
评论(0)