重难点 splice 、 reduce

1.定义

  • JS 数组是一种特殊的对象
  • JS 没有真正的数组,只是用对象模拟数组,可以用对象的东西,但不一定好用

    2.JS 数组和典型数组对比

    2.1.典型的数组

  • 元素的数据类型相同

  • 使用连续的内存存储
  • 通过数字下标获取元素

image.png

2.2.JS 的数组(对象的一种)

下标—index—属性名 元素—item—属性值

  • 元素的数据类型可以不同
  • 内存不一定是连续的(对象是随机存储的)
  • 不能通过数字下标,而是通过字符串下标
  • 可以有任何 key
    • 除了默认的从’0’开始的字符串
    • 还可以是 ‘xxx’ 等等,比如 arr[‘xxx’]=1 增加一个属性名为 ‘xxx’ 属性值为 1 的元素

image.png

3.创建数组

3.1.新建一个数组

  1. let arr = [1,2,3] //简单写法,常用,arr 是数组名,可变
  2. let arr = new Array(1,2,3) //包含元素 1,2,3 的数组,等价于上面一种
  3. let arr = new Array(3) //只有一个数字时,表示的是数组的长度

3.2.转化(将不是数组的东西转化成数组)

3.2.1.split (字符串转化为数组的API)

split 后面加一个符号来分割字符串,并且创建一个数组

  • 通过字符串来创建数组

    1. let str = '1,2,3'.split(',') //有逗号字符串'1,2,3'变成数组["1", "2", "3"]
    2. let str = '123'.split('') //没有逗号隔开的字符串变成数组["1", "2", "3"]

    如果有逗号隔开的字符串用 split(‘’) 变成数组,里面没有加逗号就会出现以下情况(五个元素)
    image.png

    3.2.2.Array.from()

    把一个不是数组的东西变成数组(但不是可以把任何东西变成数组)
    下面两种情况可以

  • 字符串

image.png

  • 一个有 0,1,2,3 下标 并且有 length 的对象(长度由 length 决定,比如有四个数,但是 length 为2 ,那么只会显示前面两个元素)

image.png

3.3.合并数组(concat)—不会改变原数组,获得一个新的数组

  1. arr1.concat(arr2) //数组1.concat(数组2)
  2. arr1.concat(5,6,7) //数组2也可以为一个值

image.png

3.4.截取数组的一部分(slice)—不会改变原数组—可以想象切面包,slice是下刀的地方

  1. arr1.slice(1) //从第二个元素开始截取,包括第二个元素
  2. arr1.slice(0) //全部截取,常用来复制一个数组
  3. //JS只提供浅拷贝

3.5.伪数组—没有数组共用属性的数组

伪数组的原型链中没有数组的原型(直接指向对象的原型)
伪数组并没有什么用,碰到伪数组的第一件事就是把它变成数组

  1. arr = {0:'a', 1:'b',2:'c',length:3} //这是一个伪数组
  • [x] 用下面代码获取全部的 ‘div’ ,这就是一个伪数组

    1. let divList = document.querySelectorAll('div') //没有加 All 只会得到一个数组

    image.png

    4.数组中元素的增删改查(记住 splice)

    对于push/pop/shift/unshift/join这些 API ,在 JS 对象分类里有讲到过,如下

    1. arr.pop() //从数组中删除最后一个元素--每次删除一个属性
    2. arr.shift() //从数组中删除第一个元素
    3. arr.push() //将一个或多个元素添加到数组的末尾
    4. arr.unshift() //将一个或多个元素添加到数组的开头
    5. arr.join() //所有元素连接成一个字符串并返回
    6. //arr.jion()=arr.join(',')--返回"1,2,3"
    7. //arr.join('')--返回"123" //arr.join('+')--返回"1+2+3"

    4.1.删除元素

    4.1.1.不推荐

  • 跟对象一样

    • delete arr[index]只会把删除的那个元素变成 empty ,下标还在,length 长度也不变
    • 插播一个名词 【稀疏数组】:大部分元素值没有被使用,造成内存浪费
  • 直接改 length (前面的Array.from()也有讲到)—但是最好不要改 length 容易出 bug

    • let arr = [1,2,3,4,5];arr.length = 1只会显示第一个元素

      *4.1.2.推荐使用的方法—此方法会改变原数组

  • 删除头部元素(shift)

  • 删除尾部元素(pop)
    • 用 shift 和 pop 每次都只会删除一个元素
  • 删除中间元素(splice)(注意不是 slice ,slice不会改变原数组,用于copy一个数组)

    1. arr.splice(index,1,'x','y') //删除index位置开始的的1个元素,并在删除位置添加'x','y'
    2. arr.splice(index,1,'x') //删一个元素加一个元素
    3. arr.splice(index,1) //只删不加
    • 括号里第一个数表示的是要修改的位置,如果不删除元素,就是把元素加到那个位置
    • 第二个数表示的是要删除的位数,为 0 则不删除
    • 后面的就是要添加的元素,可以为一个或多个元素

      4.2.查看元素

      4.2.1.查看所有元素

  • 普通对象使用(不推荐使用) ```javascript let arr = [1,2,3,4,5] arr.x = ‘xxx’ //在数组arr里增加一个属性名为’x’,值为’xxx’的元素 Object.keys(数组名) //查看所有属性名 Object.values(数组名) //查看所有属性值

for(let i in arr){console.log(i)} //查看属性名,就是下面的那种不加后面的属性值 for(let key in arr){console.log(${key}:${arr[key]})} //查看属性名和值

  1. 推荐使用下面两种
  2. - for 循环遍历数组(这是常见的查看元素的方法)
  3. ```javascript
  4. for(let i = 0; i < arr.length; i++){
  5. console.log(`${i}:${arr[i]}`)
  6. }
  7. //里面是console.log(i),就是只打印出属性名
  8. //注意反引号 ``
  • i 从 0 增长到 length-1
  • 长度由 i = 0; i < arr.length决定
    • forEach 遍历数组(数组原型自带的函数)
      1. arr.forEach(function(item, index){ //function 里的item为属性值,index为属性名,只写一个数就是取属性值
      2. console.log(`${index}:${item}`) //只取其中一个数就用一个数取代包括``在内的内容
      3. })
      image.png
  • 想要更好的理解 forEach 函数,那么就自己写一个 forEach 函数

    1. function forEach(array, fn){ //创建一个函数forEach,这个函数有两个参数;一个数组和一个函数fn
    2. for(let i=0; i<array.length; i++){ //forEach用for访问 0到length-1项,也就是array的每一项
    3. fn(array[i], i, array) //对每一项调用fn item/index/array
    4. }
    5. }
  • for 和 forEach 两种查看方式的区别

    • for 支持 break 和 continue(forEach 不支持)

      1. for(let i=0;i<arr.length;i++){
      2. console.log(`${i} : ${arr[i]}`)
      3. if (i===3){break;}
      4. }
      5. //在i===3时 break 打断,退出循环
    • for循环只是个块级作用域,而forEach 是函数

      4.2.2.查看单个属性

  • 跟对象一样

    • 数组名[index]
    • index 加不加引号都行,JS 会自动加上
  • 索引(index)越界

    • index 的范围只在 0 到 length-1

      1. arr[arr.length] //undefind
      2. arr[-1] //undefind
    • 实例

      1. for(let i=0;i<=arr.length;i++){
      2. console.log(arr[i].toString())
      3. }
      4. //报错:Cannot read property 'toString' of undefined
      5. //一般出现上面的报错就是越界了

      上面代码 i 取到了 length,所以arr[arr.length]===undefined,所以读取不到 toString
      出现报错解决办法—哪里报错就把哪里打印出来,如下

      1. for(let i=0;i<=arr.length;i++){
      2. console.log(arr[i])
      3. console.log(arr[i].toString())
      4. }
      5. //就会发现最后打印出来一个 undefined
  • 查找某个属性是否在数组里

    1. arr.indexOf(item) //item 可修改,item 存在返回索引,否则返回 -1
  • 使用条件查找元素和元素索引(下面方法只会显示找到的第一个)

    1. arr.find(item => item%2 === 0) //找到第一个偶数就会返回它的值,但是不会返回下标
    2. arr.findIndex(item => item%2 === 0) //返回第一个偶数索引index
    • find 里用 function 来表示(更容易理解)
      1. arr.find(function(item){return item%2 === 0})

      4.3.增加元素

      4.3.1.尾部添加元素

      1. arr.push(item1,item2) //在arr数组尾部添加一个或多个元素,并返回新长度

      4.3.2.头部添加元素

      1. arr.unshift(item1,item2) //在arr数组尾部添加一个或多个元素,并返回新长度

      4.3.3.中间添加元素(splice 又来了,上面的删除元素其实已经讲了如何添加)

      1. arr.splice(index,1,'x','y') //删除index位置开始的的1个元素,并在删除位置添加'x','y'

      不推荐使用的增加方式

      arr[index] = item 因为会根据下标来改变数组长度 length
      此方法也可用来修改已有的元素
      image.png
      如上图,添加了一个 ‘10’ length 直接变成了 11,相当于中间多了一大堆空的元素

      4.4.修改元素

      4.4.1.反转顺序(会改变原数组)

      1. arr.reverse()
      补充:
      反转字符串顺序(单独字符串没有 reverse)
      1. let s1 = 'abcde'
      2. let s2 = s.split('').reverse().join('')
      3. //这里分三步,先将字符串变成数组,就可调用reverse,反转顺序之后再合并成一个字符串
      4. //等号后面的表达式不会改变原字符串

      4.4.2.自定义顺序(sort会改变原数组)

      理解了记住下面这一串代码就可以了,不理解就在看看下面两个例子
      1. arr.sort((a,b)=>a-b)
      2. //会改变原数组
      3. //arr.sort() 默认从小到大排序
      4. //所以a-b大于0时,也就是a排在后面
      5. arr.sort((a,b)=>b-a) //改一下a,b的位置就是从大到小排序了
      再写详细一点的函数应该是下面这样的(便于理解,实际没必要按下面这么写)
      1. arr.sort(function(a,b){
      2. if(a > b){return -1}
      3. else if(a === b){return 0}
      4. else{return 1}
      5. })
      6. //这里的a>b也就是说a-b时应该是大于0的,sort默认就应该是a排在后面
      7. //结果返回的是一个负数,所以此时a就变成了排在前面,也就是变成了从大到小排序
      再来一个实例 ```javascript let arr= [ {‘name’:’小明’,’score’:99}, {‘name’:’小红’,’score’:90}, {‘name’:’小黄’,’score’:95} ] //根据成绩来排名 arr.sort(function(a,b){ if(a.score > b.score){return -1} else if(a.score === b.score) {return 0} else {return 1} }) //从大到小排序

//可以简写为 arr.sort((a,b)=>{return b.score-a.score}) //再去掉 return 和 {} arr.sort((a,b)=>b.score-a.score) //也就是最初的那个写法,只是没有score

  1. <a name="W91vM"></a>
  2. ## 4.数组变换
  3. 先用一张图形象的表示一下,下面再详细讲解<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1550299/1595488894858-833a5026-36e3-4a77-99de-25e9b16e79b7.png#align=left&display=inline&height=315&margin=%5Bobject%20Object%5D&name=image.png&originHeight=315&originWidth=569&size=119405&status=done&style=none&width=569)
  4. <a name="BXo8T"></a>
  5. ### 4.1.map(n变n)--映射
  6. 例1:将一个含有六个数的数组里的数变成每个数的平方
  7. ```javascript
  8. let arr = [1,2,3,4,5,6]
  9. arr.map(item => item*item)
  10. //下面是用for循环,不用map
  11. // for(let i=0; i<arr.length; i++){
  12. // arr[i] = arr[i]*arr[i]
  13. // }

例2:数字变星期

  1. //方法一
  2. let arr = [0,1,2,2,3,3,3,4,4,4,4,6]
  3. let arr2 = arr.map((item)=>{
  4. return {0:'周日',1:'周一',2:'周二',3:'周三',4:'周四',5:'周五',6:'周六'}[item]
  5. })
  6. console.log(arr2)
  7. //方法2
  8. let arr = [0,1,2,2,3,3,3,4,4,4,4,6]
  9. let arr2 = arr.map(item=>{
  10. let week = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
  11. return week[item]
  12. })
  13. console.log(arr2)
  14. // ['周日', '周一', '周二', '周二', '周三', '周三', '周三', '周四', '周四', '周四', '周四','周六']

4.2.filter(n变少)

例1:找出数组里的偶数

  1. arr.filter(item => item %2 === 0 ? true : false)
  2. //缩写
  3. //arr.filter(item => item %2 === 0)

例2:找出所有大于 60 分的成绩

  1. let scores = [95,91,59,55,42,82,72,85,67,66,55,91]
  2. let scores2 = scores.filter(item => item >= 60 ? true : false)
  3. //这里直接 (item => item >= 60) 就可以,因为这个直接就是返回的布尔值
  4. console.log(scores2)
  5. // [95,91,82,72,85,67,66, 91]

*4.3.reduce(n变1)

例1:求和

  • [ ] 用 for 循环

    1. let sum = 0
    2. for(let i=0; i<arr.length; i++){
    3. sum += arr[i]
    4. //sum = sum + arr[i]
    5. }
    6. console.log(sum)
  • [x] 用 reduce(n变1)(记住这个最简单的写法)

    1. arr.reduce((sum,item)=>{return sum+item},0)

    0 和下面的 [] 表示的是默认初始值

  • [x] 用 reduce 代替 map(有了 reduce 就可以不需要 map)

    1. arr.reduce((result,item)=>{return result.concat(item*item)},[])
    2. //因为这个结果是一个数组,所以初始值就设置为空数组 [] 而不是 0
    3. //concat 就是把之前的结果和这次的结果连起来,连成数组,而不是加起来
  • [x] 用 reduce 代替 filter ```javascript arr.reduce((result,item)=>{ if(item %2 === 1){ return result }else{ return result.concat(item) } },[])

//用 问号冒号 缩写,往下看 arr.reduce((result,item)=> item %2 === 1 ? result : result.concat(item),[])

//再改写(如果这个不会就用上面那个) arr.reduce((result,item)=> result.concat(item % 2 === 1 ? [] : item) //如果是奇数,加一个空,偶数,加入这个数 ,[])

  1. 2:算出所有奇数之和
  2. ```javascript
  3. let scores = [95,91,59,55,42,82,72,85,67,66,55,91]
  4. let sum = scores.reduce((result,item)=>{
  5. if(item % 2 === 1){
  6. return result+item
  7. }else{
  8. return result
  9. }
  10. },0)
  11. console.log(sum)
  12. //缩写成 问号冒号 形式
  13. let scores = [95,91,59,55,42,82,72,85,67,66,55,91]
  14. let sum = scores.reduce((result,item)=>
  15. item % 2 === 1 ? result+item : result ,0)
  16. console.log(sum) // 奇数之和:598
  17. // 参考答案
  18. let scores = [95,91,59,55,42,82,72,85,67,66,55,91]
  19. let sum = scores.reduce((sum, n)=>{
  20. return n%2===0?sum:sum+n
  21. },0)
  22. console.log(sum)
  23. //相当于上面的用 reduce 代替 filter 的第三种(连 return 都去掉了)
  24. let scores = [95,91,59,55,42,82,72,85,67,66,55,91]
  25. let sum = scores.reduce((sum, n)=>
  26. sum +(n%2===0?0:n) //如果不是数字,是数组,就不是用+而是用 sum.concat() 连起来
  27. ,0)
  28. console.log(sum)

提升的一个题

  1. //原本的数组
  2. let arr = [
  3. {名称: '动物',id: 1,parent: null},
  4. {名称: '狗',id: 2,parent: 1},
  5. {名称: '猫',id: 3,parent: 1}
  6. ]
  7. //变成下面这样的对象
  8. {
  9. id: 1, 名称:'动物', children:[
  10. {id: 2, 名称:'狗', children: null},
  11. {id: 3, 名称:'猫', children: null},
  12. ]
  13. } //相当于是把上面数组里的三个对象变成一个对象
  14. //答案
  15. arr.reduce((result,item)=>{
  16. if(item.parent === null){
  17. result.id = item.id
  18. result['名称'] = item['名称']
  19. }else{
  20. result.children.push(item) //在result.children上加上item
  21. delete item.parent
  22. item.children = null //再把 parent 删掉,加上 children = null
  23. }
  24. return result //不要忘记写 return 否则中间的写的就相当于空的
  25. },{id: null, children: []}) //先使结果的初始值为一个空对象,再在里面写入 id 和 children 的初始值,如果 id 和 children 不需要初始化,就直接写个空对象就行

上面一题的答题过程(可省略不看)

  1. //首先将 item 一股脑全部放到 result 里
  2. arr.reduce((result,item)=>{
  3. result[item.id] = item
  4. return result
  5. },{})
  6. //得到的结果是下面的图一
  7. //因为不同点在 parent 上,所以就得出下面的代码
  8. arr.reduce((result,item)=>{
  9. if(item.parent === null){
  10. result.id = item.id
  11. result['名称'] = item['名称']
  12. }else{
  13. result.children.push(item) //在result.children上加上item
  14. }
  15. return result //不要忘记写 return 否则中间的写的就相当于空的
  16. },{id: null, children: []}) //先初始化 id 和 children
  17. //结果如图二所示,这里显示的结果就只有 parent 没有修改,再把 parent 删掉,加上 children = null
  18. arr.reduce((result,item)=>{
  19. if(item.parent === null){
  20. result.id = item.id
  21. result['名称'] = item['名称']
  22. }else{
  23. result.children.push(item)
  24. delete item.parent
  25. item.children = null //加了这两行代码
  26. }
  27. return result
  28. },{id: null, children: []})
  29. //最终结果如图三,就很符合题目了

图一
image.png
图二
image.png
图三
image.png