1. 扩展运算符
扩展运算符(spread)是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
<div>1</div>
<div>2</div>
<div>3</div>
<script>
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
console.log(...document.querySelectorAll('div'));
// [<div>, <div>, <div>]
该运算符将一个数组,变为参数序列。
1.1 扩展运算符后可以为表达式
let num =0
let arr1 =[10,20,30];
let arr2 =['a','b','c'];
let arr =[...(num ?arr1:arr2)] //三目运算符后解构,解构后的值赋值给新数组
console.log(arr);
let x =2;
const arr = [...(x > 0 ? ['a'] : []), 'b',];
console.log(arr);
如果扩展运算符后面是一个空数组,则不产生任何效果。
console.log([...[], 1]);
1.2 传参时替代了apply方法
function f(x, y, z) {
console.log(x,y,z);
}
var args = [0, 1, 2];
f.apply(null, args); // ES5 的写法
function f(x, y, z) {
console.log(x,y,z);
}
let args = [0, 1, 2];
f(...args); // ES6的写法
1.3 可以快速取出数组中的最大值
let arr =[10,20,30,80,40,56];
console.log(Math.max.apply(null,arr));//之前es5找数组中的最大值
console.log(Math.max(...arr));//es6数组扩展运算符
//上面两种方法等同于
console.log(Math.max(10,20,30,80,40,56));
1.4 复制数组
const a1 = [1, 2];
const a2 = a1.concat(); //es5写法
console.log(a2);
a2[0] = 2;
console.log(a2);
扩展运算符提供了复制数组的简便写法。
const a1 = [1, 2];
const a2 = [...a1];// 写法一
const [...a2] = a1;// 写法二
1.5 合并数组
扩展运算符提供了数组合并的新写法。
[1, 2].concat(more) // ES5
[1, 2, ...more] // ES6
var arr1 = ['a', 'b'];
var arr2 = ['c'];
var arr3 = ['d', 'e'];
arr1.concat(arr2, arr3); // ES5的合并数组
[...arr1, ...arr2, ...arr3,77,88] // ES6的合并数组
1.6. 扩展运算符与解构组合使用
扩展运算符可以与解构赋值结合起来,用于生成数组。
let list=[11,22,33,44,66,55]
// let a = list[0], rest = list.slice(1) // ES5
let [a, ...rest] = list // ES6
console.log(a,rest);
//首字母大写
let str1='this';
let [a,...ss] =str1;
console.log(a ,ss);
let str2 =a.toUpperCase() + ss.join(''); //数组通过join拼接成字符串
console.log(str2);
下面是另外一些例子。
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
const [first, ...rest] = [];
first // undefined
rest // []
const [first, ...rest] = ["foo"];
first // "foo"
rest // []
const [...butLast, last] = [1, 2, 3, 4, 5]; // 报错
const [first, ...middle, last] = [1, 2, 3, 4, 5]; // 报错
1.7. 类数组转数组
扩展运算符还可以将字符串转为真正的数组。
[...'hello']
// [ "h", "e", "l", "l", "o" ]
let nodeList = document.querySelectorAll('div'); //nodeList是类数组
let array = [...nodeList]; //array是数组
2. 数组循环
2.1 . 常用循环
for(var i = 0; i < arr.length; i++){}
while(){}
2.2. ES 5 新增的循环方法
新增的循环都是数组身上的方法
- arr.forEach()
- arr.map()
- arr.filter()
- arr.some()
- arr.every()
以上这些方法接收的参数一模一样,接受两个参数
第一个: 就是循环的回调函数
第二个: 你希望this指向谁。这个参数用的少。
2.2.1 forEach用法
// arr.forEach()
// forEach其实就是for循环,for里面循环变量得自增,forEach就简单多了
var arr = ["a","b","c","d"]
arr.forEach(function(value,index,arr){
console.log(value,index,arr);
})
// forEach()就是普通for循环的变种
let arr= [10,20,30,40,50,60];
for(var i=0;i<arr.length;i++){ //手动的跟踪下标的变化
console.log(i,arr[i]);
}
// forEach()是数组的方法,会自动跟踪下标
arr.forEach(function(val,index){ //形参怎么命名取决于自己
// console.log(arguments);//每一次会把值,索引,原数组传过来 val index arr
console.log(index,val);
})
// 如果数组的每一项是对象
let arrobj =[{
name:"历史书",
price:20
},{
name:"画册",
price:40
},{
name:"文学",
price:10
}]
arrobj.forEach(function(item,i,arrobj){
console.log(item.name,item.price);
})
//forEach第一个参数是迭代的回调函数(接受值、小标、原数组)。第二个参数修改第一个参数中的this的指向。不管第一个参数内是否有return,整个forEach始终返回undefined。使用forEach就是为了拿到索引、值的。
let obj= {
name:'usename',
age:20
}
let arrobj =[55,66,77,88,99]
let res =arrobj.forEach(function(){ //forEach的返回结果始终是undefined
console.log(this);
return 10 //forEarch始终返回undefined 有return也没用
},obj)
console.log(res);
forEach的this
var arr = ["a","b","c","d"]
arr.forEach(function(value,index,arr){
console.log(this); // 此时this是window
console.log(value,index,arr);
})
// 如果传了第二个参数
var arr = ["a","b","c","d"]
arr.forEach(function(value,index,arr){
console.log(this); // 此时this就是#document
console.log(value,index,arr);
},document)
如果使用箭头函数,那么this就会发生变化
var arr = ["a","b","c","d"]
arr.forEach((value,index,arr) => {
console.log(this); // 此时this就是window
console.log(value,index,arr);
})
// 添加第二个参数
var arr = ["a","b","c","d"]
arr.forEach((value,index,arr) => {
console.log(this); // 此时this还是window
console.log(value,index,arr);
},document)
// 箭头函数里面的this指向函数定义时的this
// 将代码放到一个对象中
var obj= {
name: function(){
var arr = ["a","b","c","d"]
arr.forEach((value,index,arr) => {
console.log(this); // 此时this还是obj
console.log(value,index,arr);
},document)
}
}
// 此时obj.name() this指向obj
2.2.2 map
翻译过来就是地图(映射),非常有用,尤其是数据交互
正常情况下,需要配合return使用,返回新数组,如果没有return,这个就相当于forEach
forEach 返回undefined
map如果没有return 则返回元素项数个undefined组成的新数组
// map 就是将原数组映射成一个新数组,原数组和新数组的个数保持一致,跟forEach一样也传三个参数(值、索引、原数组)
// 原数组每一项翻倍forEach的使用
let arr =[10,20,30,40,50];
let newArr=[];
arr.forEach((val)=>{
newArr.push(val*2)
})
console.log(newArr);
// 原数组每一项翻倍map的使用
let new2Arr =arr.map(function(val){
return val*2; //return的值决定映射数组的值,没有就是undefined
})
// let new2Arr =arr.map((val)=>val*2) //箭头函数简写了
console.log(new2Arr);
// 后台给的数据与页面渲染需要的字段名不一致,可以将后台给的数组数据映射一份
let date =[{
name:"aa",
num:10
} ,{
name:"bb",
num:16
},{
name:"cc",
num:20
}];
let arr = date.map((item,index)=>{ //map第二个参数是this,如果使用了箭头函数就别想改变this了
return{
bookname:item.name,
booknum:item.num
}
})
console.log(arr);
// arr.map()
let arr= [
{title: "aa",read: 100},
{title: "aa",read: 100},
{title: "aa",read: 100}
]
let newArr = arr.map((item,index,arr) => {
console.log(item,index,arr);
return item;
})
注意
平时只用用map,就一定要有return,否则你就用forEach 不要走map
map 可以重新整理我们的数据结构
// arr.map() 整理后台传过来的数据
let arr= [
{title: "aa",read: 100},
{title: "bb",read: 20},
{title: "cc",read: 50}
]
let newArr = arr.map((item,index,arr) => {
let json = {};
json.shop = `*${item.title}--`;
json.price = `¥${item.read}元`
return json;
})
console.log(arr);
console.log(newArr);
2.2.3 filte
过滤,过滤一些不合格的元素,如果回调函数返回true,就留下来,添加到返回的新数组中,为false就过滤
//将数组中的偶数重新建个新数组,map不合适,map要求新数组和原数组个数一致
let arr =[10,23,19,72,88,90,7,6,12];
let newArr =[];
arr.forEach((val)=>{
if(!(val%2)){
newArr.push(val)
}
})
console.log(newArr);
//filter过滤 只有return为ture是才保留,并且不修改值(map可以修改值,也能返回undefined,filter不返回undefined,新数组原数组个数也不一样)
let newArrF =arr.filter((val)=>{
return !(val%2) //偶数模后为0再取反为ture
})
console.log(newArrF);
let newArrFF =arr.filter((val)=>{
return val >= 15 // 大于等于15的保留
})
console.log(newArrFF);
// arr.filter() 过滤
let arr= [
{title: "aa",read: 100},
{title: "bb",read: 20},
{title: "cc",read: 50}
]
let newArr = arr.filter((item,index,arr) => {
return item.read > 50;
})
console.log(arr);
console.log(newArr);
// 过滤所有没有价格的商品不展示,价格为0的也不展示
let arr = [{
name: "苹果",
price: 10
}, {
name: "橘子",
price: 0
}, {
name: "香蕉"
}, {
name: "葡萄",
price: 16
}, {
name: "梨子",
price:2
}];
let newArr =arr.filter((item)=>{
return item.price //价格为0或者undefined的都是false
})
console.log(newArr);
filte方法去重
let arr = [2,1,3,5,2,3,1];
let newArr = arr.filter((val,index,arr)=>{
return arr.indexOf(val) == index; //从前往后查的索引的值是否等于传进来的索引
})
console.log(newArr); // [2, 1, 3, 5]
######2.2.4 some every
这个方法感觉像查找比对的意思,返回的结果是布尔值
//some表示有一个满足条件就返回true,结果是布尔值
let arr = [10, 20, 30, 40, 55, 66, 80];
let bol = arr.some((val, index) => {
console.log(val);//迭代到55就停止
return val > 50 //碰到一个成立就返回true
})
console.log(bol);
let arr = ["a","b","c","d"]
let b = arr.some((item,index,arr) => {
return item == "c";
})
console.log(b); // true
//感觉像查询,数组中只要有一项符合条件,那么就返回true,如果都是false 那结果就是false
// every 判断数组里面是不是都是奇数
let arr = [1,3,7,9,5]
let b = arr.every((item,index,arr) => {
return item % 2 == 1;
})
console.log(b); // true
而every则刚好相反,every是数组中每一项都为true,才返回true ,只要有一项不符合条件都是false
来封装一个小功能
//every有一个不满足条件就返回false,结果是布尔值
let arr = [10, 20, 30, 40, 55, 66, 80];
let bol = arr.every((val, index) => {
console.log(val);//
return val >9 // 全部满足才是true
})
console.log(bol);
let arr = ["apple","banana","orange"]
function finedInArray(arr,item){
return arr.some((val,index,arr) => {
return val == item;
})
}
console.log(finedInArray(arr,"orange")); // true
接下来reduce和reduceRight接收的参数和上面方法都不一样了
######2.2.5 reduce reduceRight
reduce
从左往右计算
reduceRight
是从右往左计算
用的极少,比如求数组的和,阶乘都可以
let arr = [1,2,3,4,5,6,7,8,9,10]
let res = arr.reduce((prev,cur,index,arr) => {
// console.log(arguments);
return prev + cur;
})
console.log(res); //55
prev是上一次的运算结果,cur是当前的值,index是当前的下标,arr是当前的数组
数组去重
let arr = [1,2,2,4,5,4,7,5,6,1]
let res = arr.reduce(function(prev,cur,index,arr){
//console.log(arguments);
if(!prev.includes(cur)){
prev.push(cur)
}
return prev;
},[])
console.log(res);
//reduce求累和
// return是整个迭代完毕后最后一次回调函数return值
let arr=[10,20,30,40,55,60,70];
let result = arr.reduce(function(prev,val,index,arr){
//prev是上一次迭代的结果返回值(初始值)、值、索引、原数组
//没有初始值的情况下,第一个值将作为初始值
console.log(arguments); //第二次迭代开始,每个prev都是上次prev与val的和
return prev+val //求数组累和
})
console.log(result);
//reduce第二个参数为初始值。没传第二个参数时,就把数组第一项作为初始值
let arr=[10,20,30,40,55,60,70];
let result = arr.reduce(function(prev,val,index,arr){
console.log(arguments);
return prev+val
},0) //设置初始值,数组有几项就迭代几次啦
console.log(result);
//reduceRight没有第二个参数的迭代,(也可以传第二个参数,原理和reduce一样,方向不同)
let arr=[10,20,30,40,55,60,70];
let result = arr.reduceRight(function(prev,val,index,arr){
console.log(arguments);
return prev+val
}) //设置初始值,数组有几项就迭代几次啦
console.log(result);
// 统计数据例子
let obj ={children:[{count:10},{count:20},{count:30},{count:40}],
user:[{name:2},{name:12},{name:9},{name:20}]
};
let num = obj.children.reduce(function(prev,item,index,arr){
return prev + item.count
},obj.user.length||0)
console.log(num);
3.ES 6 新增的循环
es6引入的作为遍历所有数据结构的统一的方法。
一个数据结构只要部署了Symbol.iterator属性(控制台打印数组原型上最下面就有这个属性),就被视为具有 iterator(迭代器) 接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法。
let arr =[10,20,30];
console.log(arr)
console.log(arr[Symbol.iterator])//Symbol.iterator这个属性是个函数
console.log(arr[Symbol.iterator]())//Symbol.iterator这个函数执行的结果是对象,for of循环的就是这个迭代器对象
for(let val of arr){ //循环的不是数组,是迭代器对象。js发现一个数组使用for of,会自动调用Symbol.iterator迭代器函数,生成迭代器对象
console.log(val);
}
for(let val of arr.values()){ //循环的不是数组,是迭代器对象。js发现for of一个数组时,会自动调用Symbol.iterator迭代器函数,生成迭代器对象
console.log(val);
}//与循环arr.values() 结果一样
3.1. for ... of 循环(下面三个方法是分别创建不同的三个迭代器对象)
arr.keys() 数组下标集合
arr.values() 数组值集合
arr.entries() 数组的每一项的集合
entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
arr.keys() 、arr.values()、arr.entries() 这三个方法都是挂载到构造函数Array原型对象上的,因此每个数组都可调用这个方法。
let arr =[10,20,30];
for(let val of arr){
console.log(val);
}
for(let val of arr.values()){ //只需要用值
console.log(val);
}
for(let val of arr.keys()){
console.log(val);
}
for(let item of arr.entries()){ //需要值和索引
console.log(item,item[0],item[1]);
}
for(let [index,val] of arr.entries()){ //item是变量,存放的是数组,所以可以解构
console.log(index,val);
}
let arr = ["apple","banana","orange"];
for(let val of arr){
console.log(val);// 和for循环很像// 这个只是循环值,如果循环索引怎么办
}
for(let index of arr.keys()){
console.log(index); // arr.keys() 就是arr数组索引的集合
}
for(let item of arr.entries()){
console.log(item); // item 是有数组的下标和值组成的数组如[0,"apple"] 索引和值一起循环
}
// 也可以像下面这样写
for(let [key, val] of arr.entries()){
console.log(key, val); // 打印索引和值 0 "apple"
}
4.ES6 数组新增方法
4.1. Array.from()
作用: 把类数组(获取一组元素,arguments)对象转成数组
4.1.1 拷贝数组
// ES5的方法
var arr2 = [].slice.call(arr);
var arr2 = Array.prototype.slice.call(arr);
// ES6的方法
let arr = [1,2,3];
let arr2 = [...arr];
// 第二种方法
let arr3 = Array.from(arr);
j
4.1.2 Array.from(),将类数组转成数组 挂载到Array构造函数身上的
参数:
- 第一个参数:一个类数组对象,用于转为真正的数组
- 第二个参数:类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
- 第三个参数:如果map函数里面用到了this关键字,还可以传入Array.from的第三个参数,用来绑定this。
// 将arguments转数组
function haha(){
console.log(arguments); // 类数组
console.log(Array.from(arguments)); // 基于Array创建数组
}
haha(1,2,3,4)
什么是类数组.有length就靠谱
var str = "wuwei";
var arr = Array.from(str);
console.log(arr); // 此时arr就是一个数组了
var obj = {
0: "a",
1: "b",
2: "c",
length: 3
}
var arr = Array.from(obj); // ["a", "b", "c"]
function fun(){
console.log(arguments);
let arr = Array.prototype.slice.apply(arguments)//arguments转为真正的数组
console.log(arr);
//argument身上没有转原型的切割方法,原型上也没有,但真正的数组arr身上有
}
fun(10,20,30)
function fun(){
}
let funO = new fun()
console.log(funO);
console.dir(Object);//打印构造函数对象
//构造函数身上有个修改原型的方法getPrototypeOf,但是不能修改内置的原型。可以修改自己创建的函数,自己创建的原型
Object.getPrototypeOf(funO,Array.prototype)
let obj ={
name:"aa",
age:18
}
//基于字符串创建数组,除了字符串,from基本不给基本类型使用
// let arr = Array.from("hello")
// console.log(arr);
let arr = Array.from("hello",function(val,index){
console.log(arguments);
console.log(this);
return val + this.age //this默认是window
},obj)//第一个参数为类数组,第二个参数是迭代后处理的函数,对数组进行二次处理,自定义迭代函数。第三个参数为修改第二个参数的this指向,this默认是window
console.log(arr);
4.2. Array.of() 将散列的值转为数组 挂载到Array构造函数身上的
把一组值转成数组
var arr = Array.of("a","b","c","d");
这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。
//Array
Array() // []
Array(3) // [, , ,]
Array(3, 11, 8) // [3, 11, 8]
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
let arr = new Array(5);//当数组中只有一项数字值时,就会将数字当成数组的长度,每一项是空项
console.log(arr);
//Array.of用法与new Array类似,Array.of是为了弥补newArray创建数组时,参数只有一个且为数字的情况。
let arr1 = Array.of(10)//静态方法,挂载到构造函数上的
console.log(arr1);
4.3. arr.find() 挂载到数组原型对象上的,意味着所有的实例都能使用这个方法
查找第一个符合要求的数组成员,返回值,如果没找到,则返回undefined
var arr = [50,10,201,103,62,81];
var res = arr.find((val , index ,arr) => {
// console.log(arguments)
return val > 100;
})
console.log(res); // 201
let arr =['苹果','梨子','香蕉','桃子','杏子']
let val = arr.find(function(val,index,arr){ //es6很多方法都是一个回调函数,最终返回一个值
// console.log(arguments);
return val === '香蕉1'; //根据迭代函数的返回值转为布尔值,为true终止迭代,整体返回当前值,如果迭代结束都返回false,整体返回undefined
})
if(val){
console.log(val);
}else{
console.log("没有这个水果");
}
//箭头函数简写
let arr =['苹果','梨子','香蕉','桃子','杏子']
let val = arr.find((val,index,arr)=> val === '香蕉1')
4.4. arr.findIndex()
和find()很像,这是这里找的是位置,返回条件为真时的索引;没找到返回-1
var arr = [50,10,201,103,62,81];
var res = arr.findIndex((val , index ,arr) => {
return val > 100;
})
console.log(res); // 2
let arr =['苹果','梨子','香蕉','桃子','杏子']
let index = arr.findIndex((val,index,arr)=> val === '香蕉')//返回的是索引,查不到返回-1
console.log(index);
4.5.arr.fill()
arr.fill(需要填充的值, 填充开始位置,填充结束位置);
填充内容不包括结束的位置 ,填充会修改原数组
var arr = new Array(3);
arr.fill("默认值");
console.log(arr); //["默认值", "默认值", "默认值"]
// 完整用法
var arr = new Array(10);
arr.fill("默认值",1,3);
console.log(arr); // [empty, "默认值", "默认值", empty × 7]
let arr =['苹果','梨子','香蕉','桃子','杏子']
arr.fill('你好',2,5)
console.log(arr);
// 创建每一项都是10的数组,共10项
var arr =new Array(10).fill(10);
console.log(arr);
let arr = new Array(3).fill({});//有问题 ,fill填充引用类型时,引用类型只会创建一次,都是同一个内存地址的引用
console.log(arr);
arr[0].name =20;
console.log(arr); //数组的每一项都会被添加name属性
//使用map给哪一项添加,哪一项才有,不会都有
let arr1 = new Array(3).map(()=>{});//map是映射,会跳过空项,不会执行回调函数,
console.log(arr1);
let arr = new Array(3).fill('').map(()=>({})); //所以给每一项fill填充undefined或者''
console.log(arr);
arr[0].name=20;
console.log(arr);
5. ES2016里面新增
5.1. arr.includes()
查看数组中是否包含参数的值,返回布尔值
var arr = ["apple" , "origan","banana"];
var a = arr.includes("apple");
console.log(a); // true
var b = arr.includes("apple2");
console.log(b); // false
没有该方法之前,我们通常使用数组的indexOf方法,检查是否包含某个值。,结果为-1就是不包含。
indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相等运算符(===)进行判断,这会导致对NaN的误判。
let arr = [10,NaN,20,30];
let index = arr.indexOf(NaN); //事与愿违,返回-1,这里用的是===,所以是全部false为-1
console.log(index);
let arr = [10,NaN,20,30,+0];
let index = arr.includes(NaN); //includes()返回的是布尔值内部使用的是Object.is判断
console.log(index);
console.log(Object.is(NaN,NaN)); //为true
console.log(Object.is(+0,-0)); //为false
let index1 = arr.includes(-0);
console.log(index1);//为true
6.关于数组的空位
数组的空位指数组的某一个位置没有任何值。
注意,空位不是undefined,一个位置的值等于undefined,依然是有值的。空位是没有任何值,in运算符可以说明这一点。
0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false
var arr =new Array(10).fill(undefined);//这种数组不叫空项,它是有值的
console.log(arr);
arr.forEach(function(){
console.log(1111); //undefined会被执行
})
var arr =new Array(10);//空项
console.log(arr);
arr.forEach(function(){
console.log(1111); //空项不会被执行
})
ES5 对空位的处理,已经很不一致了,大多数情况下会忽略空位。
forEach(), filter(), reduce(), every() 和some()都会跳过空位。
map()会跳过空位,但会保留这个值,map要保证与原数组项数一样
-
join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。
var arr =new Array(10);
console.log(arr); //空项
console.log(arr.join());
var arr1 =new Array(10).fill(undefined); //undefined
console.log(arr1.toString());
ES6 则是明确将空位转为undefined。
Array.from方法会将数组的空位,转为undefined,也就是说,这个方法不会忽略空位。
let aa =Array.from(['a',,'b',,])
console.log(aa); //es6不会跳过空项,会识别成undefined
扩展运算符(...)也会将空位转为undefined。
[...['a',,'b']]
// [ "a", undefined, "b" ]
fill()会将空位视为正常的数组位置。
new Array(3).fill('a') // ["a","a","a"]
for...of循环也会遍历空位。
let arr = [, ,];
for (let i of arr) {
console.log(1);
}
上面代码中,数组arr有两个空位,for...of并没有忽略它们。如果改成map方法遍历,空位是会跳过的。
entries()、keys()、values()、find()和findIndex()会将空位处理成undefined。
// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]
// keys()
[...[,'a'].keys()] // [0,1]
// values()
[...[,'a'].values()] // [undefined,"a"]
// find()
[,'a'].find(x => true) // undefined
// findIndex()
[,'a'].findIndex(x => true) // 0
由于空位的处理规则非常不统一,所以建议避免出现空位。