重难点 splice 、 reduce
1.定义
- JS 数组是一种特殊的对象
JS 没有真正的数组,只是用对象模拟数组,可以用对象的东西,但不一定好用
2.JS 数组和典型数组对比
2.1.典型的数组
元素的数据类型相同
- 使用连续的内存存储
- 通过数字下标获取元素
2.2.JS 的数组(对象的一种)
下标—index—属性名 元素—item—属性值
- 元素的数据类型可以不同
- 内存不一定是连续的(对象是随机存储的)
- 不能通过数字下标,而是通过字符串下标
- 可以有任何 key
- 除了默认的从’0’开始的字符串
- 还可以是 ‘xxx’ 等等,比如 arr[‘xxx’]=1 增加一个属性名为 ‘xxx’ 属性值为 1 的元素
3.创建数组
3.1.新建一个数组
let arr = [1,2,3] //简单写法,常用,arr 是数组名,可变let arr = new Array(1,2,3) //包含元素 1,2,3 的数组,等价于上面一种let arr = new Array(3) //只有一个数字时,表示的是数组的长度
3.2.转化(将不是数组的东西转化成数组)
3.2.1.split (字符串转化为数组的API)
split 后面加一个符号来分割字符串,并且创建一个数组
通过字符串来创建数组
let str = '1,2,3'.split(',') //有逗号字符串'1,2,3'变成数组["1", "2", "3"]let str = '123'.split('') //没有逗号隔开的字符串变成数组["1", "2", "3"]
如果有逗号隔开的字符串用 split(‘’) 变成数组,里面没有加逗号就会出现以下情况(五个元素)
3.2.2.Array.from()
把一个不是数组的东西变成数组(但不是可以把任何东西变成数组)
下面两种情况可以字符串

- 一个有 0,1,2,3 下标 并且有 length 的对象(长度由 length 决定,比如有四个数,但是 length 为2 ,那么只会显示前面两个元素)
3.3.合并数组(concat)—不会改变原数组,获得一个新的数组
arr1.concat(arr2) //数组1.concat(数组2)arr1.concat(5,6,7) //数组2也可以为一个值
3.4.截取数组的一部分(slice)—不会改变原数组—可以想象切面包,slice是下刀的地方
arr1.slice(1) //从第二个元素开始截取,包括第二个元素arr1.slice(0) //全部截取,常用来复制一个数组//JS只提供浅拷贝
3.5.伪数组—没有数组共用属性的数组
伪数组的原型链中没有数组的原型(直接指向对象的原型)
伪数组并没有什么用,碰到伪数组的第一件事就是把它变成数组
arr = {0:'a', 1:'b',2:'c',length:3} //这是一个伪数组
[x] 用下面代码获取全部的 ‘div’ ,这就是一个伪数组
let divList = document.querySelectorAll('div') //没有加 All 只会得到一个数组
4.数组中元素的增删改查(记住 splice)
对于
push/pop/shift/unshift/join这些 API ,在 JS 对象分类里有讲到过,如下arr.pop() //从数组中删除最后一个元素--每次删除一个属性arr.shift() //从数组中删除第一个元素arr.push() //将一个或多个元素添加到数组的末尾arr.unshift() //将一个或多个元素添加到数组的开头arr.join() //所有元素连接成一个字符串并返回//arr.jion()=arr.join(',')--返回"1,2,3"//arr.join('')--返回"123" //arr.join('+')--返回"1+2+3"
4.1.删除元素
4.1.1.不推荐
跟对象一样
delete arr[index]只会把删除的那个元素变成 empty ,下标还在,length 长度也不变- 插播一个名词 【稀疏数组】:大部分元素值没有被使用,造成内存浪费
直接改 length (前面的
Array.from()也有讲到)—但是最好不要改 length 容易出 bug删除头部元素(shift)
- 删除尾部元素(pop)
- 用 shift 和 pop 每次都只会删除一个元素
删除中间元素(splice)(注意不是 slice ,slice不会改变原数组,用于copy一个数组)
arr.splice(index,1,'x','y') //删除index位置开始的的1个元素,并在删除位置添加'x','y'arr.splice(index,1,'x') //删一个元素加一个元素arr.splice(index,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]})} //查看属性名和值
推荐使用下面两种- 用 for 循环遍历数组(这是常见的查看元素的方法)```javascriptfor(let i = 0; i < arr.length; i++){console.log(`${i}:${arr[i]}`)}//里面是console.log(i),就是只打印出属性名//注意反引号 ``
- i 从 0 增长到 length-1
- 长度由
i = 0; i < arr.length决定- forEach 遍历数组(数组原型自带的函数)
arr.forEach(function(item, index){ //function 里的item为属性值,index为属性名,只写一个数就是取属性值console.log(`${index}:${item}`) //只取其中一个数就用一个数取代包括``在内的内容})

- forEach 遍历数组(数组原型自带的函数)
想要更好的理解 forEach 函数,那么就自己写一个 forEach 函数
function forEach(array, fn){ //创建一个函数forEach,这个函数有两个参数;一个数组和一个函数fnfor(let i=0; i<array.length; i++){ //forEach用for访问 0到length-1项,也就是array的每一项fn(array[i], i, array) //对每一项调用fn item/index/array}}
for 和 forEach 两种查看方式的区别
跟对象一样
数组名[index]- index 加不加引号都行,JS 会自动加上
索引(index)越界
index 的范围只在 0 到 length-1
arr[arr.length] //undefindarr[-1] //undefind
实例
for(let i=0;i<=arr.length;i++){console.log(arr[i].toString())}//报错:Cannot read property 'toString' of undefined//一般出现上面的报错就是越界了
上面代码 i 取到了 length,所以
arr[arr.length]===undefined,所以读取不到 toString
出现报错解决办法—哪里报错就把哪里打印出来,如下for(let i=0;i<=arr.length;i++){console.log(arr[i])console.log(arr[i].toString())}//就会发现最后打印出来一个 undefined
查找某个属性是否在数组里
arr.indexOf(item) //item 可修改,item 存在返回索引,否则返回 -1
使用条件查找元素和元素索引(下面方法只会显示找到的第一个)
arr.find(item => item%2 === 0) //找到第一个偶数就会返回它的值,但是不会返回下标arr.findIndex(item => item%2 === 0) //返回第一个偶数索引index
- find 里用 function 来表示(更容易理解)
arr.find(function(item){return item%2 === 0})
4.3.增加元素
4.3.1.尾部添加元素
arr.push(item1,item2) //在arr数组尾部添加一个或多个元素,并返回新长度
4.3.2.头部添加元素
arr.unshift(item1,item2) //在arr数组尾部添加一个或多个元素,并返回新长度
4.3.3.中间添加元素(splice 又来了,上面的删除元素其实已经讲了如何添加)
arr.splice(index,1,'x','y') //删除index位置开始的的1个元素,并在删除位置添加'x','y'
不推荐使用的增加方式
arr[index] = item 因为会根据下标来改变数组长度 length
此方法也可用来修改已有的元素
如上图,添加了一个 ‘10’ length 直接变成了 11,相当于中间多了一大堆空的元素4.4.修改元素
4.4.1.反转顺序(会改变原数组)
补充:arr.reverse()
反转字符串顺序(单独字符串没有 reverse)let s1 = 'abcde'let s2 = s.split('').reverse().join('')//这里分三步,先将字符串变成数组,就可调用reverse,反转顺序之后再合并成一个字符串//等号后面的表达式不会改变原字符串
4.4.2.自定义顺序(sort会改变原数组)
理解了记住下面这一串代码就可以了,不理解就在看看下面两个例子
再写详细一点的函数应该是下面这样的(便于理解,实际没必要按下面这么写)arr.sort((a,b)=>a-b)//会改变原数组//arr.sort() 默认从小到大排序//所以a-b大于0时,也就是a排在后面arr.sort((a,b)=>b-a) //改一下a,b的位置就是从大到小排序了
再来一个实例 ```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(function(a,b){if(a > b){return -1}else if(a === b){return 0}else{return 1}})//这里的a>b也就是说a-b时应该是大于0的,sort默认就应该是a排在后面//结果返回的是一个负数,所以此时a就变成了排在前面,也就是变成了从大到小排序
//可以简写为 arr.sort((a,b)=>{return b.score-a.score}) //再去掉 return 和 {} arr.sort((a,b)=>b.score-a.score) //也就是最初的那个写法,只是没有score
<a name="W91vM"></a>## 4.数组变换先用一张图形象的表示一下,下面再详细讲解<br /><a name="BXo8T"></a>### 4.1.map(n变n)--映射例1:将一个含有六个数的数组里的数变成每个数的平方```javascriptlet arr = [1,2,3,4,5,6]arr.map(item => item*item)//下面是用for循环,不用map// for(let i=0; i<arr.length; i++){// arr[i] = arr[i]*arr[i]// }
例2:数字变星期
//方法一let arr = [0,1,2,2,3,3,3,4,4,4,4,6]let arr2 = arr.map((item)=>{return {0:'周日',1:'周一',2:'周二',3:'周三',4:'周四',5:'周五',6:'周六'}[item]})console.log(arr2)//方法2let arr = [0,1,2,2,3,3,3,4,4,4,4,6]let arr2 = arr.map(item=>{let week = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']return week[item]})console.log(arr2)// ['周日', '周一', '周二', '周二', '周三', '周三', '周三', '周四', '周四', '周四', '周四','周六']
4.2.filter(n变少)
例1:找出数组里的偶数
arr.filter(item => item %2 === 0 ? true : false)//缩写//arr.filter(item => item %2 === 0)
例2:找出所有大于 60 分的成绩
let scores = [95,91,59,55,42,82,72,85,67,66,55,91]let scores2 = scores.filter(item => item >= 60 ? true : false)//这里直接 (item => item >= 60) 就可以,因为这个直接就是返回的布尔值console.log(scores2)// [95,91,82,72,85,67,66, 91]
*4.3.reduce(n变1)
例1:求和
[ ] 用 for 循环
let sum = 0for(let i=0; i<arr.length; i++){sum += arr[i]//sum = sum + arr[i]}console.log(sum)
[x] 用 reduce(n变1)(记住这个最简单的写法)
arr.reduce((sum,item)=>{return sum+item},0)
0 和下面的 [] 表示的是默认初始值
[x] 用 reduce 代替 map(有了 reduce 就可以不需要 map)
arr.reduce((result,item)=>{return result.concat(item*item)},[])//因为这个结果是一个数组,所以初始值就设置为空数组 [] 而不是 0//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) //如果是奇数,加一个空,偶数,加入这个数 ,[])
例2:算出所有奇数之和```javascriptlet scores = [95,91,59,55,42,82,72,85,67,66,55,91]let sum = scores.reduce((result,item)=>{if(item % 2 === 1){return result+item}else{return result}},0)console.log(sum)//缩写成 问号冒号 形式let scores = [95,91,59,55,42,82,72,85,67,66,55,91]let sum = scores.reduce((result,item)=>item % 2 === 1 ? result+item : result ,0)console.log(sum) // 奇数之和:598// 参考答案let scores = [95,91,59,55,42,82,72,85,67,66,55,91]let sum = scores.reduce((sum, n)=>{return n%2===0?sum:sum+n},0)console.log(sum)//相当于上面的用 reduce 代替 filter 的第三种(连 return 都去掉了)let scores = [95,91,59,55,42,82,72,85,67,66,55,91]let sum = scores.reduce((sum, n)=>sum +(n%2===0?0:n) //如果不是数字,是数组,就不是用+而是用 sum.concat() 连起来,0)console.log(sum)
提升的一个题
//原本的数组let arr = [{名称: '动物',id: 1,parent: null},{名称: '狗',id: 2,parent: 1},{名称: '猫',id: 3,parent: 1}]//变成下面这样的对象{id: 1, 名称:'动物', children:[{id: 2, 名称:'狗', children: null},{id: 3, 名称:'猫', children: null},]} //相当于是把上面数组里的三个对象变成一个对象//答案arr.reduce((result,item)=>{if(item.parent === null){result.id = item.idresult['名称'] = item['名称']}else{result.children.push(item) //在result.children上加上itemdelete item.parentitem.children = null //再把 parent 删掉,加上 children = null}return result //不要忘记写 return 否则中间的写的就相当于空的},{id: null, children: []}) //先使结果的初始值为一个空对象,再在里面写入 id 和 children 的初始值,如果 id 和 children 不需要初始化,就直接写个空对象就行
上面一题的答题过程(可省略不看)
//首先将 item 一股脑全部放到 result 里arr.reduce((result,item)=>{result[item.id] = itemreturn result},{})//得到的结果是下面的图一//因为不同点在 parent 上,所以就得出下面的代码arr.reduce((result,item)=>{if(item.parent === null){result.id = item.idresult['名称'] = item['名称']}else{result.children.push(item) //在result.children上加上item}return result //不要忘记写 return 否则中间的写的就相当于空的},{id: null, children: []}) //先初始化 id 和 children//结果如图二所示,这里显示的结果就只有 parent 没有修改,再把 parent 删掉,加上 children = nullarr.reduce((result,item)=>{if(item.parent === null){result.id = item.idresult['名称'] = item['名称']}else{result.children.push(item)delete item.parentitem.children = null //加了这两行代码}return result},{id: null, children: []})//最终结果如图三,就很符合题目了
图一
图二
图三
