js中数组的操作
我们按照是否修改数组以及最终返回值是否是得到数组来分类
知识补充
什么是可变参数
简单来说就是一个切片,对于可变参数,你可以传多个元素,也可以传一个 ...[]
展开数组
...
是数组展开语法表示
基本操作
以下介绍基本的增删操作
添加和删除
如果是新手的话一定要注意我们函数里面是否传了参数
可以简单的认为简单的删除不需要传参,但是添加是需要的,不然怎么知道添加的是什么呢?
push()
- 参数: 可变参数
...items
- 作用:在数组末尾添加一个或者多个元素
- 用法:
arr.push("newItem")
- 结果:
[ "a", "b", "c", "newItem" ]
pop()
- 作用:在数组末尾删除一个元素
- 用法:
arr.pop()
- 结果:
[ "a", "b" ]
unshift()
- 参数: 可变参数
...items
- 作用:在数组头部添加一个或者多个元素
- 用法:
arr.unshift("newItem")
- 结果:
[ "newItem", "a", "b", "c" ]
shift()
- 作用:在数组头部删除一个元素
- 用法:
arr.shift()
- 结果:
[ "b", "c" ]
修改元素
以下介绍对元素本身的修改操作
splice()
- 作用:在数组指定位置添加或者删除指定数目的元素
- 参数一开始位置
start
,代表的是你指定的位置,即索引(index);参数二删除数目deleteCount
,要删除的元素个数 n;参数三可变参数never[]
,表示添加的元素
注意:第二个参数可以为0,即表示不删除,第三个参数是可变参数
删除
js
let arr = ["a", "b", "c"]
const changeArrHandel = () => {
arr.splice(1,2)
console.log(arr) //[ "a" ]
}
changeArrHandel()
解释:我们第一个参数是1,表示索引为1,所以就是在第二个元素开始的,第二个参数是2,表示删除两个数据,删除了"b"和"c",所以我们最后的结果就是 ["a"]
了
注意:我们的n可以大于剩下的元素个数,但是n为-1是不可以起到索引以后剩余元素的功能的(学了python的朋友可能会习惯这么用)
如果要删除剩下的元素 我建议你这么做
js
let arr = ["a", "b", "c"]
const changeArrHandel = () => {
arr.splice(1,arr.length - 1)
console.log(arr) //[ "a" ]
}
changeArrHandel()
增加
js
let arr = ["a", "b", "c"]
const changeArrHandel = () => {
arr.splice(1,0,"e","f") //[ "a", "e", "f", "b", "c" ]
console.log(arr)
//上面传两个字母的相当于下面的传一个展开数组 ...是数组展开语法 效果是一样的
//arr.splice(1, 0, ...["e","f"])
}
changeArrHandel()
解释:由上面的删除操作我们知道了我们的i(index)为1,我们定位的位置为"b"现在所在的位置。然后我们的n为0,表示不删除。第三个可变参数我们传的是"e","f"
所以我们要做的就是,不删除,并且在"b"的位置依次添加"e"和"f",所以我们最后的结果就是 ["a", "e", "f", "b", "c"]
既删除又添加
js
let arr = ["a", "b", "c"]
const changeArrHandel = () => {
arr.splice(1, 1, "e", "f")
console.log(arr) //[ "a", "e", "f", "c" ]
}
changeArrHandel()
这个不解释,有上面两个的解释,是可以理解的
遍历和转换
以下介绍数组的遍历和转换操作
遍历数组
for
最基础的遍历方法
js
const arr = [1, 2, 3, 4, 5, 6]
const arrTraverse = () => {
for (let index = 0; index < arr.length; index++) {
console.log(index)
}
//依次输出 0 1 2 3 4 5
}
arrTraverse()
forEach
接受一个回调函数,我们接下来打印该回调函数的参数
js
const arr = [1, 2, 3, 4, 5, 6]
const arrTraverse = () => {
arr.forEach((...obj) => {
console.log(obj)
})
// (3)[1, 0, Array(6)]
// (3)[2, 1, Array(6)]
// (3)[3, 2, Array(6)]
// (3)[4, 3, Array(6)]
// (3)[5, 4, Array(6)]
// (3)[6, 5, Array(6)]
}
arrTraverse()
我们可以知道,这个回调函数中有三个参数,元素值
item
,索引index
,遍历数组自身self
两者区别
- for循环支持
break
for循环中可以添加特定条件使得循环退出(使用break),而forEach则不行
- for循环支持异步,forEach不支持
本质是forEach内部的
await
无法影响到外部 即是否创建新的作用域
代码比较如下
使用for循环
js
import axios from "axios"
const queryArr = [1, 2, 3, 4, 5, 6, 7]
const requestFunc = async () => {
for (let index = 0; index < queryArr.length; index++) {
await axios.get(`https://jsonplaceholder.typicode.com/posts/${queryArr[index]}`)
.then(res => console.log(res.data.id))
.catch(err => console.log(err))
}
}
console.log("请求开始")
await requestFunc()
console.log("请求结束")
//输出依次如下
// 请求开始
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 请求结束
使用forEach
js
import axios from "axios"
const queryArr = [1, 2, 3, 4, 5, 6, 7]
const requestFunc = async () => {
queryArr.forEach(async (item) => {
await axios.get(`https://jsonplaceholder.typicode.com/posts/${item}`)
.then(res => console.log(res.data.id))
.catch(err => console.log(err))
})
}
console.log("请求开始")
await requestFunc()
console.log("请求结束")
//依次输出
// 请求开始
// 请求结束
// 1
// 2
// 6
// 4
// 5
// 3
// 7
很明显,这样写不支持异步
输出顺序有问题,但是请求是按顺序执行的,不信的话可以去看网络请求
原因就是forEach每次遍历执行会创建一个函数,我们的async和await的异步操作仅仅限于函数内部
即使我们的requestFunc使用了async,但是我们requestFunc内部并没有直接的await,仅有的一个await是forEach内部的异步操作,这个异步操作并不能影响外部
而我们的for循环执行并没有创建新的作用域,所以支持异步
总结就是
1.for 循环可以与 await 一起使用来按顺序处理异步操作,因为 await 会暂停循环直到当前异步操作完成
2.forEach 不能直接与 await 一起使用来按顺序处理异步操作,因为 forEach 本身不会等待其回调函数完成
3.for循环开销更小
forEach参数为回调函数,开销更大
转换数组
以下介绍数组的转换操作
map
map方法接收一个回调函数,回调函数中有三个参数,元素值
item
,索引index
,遍历数组自身self
打印参数如下
js
const arr = [{
name: "李华",
vip: false
},
{
name: "小明",
vip: true
}, {
name: "小红",
vip: true
}]
const changeArrHandel = () => {
arr.map(console.log)
// { name: '李华', vip: false } 0(3)[{… }, {… }, {… }]
// { name: '小明', vip: true } 1(3)[{… }, {… }, {… }]
// { name: '小红', vip: true } 2(3)[{… }, {… }, {… }]
}
changeArrHandel()
接着我们演示一下map的基础使用
得到指定字段
js
const arr = [{
name: "李华",
vip: false
},
{
name: "小明",
vip: true
}, {
vip: true
}]
let nerArr = []
const changeArrHandel = () => {
nerArr = arr.map((item) => {
return item.name
})
console.log(nerArr) //['李华', '小明', undefined]
}
changeArrHandel()
解释: map方法会遍历数组中的每个元素,在返回时指定返回字段,得到一个新的数组,该数组内只含返回的指定字段,若遍历的元素不存在该字段,则返回 undefined
返回时可以指定多个字段,需要用 { }
包裹
其实这里涉及到了ES6的解构语法,下篇文章会说
修改指定字段
js
const arr = [{
name: "李华",
vip: false
},
{
name: "小明",
vip: true
}, {
vip: true
}]
let nerArr = []
const changeArrHandel = () => {
nerArr = arr.map((item) => {
return {
...item,
name: "我叫" + item.name
}
})
console.log(nerArr)
// 0: { name: '我叫李华', vip: false }
// 1: { name: '我叫小明', vip: true }
// 2: { vip: true, name: '我叫undefined' }
}
changeArrHandel()
解释:我们使用map遍历数组,首先使用...item返回所有字段,然后接着使用name: "我叫" + item.name 来覆盖原先的name字段
至于为什么三个元素的字段顺序不一样,这是因为第三个元素原先并没有name字段,这个字段是新添加的,放在后面,其余两个元素有了name字段,做的是修改操作,顺序还是第一个顺序,如此便导致了字段顺序的不同
添加字段
还是同样的数据,现在我们为每个元素添加一个新的字段age
js
const arr = [{
name: "李华",
vip: false
},
{
name: "小明",
vip: true
}, {
vip: true
}]
let nerArr = []
const changeArrHandel = () => {
nerArr = arr.map((item) => {
return {
...item,
age: 18
}
})
console.log(nerArr)
// 0: { name: '李华', vip: false, age: 18 }
// 1: { name: '小明', vip: true, age: 18 }
// 2: { vip: true, age: 18 }
}
changeArrHandel()
其中也使用了ES6的展开语法,这个相关细节以及解构下篇文章会讲到
查找和筛选
查找元素
find
- 作用:返回数组中满足提供的回调函数的第一个元素的值,如果没有元素满足回调函数,则返回
undefined
- 参数:接受一个回调函数,回调函数中有三个参数,元素值
item
,索引index
,遍历数组自身self
js
const arr = ["Go","Python","JavaScript"]
const arrFind = () => {
const found = arr.find((item)=>{
return item === "Go"
})
console.log(found) //Go
}
arrFind()
findIndex
- 作用:返回数组中满足提供的回调函数的第一个元素的索引,如果没有元素满足回调函数,则返回
-1
- 参数:与
find
一致
js
const arr = ["Go","Python","JavaScript"]
const arrFind = () => {
const foundIndex = arr.findIndex((item)=>{
return item === "Go"
})
console.log(foundIndex) //0
}
arrFind()
indexOf
- 作用:返回数组中第一个匹配元素的索引,如果没有找到匹配元素则返回
-1
- 参数: 接受两个参数,
searchElement
:要查找的元素,fromIndex
(可选):从该索引位置开始查找
js
const arr = ['apple', 'banana', 'orange', 'banana', 'pear']
const arrFind = () => {
let index = arr.indexOf('banana')
console.log(index) // 输出: 1,第一个 'banana' 的索引是 1
index = arr.indexOf('apple',1)
console.log(index) // 输出: -1,从索引1开始,往后没有找到 'apple'
}
arrFind()
lastIndexOf
- 作用:方法返回数组中指定元素最后一次出现的索引,从数组的末尾向前搜索。如果没有找到匹配元素则返回
-1
- 参数:与
indexOf
一致
js
const arr = ['apple', 'banana', 'orange', 'banana', 'pear']
const arrFind = () => {
let index = arr.lastIndexOf('banana')
console.log(index) // 输出: 3,最后一个 'banana' 的索引是 3
index = arr.lastIndexOf('grape')
console.log(index) // 输出: -1,没有找到 'grape'
}
arrFind()
includes
用于判断一个字符串或数组是否包含指定值
根据使用场景的不同,includes 方法有两个主要的实现:一个用于字符串,一个用于数组。
对于字符串区分大小写
对于对象和数组,它检测的是引用相等性,而不是内容相等性
js
const arr = [1, 2, 3, 4, 5, 6, 7]
const str = "abcd"
const arrTraverse = () => {
const result1 = arr.includes(2)
const result2 = str.includes("b")
console.log(result1) //true
console.log(result2) //true
}
arrTraverse()
筛选元素
filter()
方法参数 : filter方法接受一个回调函数,回调函数中有三个参数,元素值
item
,索引index
,遍历数组自身self
打印参数如下
js
const arr = [1, 2, 3, 4, 5]
const changeArrHandel = () => {
arr.filter(console.log)
// 1 0(5)[1, 2, 3, 4, 5]
// 2 1(5)[1, 2, 3, 4, 5]
// 3 2(5)[1, 2, 3, 4, 5]
// 4 3(5)[1, 2, 3, 4, 5]
// 5 4(5)[1, 2, 3, 4, 5]
}
changeArrHandel()
由filter这个函数的名字我们也可以得知,这个函数的作用就是过滤,即按照我们的意愿来过滤数组中的部分数据(当然你也可以在过滤的时候进行其他的操作,所以也可以理解为筛选)
话不多说,我们来演示一下简单的过滤操作
由于我们不目前不关注索引和原有数组,所以我们接下来的回调函数中也只传一个 item
参数了
示例
js
let arr = [{
name: "李华",
vip: false
},
{
name: "小明",
vip: true
}, {
name: "小红",
vip: true
}]
let newArr = []
const changeArrHandel = () => {
newArr = arr.filter((item) => {
return item.vip
// return item.vip == true
// 两种写法就都可以
})
console.log(newArr)
// 0: { name: '小明', vip: true }
// 1: { name: '小红', vip: true }
}
changeArrHandel()
类似地,想要筛选满足什么样条件的元素只需要给定相应的条件就可以了
组合与拆分
concat()
- 作用:将两个数组合并,返回一个新的数组
js
const arr1 = ["a","b","c"]
const arr2 = [1,2,3]
let concatArr = []
const changeArrHandel = () => {
concatArr = arr1.concat(arr2)
console.log(concatArr) //['a', 'b', 'c', 1, 2, 3]
}
changeArrHandel()
slice()
- 作用:将数组按照给定条件切割返回一个区间左闭右开新的数组,参数一
start
,参数二end
,也可以作用于字符串,效果和数组类似
js
const arr = ["a", "b", "c"]
const str = "abcdefg"
let sliceArr = []
let sliceStr = ""
const changeArrHandel = () => {
sliceArr = arr.slice(1,2)
console.log(sliceArr) //[ "b" ]
sliceStr = str.slice(1,5)
console.log(sliceStr) //"bcde"
}
changeArrHandel()
split()
-
作用:虽然不是数组的方法,但与
join()
相对,可以将一个字符串分割成数组 -
参数:split接受两个参数,第一个参数是分隔符
separator
,第二个参数是分割上限limit
limit
可以理解为返回数组的最大长度,如果切割之后满足的元素数目超过了 limit
就只会返回由前 limit
个元素构成的数组或字符串了
只传分隔符
js
let arr = []
let str = "2024-01-02"
const changeArrHandel = () => {
arr = str.split("-")
console.log(arr) //["2024","01","02"]
}
changeArrHandel()
传分割上限
js
let arr = []
let str = "2024-01-02"
const changeArrHandel = () => {
arr = str.split("-",2)
console.log(arr) //["2024","01"] 这里的日就丢失了
}
changeArrHandel()
join()
将数组按照给定的分隔符拼接,得到字符串
- 参数:只有一个分隔符参数
separator
js
const arr = ["2024", "01", "02"]
let str = ""
const changeArrHandel = () => {
str = arr.join("-")
console.log(str) //2024-01-02
}
changeArrHandel()
示例
假如后端传过来一个时间
值为2024/04/17 09:05:29
我前端只想渲染出它的具体日期(2024-04-17)并以-作为分割符,我们可以这样做
js
const arr = []
let str = "2024/04/17 09:05:29"
let time = ""
const changeArrHandel = () => {
time = str.split(" ")[0].split("/").join("-")
console.log(time) //2024-04-17
}
changeArrHandel()
这个不解释了,看懂是完全没问题的
数组排序和反转
sort()
默认排序
- 作用:对数组的元素进行排序,并返回数组。默认排序顺序是按照字符串
Unicode
码点
js
let arr = ["abc","abb","aaa"]
const changeArrHandel = () => {
arr.sort()
console.log(arr) //[ "aaa", "abb", "abc" ]
}
changeArrHandel()
数字排序
要正确排序数字数组,需要通过提供比较函数来指定排序规则
比较函数
比较函数是一个用于排序的函数,它接受两个参数 a 和 b,表示数组中的两个元素。根据比较函数的返回值,sort() 方法决定如何排序这两个元素
如果返回一个小于 0 的值,将 a 排在 b 前面
如果返回一个大于 0 的值,将 b 排在 a 前面
如果返回 0,a 和 b 的相对位置不变
升序排序
js
let numbers = [3, 1, 5, 2, 4]
numbers.sort((a, b) => a - b)
console.log(numbers) //[1, 2, 3, 4, 5]
降序排序
js
let numbers = [3, 1, 5, 2, 4]
numbers.sort((a, b) => b - a)
console.log(numbers) // [5, 4, 3, 2, 1]
reverse()
- 作用:反转数组
js
let arr = ["a","b","c"]
const changeArrHandel = () => {
arr.reverse()
console.log(arr) //[ "c", "b", "a" ]
}
changeArrHandel()
高阶函数
reduce
- 作用:用于从左到右(从数组的第一个元素到最后一个元素)累积数组中的值,最终计算出一个值
- 它接受一个回调函数作为参数,并可以接收一个初始值
函数构造
js
array.reduce(function(accumulator, currentValue, currentIndex, array) {
// 返回累积结果
}, initialValue);
accumulator
:累积器,它累积每次回调函数的返回值,用来存储累积的结果
currentValue
:当前正在处理的数组元素
currentIndex
(可选):当前正在处理的元素在数组中的索引
array
(可选):调用 reduce 方法的数组本身
下面是一个使用 reduce()
方法计算数组总和的示例:
js
let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce((accumulator, current) => {
return accumulator+ current
}, 0)
console.log(sum) // 输出: 15,即 1 + 2 + 3 + 4 + 5
需要注意的是,如果对对象数组进行操作,我们必须要传初始值
接下来我们就来解释一下原因
我们需要知道的是如果没有传初始值,我们只会循环
arr.length - 1
次,传了初始值则会循环arr.length
次
先给个错误示例
js
const arr = [
{
name:"AAA",
money:10
},
{
name:"BBB",
money:20
},
{
name:"CCC",
money:30
},
{
name:"DDD",
money:40
}
];
const arrReduce = () => {
arr.reduce((accumulator,current)=>{
console.log(accumulator.money)
return accumulator.money + current.money
//第一次不会报错 但是第二次之后就报错了
//第一次没有报错的原因就是我们第一次循环的accumulator是数组的第一个元素,是可以使用对象访问操作的
//但是后续循环的accumulator你返回就不是一个对象了
})
}
arrReduce()
我们应该知道的是 accumulator
的类型只有两种可能,一是数字,二是字符串,所以对于 accumulator
我们不能将其作为对象使用 .
去访问
第二个错误示例
js
const arrReduce = () => {
arr.reduce((accumulator, current) => {
console.log(accumulator)
// { name: 'AAA', money: 10 }
// [object Object]20
// [object Object]2030
return accumulator+ current.money
})
}
在没有传初始值的情况下,我们的 accumulator
会默认为数组第一个值,这个情况下即一个对象,我们执行 +
运算,导致结果成了字符串的拼接,结果也不是我们预料的
从上可以发现,第一种情况是访问数据出错,意味着我们不能对 accumulator
去做访问操作,第二种情况是虽然解决了第一种情况下的问题,但是却产生了 accumulator
默认为对象的问题,所以我们的解决方法只能是传一个初始值,进而解决问题二,从而实现我们的需求
js
const arrReduce = () => {
arr.reduce((accumulator, current) => {
console.log(accumulator)
// 10
// 20
// 40
// 70
return accumulator+ current.money
}, arr[0].money)
}
every
every
接收的是一个回调函数,回调函数中有三个参数,元素值item
,索引index
,遍历数组自身self
,但不同的是every
返回的是一个布尔值
每次遍历返回全部为true
结果就为true
,否则为false
js
const arr = [1, 2, 3, 4, 5, 6, 7]
const arrTraverse = () => {
const result = arr.every(item => {
return item > 5
})
console.log(result) // false
}
arrTraverse()
some
于
every
类似,但遍历结果中只要有一个true
最终结果就是true
js
const arr = [1, 2, 3, 4, 5, 6, 7]
const arrTraverse = () => {
const result = arr.every(item => {
return item > 5
})
console.log(result) // true
}
arrTraverse()
数组扁平化和去重
扁平化数组
flat
- 作用:用于将嵌套的数组"拉平",可以通过指定深度来拉平多层嵌套数组。
- 参数:
depth
(可选):指定要提取嵌套数组的结构深度,默认值为1
js
const arr = [1, [2, [3, [4, 5]]]];
const arrFlat = () => {
let flat1 = arr.flat()
console.log(flat1)
// (3)[1, 2, Array(2)]
let flat2 = arr.flat(2)
console.log(flat2)
// (4)[1, 2, 3, Array(2)]
let flat3 = arr.flat(Infinity) //Infinity表示正无穷大
console.log(flat3)
// (5)[1, 2, 3, 4, 5]
}
arrFlat()
flatMap
- 作用:首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它对结果数组执行一次
flat
操作,其深度值为1
- 参数:接受一个回调函数,回调函数中有三个参数,元素值
item
,索引index
,遍历数组自身self
js
const arr = [1, 2, 3, 4]
const arrFlatMap = () => {
let mapped = arr.map(x => [x * 2])
console.log(mapped)
// 输出: (4)[Array(1), Array(1), Array(1), Array(1)]
let flatMapped = arr.flatMap(x => (x % 2 === 0 ? [x, x * 2] : []));
console.log(flatMapped)
// 输出: [2, 4, 4, 8]
}
arrFlatMap ()
从打印中我们能看见明显差距了,flatMap相较于map会多对数组进行一次深度为1的flat操作
数组去重
利用Set
Set
是一种集合数据结构,其中每个值必须是唯一的。可以使用Set
来去重数组,然后将Set
转换回数组
js
const arr = [1, 2, 2, 3, 4, 4, 5]
const arrSet = () => {
let uniqueArr = Array.from(new Set(arr))
console.log(uniqueArr) // [1, 2, 3, 4, 5]
}
arrSet()
Array对象
Array
- 作用:创建指定长度的空数组
- 参数:接受参数
length
js
const arr = Array(3)
console.log(arr) //(3) [空 ×3]
在这个示例中,Array(3)
创建了一个包含三个空位的数组。这里的 empty
表示空位,即数组中没有被初始化的元素。
Array.from
- 作用:用于从类数组对象或可迭代对象创建一个新的数组实例
- 参数:
arrayLike
:想要转换成数组的类数组对象或可迭代对象,mapFn
(可选):映射函数,用于对每个元素进行处理,生成新的数组元素
js
let str = "hello"
let arrFromStr = Array.from(str)
console.log(arrFromStr) // 输出: ['h', 'e', 'l', 'l', 'o']
let arrFromRange = Array.from({ length: 5 }, (_, index) => index + 1)
console.log(arrFromRange) // 输出: [1, 2, 3, 4, 5]
在第一个示例中,Array.from(str)
将字符串 str
转换为一个包含每个字符的数组。
在第二个示例中,Array.from({ length: 5 }, (_, index) => index + 1)
使用了映射函数来创建一个包含数字 1
到 5
的数组
Array.of
- 作用
Array.of()
方法用于创建一个具有可变数量参数的新数组实例,不管参数的数量或类型 - 参数:
...element
要放入数组的元素
js
let arrOf = Array.of(1, 2, 3, 4, 5)
console.log(arrOf) // 输出: [1, 2, 3, 4, 5]
let arrOfSingle = Array.of(3)
console.log(arrOfSingle) // 输出: [3]
在第一个示例中,Array.of(1, 2, 3, 4, 5)
创建了一个包含 1
到 5
的数组
在第二个示例中,Array.of(3)
创建了一个包含一个元素 3
的数组
Array.fill
- 作用
Array.fill()
方法用于重置指定索引范围内的元素值 - 参数:
value
:你希望用来填充数组的值。start
(可选):开始填充的位置索引,默认为 0。end
(可选):结束填充的位置索引(但不包括这个索引),默认为数组的长度。
js
let arrFill = [1,2,3,4,5]
arrFill.fill(666,1,4)
console.log(arrFill) // 输出: [1,666,666,666,5]
let arrOfSingle = Array.of(3)
console.log(arrOfSingle) // 输出: [3]
链式调用
演示一下使用 filter 和 map 的链式调用,其它方法当然也可以
js
const arr = [{
name: "李华",
age: 18,
vip: false
},
{
name: "小明",
age: 19,
vip: true
}, {
name: "小刚",
age: 17,
vip: true
}]
let nerArr = []
const changeArrHandel = () => {
nerArr = arr.filter((item) => {
return item.vip
}).map((item) => {
return {
...item,
age: item.age + "岁",
exception: "额外字段"
}
})
console.log(nerArr)
// 0: { name: '小明', age: '19岁', vip: true, exception: '额外字段' }
// 1: { name: '小刚', age: '17岁', vip: true, exception: '额外字段' }
}
changeArrHandel()
解释: 我们先执行 filer ,过滤出vip为真的用户,得到新数组
然后对这个新数组使用map遍历,首先保留全部字段,修改age字段,在其末尾加上 岁,然后加了一个新字段 exception ,值全部为额外字段
最后将结果赋值给newArr,打印得到结果