基本语法
声明数组:
let arr = ['第一','第二','第三']
//or
let arr = new Array('第一','第二','第三')
第一个就可以了。
数组从0开始,通过下标指定想要获取数组里面的值:
arr[0] //'第一'
arr[1] //'第二'
arr[2] //'第三'
可通过下标指定的值,替换内容,如:
arr[0] = "修改了第一的内容"
console.log(arr) // ["修改了第一的内容",'第二','第三']
也可以自己在数组后面手动添加内容,例如:
arr[3] = "自己添加了第四"
console.log(arr) // ["修改了第一的内容",'第二','第三',"自己添加了第四"]
当然,数组有 length
得出数组的值:
console.log(arr.length) // 4
数组的内容可以是任何类型:
let arr = ['第一', {name: 'John'}, true, function(){consoel.log('hellow')}]
consoel.log(arr[1].name) //"John"
arr[3]() //'hellow'
数组方法
pop/push
pop方法是把数组末端最后的元素取出来并返回,如:
let arr = ['第一','第二','第三']
arr.pop() // '第三'
console.log(arr) // ['第一','第二']
push与pop相反,在数组末端添加一新元素:
let arr = ['第一','第二','第三']
arr.pop("添加第四")
console.log(arr) // ['第一','第二','第三',"添加第四"]
shift/unshift
与上面的类似,只不过专门针对数组首端
shift方法,把数组的第一个元素取出来并返回, 并且后面的元素就会往前排,例如原本下标为1的元素就变成下标为0 :
let arr = ['第一','第二','第三']
console.log(arr[0]) // '第一'
arr.shift() // '第一'
console.log(arr) // ['第二','第三']
console.log(arr[0]) // '第二'
unshift方法,类似的,在首端前面新增一个元素,后面的元素就需要往后排:
let arr = ['第一','第二','第三']
console.log(arr[0]) // '第一'
arr.unshift('新增一个第一') // '第一'
console.log(arr) // ['新增一个第一','第一','第二','第三']
console.log(arr[1]) // '第一'
上面四种方法,允许开发者在一个数组的前后两端进行新增、删除操作,就类似于栈,遵循“先进后出”原则,在计算机科学里,这样的数据结构称之为“双端队列”。
性能
以上四种方法是有性能区别的, push
和 pop
性能比 shift
和 unshift
快。元素越多,该性能区别越明显。
因为shift
和 unshift
对数组的前端操作,每操作一次,后面的数组的下标就要变化一次,元素越多,所花的时间就越多。
数组的特殊之处
在JS中,只有8种原始类型,分别是:Number,String、Boolean、Bight、null、undefined、symbol、object。并没有数组类型,也就是说,JS的数组是一个特殊的对象。即object类型!
也就是说,数组可以像对象一样使用,甚至还可以如下:
let arr = ['第一','第二','第三']
arr[9999] = 'a' //分配索引值远大于原数组的长度的属性,中间什么都没有
arr[8888] = 'b' //倒序填充属性
arr.name = '小名' //像对象一样给数组一个属性
以上就不要用了,像正常的数组对待即可。
遍历数组
最原始的遍历就是直接使用 for
语句:
let arr = [1,2,3]
for (let i = 0; i < arr.length; i++){
console.log(arr[i])
}
还有一种遍历方法,使用 for...fo
:
let arr = [1,2,3]
for(let arr of arrs){
console.log(arr)
}
因为数组也是对象,所以可以使用 for...in
:
let arrs = [1,2,3]
for(let key in arrs){
console.log(arrs[key])
}
但是该方法存在一定的危险,for...in
语句会遍历所有的属性,因为数组是对象,该语句会遍历length和索引属性。所以在数组建议不使用for...in
语句,而是使用for...fo
,普通对象就可以使用for...in
。
数组方法进阶
除了以上提到的4个方法,还有其他的方法。
splice
多功能瑞士刀,功能兼具以上方法,也就是可以增加、删除、插入。语法:
let arrs = [1,2,3]
arrs.splice(state[, deleteCount, elem1,...,elemN])
start
指的是arr的下标,往后开始修改。 deleteCount
,删除N个元素并插入 elem1,...,elemN
,然后返回已经被删除的元素。例如:
arrs.splice(1,1,'小明') //返回已经删除的[2]
console.log(arrs) //[1,'小明',3]
//or
let arr = arrs.splice(1,1,'小明','小红')
console.log(arr) //[2]
console.log(arrs) //[1,'小明','小红',3]
slice
复制数组到一个新的数组。
语法:
arr.slice(start, end)
返回一个数组,将从start到end(不包括end)的数组复制到一个新的数组。如:
let arrs = [1,2,3]
let arr1 = arrs.slice(0,2)
console.log(arr1) //[1,2]
//也可以
let arr1 = arrs.slice() //全部复制
concat
创建一个新数组,其中包括来自其他数组和其他项的值。
语法:
arr.concat(arg1, arg2, ...,argN)
如:
let arrs = [1,2,3]
let arrs1 = ["小明","小红"]
let arrx = arrs.concat(arrs1, [3,4,5,6,7,],33,44,55)
console.log(arrx) //[1, 2, 3, "小明", "小红", 3, 4, 5, 6, 7, 33, 44, 55]
forEach
为数组里面的每个元素都运行一个函数:
如:
["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => {
console.log(`${item} is at index ${index} in ${array}`)
});
数组搜索
在数组中搜索想要的元素,有几种方法。
indexOf、lastIndexOf、includes
indexOf(item,from)
从索引from
开始搜索item
,找到返回索引,没有返回-1。lastIndexOf(item, from)
作用同上,只是从右往左的去查找。includes(item, from)
作用一样,但查找的时候如果有,返回true,反之返回false
例如:
let arr = [1, 0, false]
console.log(arr.index(0)) //1
console.log(arr.index(false)) //2
console.log(arr.index(null)) //-1
console.log(arr.includes(1)) //true
注意,includes
可以比较精准的查找到NaN:
const arr = [1,2,3,NaN,5]
console.log(arr.indexOf(NaN)) //-1
console.log(arr.includes(NaN)) // 3
find 与 findIndex
假如有个对象数组,我要怎么查找某个对象里面的元素呢?这里就用到了find。语法如下:
arr.find(function(item, index, array){
//如果找到,就会返回true,并停止迭代且返回item
//如果没有,则返回undefined
})
该方法依次对数组中的每个元素调用该函数:
item
是元素index
是数组的索引array
是数组本身
例如:
let users = [
{id: 1, name: '小明'},
{id: 2, name: '小红'}
]
let user = users.find(item => item.id === 1)
console.log(user.name) //'小明'
而 findIndex
和 find
是一样的语法,但是如果找到元素,就会返回元素的索引,而不是元素的本身,反之返回-1。
filter
在迭代对象数组时,如果搜索条件为ture的对象,就返回并继续迭代。语法与 find
是一致的,如:
let users = [
{id: 1, name: "小明"},
{id: 2, name: "小红"},
{id: 3, name: "小黄"}
];
let user = users.filter(item => item.id > 1)
console.log(user[0].name) //"小红"
转换数组
map
对数组的每个元素都调用函数,并返回结果数组。语法与 find
一样:
let result = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length)
console.log(result) //[5,7,6]
sort
该方法对数组进行原位(即对原数组进行修改)排序,更改数组的顺序,并返回排序后的数组:
let arr = [1,2,15]
arr.sort() //如果不传参,就会按照字符串的字典排序,结果如下
console.log(arr) //[1,15,2]
传参后:
function compareNumeric(a, b){ //注意,sort这个方法是两两比较的,所以这里也是两个参数
if(a > b) return 1;
if (a == b) return 0;
if (a < b) return -1;
}
let arr = [1,2,15]
arr.sort(compareNumeric)
console.log(arr) //[1, 2, 15]
以上的代码可以简化:
let arr = [1,2,15]
arr.sort((a,b) => a - b)
console.log(arr) //[1, 2, 15]
reverse
该方法可用于颠倒数组中的元素的顺序。例如:
let arr = [1,2,3,4,5,6,7,8]
arr.reverse()
console.log(arr) // [8, 7, 6, 5, 4, 3, 2, 1]
split 和 join
split
是用于分隔一个字符串,返回数组,如:
let str = 'Bilbo, Gandalf, Nazgul';
let arr = str.split(",")
console.log(arr) //['Bilbo', 'Gandalf', 'Nazgul']
该方法有个可选项,可以限定长度,如:
let str = 'Bilbo, Gandalf, Nazgul';
let arr = str.split(",", 2)
console.log(arr) //['Bilbo', 'Gandalf']
如果参数为空字符串,会:
let str = "string"
let arr = str.split('')
console.log(arr) //["s", "t", "r", "i", "n", "g"]
而 join
相反,把数组的所有元素连接成一个字符串:
let arr = ["s", "t", "r", "i", "n", "g"]
let str = arr.join('+')
console.log(str) //'s+t+r+i+n+g'
reduce 和 reduceRight
与 forEach
和 map
差不多,也是为数组内的每个元素都执行一次,但是这个用法稍微复杂点。语法如下:
let value = arr.reduce(function(accumulator, item, index, array){
// ...
}, [initial])
参数讲解:
accumulator
:是上一个函数的调用结果,初始化的时候就等于initial
(前提提供了initial
)item
:当前数组的元素。index
:当前数组的元素的下标。array
:数组本身。initial
:初始值,如果不事先设置,则默认把数组的第一个元素当作初始值。
例子:
let result1 = arr.reduce((sum, current, a, b) => {
sum + current;
console.log(`index: ${a}, arr: ${b}`)
}, 0)
结果:
这个运行过程:
- sum(
accumulator
)的初始值为initial
,而current(item
)为遍历的第一个元素,即值为1,所以1+0
结果就为1 。 - 上一次的结果为1 ,所以
sum = 1
,然后current遍历的第2个元素为2,所以1+2
的值为3. - 以此类推。。。。
流程图:
而 reduceRight
如方法名一样,遍历是从右往左,作用一样。
Array.isArray
该方法作用是判断数组是否是数组!
由于数组是基与对象的,所以判断原始类型的 typeof
判断数组的结果只能是对象。如下 :
let arr = []
typeof(arr) //"object"
Array.isArray(arr) //true
关于数组方法里的this
大多数的方法参数最后是有可选参数的,这个可选参数是 thisArg
,例如:
arr.find(func, thisArg);
arr.filter(func, thisArg);
arr.map(func, thisArg);
//...
而 thisArg
的值就是 func
的 this
。例如:
let army = {
minAge: 18,
maxAge: 27,
canJoin(user){
console.log(`minAge:${this.minAge}, maxAge:${this.maxAge}`) //minAge:18, maxAge:17
return user.age >= this.minAge && user.age < this.maxAge
}
}
let users = [
{age: 16},
{age: 19},
{age: 26},
{age: 32}
]
let arr = users.filter(army.canJoin, army)
console.log(arr[0].age) //16
如果不给 thisArg
参数,那么 this = undefined
。
当然,上面的第15行代码可以换成如下更好理解:
let arr = users.filter(user => army.canJoin(user))
总结
数组添加删除操作:
pop
—— 末端最后一个元素被取出并返回push
—— 末端添加一个新的元素shift
—— 首端第一个元素被取出并返回unshift
—— 首端新添加一个新的元素
数组万能操作:
splice(state[,deleteCount, ..., items)
—— 从state
开始删除deleteCount
个元素,并插入items
(原位in-place: 对,操作自身的,不会创建新的数) 。slice(state, end)
—— 从索引state
开始到索引end
(不包括end
)的元素复制到新的数组。concat(...items)
—— 返回一个新的数组,复制当前数组所有元素,并向其中添加items
。如果items
里也有数组,那么把这个数组里面的元素复制出了。
遍历操作:
forEach(func)
—— 为每个元素执行一次函数,可以修改里面的元素,并不会返回任何内容。
数组搜索:
indexOf(from, item) / lastIndex(from, item)
—— 从from
开始查找数组内是否有想要item
,有返回该元素,无返回-1。find(func)
—— 为每个元素执行一次函数,如果使得func
返回true
,就返回这个的值,并停止迭代;没有就返回undefined
。findIndex(func)
—— 同上,只不过ture
只会返回索引下标而不是值。filter(func)
—— 与find
类似,但如果执行到true
就会返回并继续迭代直到结束。
数组转换:
map(func)
—— 为数组内的每个元素执行一次函数,与forEach
是差不多的,但是这个不会修改数组里面的元素,会返回新的数组。sort(func)
—— 对原来的数组进行排序(原位in-place),然后返回。reverse()
—— 对原来的数组进行反转,然后返回(也是原位in-place)splite()
—— 对字符串进行切割并返回数组。join()
—— 对数组进行拼接成字符串。reduce(func[, initial]) / reduceRight(func[, initial])
—— 对数组里的每个元素执行一次函数,并把函数的结果传递给下一次的执行。
数组判断:
Array.isArray(arr)
判断是否是数组。