第二十五节: ES6 数组扩展

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 新增的循环方法

新增的循环都是数组身上的方法

  1. arr.forEach()
  2. arr.map()
  3. arr.filter()
  4. arr.some()
  5. 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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354

推荐阅读更多精彩内容