JS给数组分了许多类,其中就有数组和函数,今天学习数组,JS根据不同对象拥有不同的__proto__分类
JS数组和典型数组
典型数组
- 元素的数据类型相同
- 通过数字下标获取元素
- 使用连续的内存存储
JS数组
- 元素的数据类型可以不同
- 内存不一定是连续的(对象是随机存储的)
- 不能通过数字下标,而是通过字符串下标
- 数组可以有任何key
JS创建数组
- 正规写法:let 数组名 = new Array(1,2,3)//元素为1,2,3
- 正规写法:let 数组名 = new Array(3)//长度为3
- 简便写法:let 数组名 = [1,2,3]
用字符串创建数组
- str.split(',') //用什么来分隔数组
- str.split('') 空字符串不是空格字符串
Array.from()
- 可以把不是数组的东西变为数组,比如字符串
- 比如把一个对象《有0、1、2这样的下标而且有length属性》变成数组,注意由length决定新数组中元素的个数。
伪数组
- 注意以上提到的传统方式,还是str.split还有Array.from都不是伪数组
- 真数组的第一层原型为Array.prototype,第二层原型为Object.prototype
- 伪数组的原型链中没有数组的原型,只是个对象
- 比如这个对象{0:'yy',1:'18',2:3,length:3}就是伪数组,只是伪数组恰好有属性'length'和属性名'0','1','2'才让他看起来像真数组。
- 比如let divList = document.querySelectorAll('div')是个伪数组
合并数组
- 数组1.concat(数组2)
- 原来的两个数组不会改变,只是得到一个新数组
截取数组
- 数组名.slice(index)从第index下标开始截取后面的元素为一个新数组
- 数组名.slice(0)从第0个下标也就是第一个元素开始截取。意思就是把这整个数组复制下来成为一个新数组。只是浅拷贝
- 原数组不变
增删改查
删
- delete arr[index]只会把元素变为empty,没有对应下标,数组长度不变。(把至少有一个元素为empty的数组成为稀疏数组)
以上为对象的删法不推荐
- 还有一种把length变短来删除元素(不要用!)
- 数组名.shift():该数组的头部元素被删除了,并且返回被删元素
- 数组名.pop():该数组的尾部元素被删除了,并且返回被删元素
- 数组名.splice(index,n,x,y)删除从index下标开始的n个元素,并且在删除位置添加x、y元素(添加几个就写几个,不添加就不写)
查
- arr.
- Object.keys(obj)
- for (let i in arr){console.log(
${i}:${arr[i]})}
以上为对象查法不推荐,可以打出乱七八糟的下标,使用0到length-1可以控制
推荐用法一
for(let i = 0; i < arr.length; i++){
console.log(`${i}: ${arr[i]}`)
}
推荐用法二
- 用数组原型上带有的forEach函数
arr.forEach(function(item,index){
console.log(`${index}: ${item}`)
})
自己写一个forEach
function forEach(array,fn){
for(let i = 0; i < arr.length; i++){
fn(array[i])
}
}
此时调用forEach([1,2,3],function(){console.log()})
forEach接受两个参数[1,2,3]和function(){},一个数组一个函数,每次for循环都会把数组的值传给function(){}做参数
此时调用forEach([1,2,3],function(x){console.log(x)})
区别在于把数组值传给了x,并打印x
function forEach(array,fn){
for(let i = 0; i < arr.length; i++){
fn(array[i],i)
}
}
现在把i也传回去
此时调用forEach([1,2,3],function(x,y){console.log(x,y)})
//给他一个函数一个数组,函数里面带有两个参数,居然这个函数把未知参数打印了出来,说明forEach肯定赋予了两个参数值,然后调用了函数
把数组值给了x,下标给了y
- 我们在调用的时候输入了function(){}相当于创建了一个没有名字的函数,道理和直接打个1是一样的,存在于内存中,没有必要深究
- forEach类似的回调函数的逻辑是先写内部的函数才能确定需要几个参数先写fn(array[i],i),才能知道fn需要几个参数,事先规定好每个参数和返回值在函数内代表什么写入文档中,在内部调用fn,调用产生的返回值一般与该参数有关对返回值进行操作这样才有逻辑
区别
- 当for循环中有continue或break时两者不一样
- for循环可以在某一位置终止
- forEach只能从头遍历到尾
- for循环是块级作用域
- forEach是函数作用域
查看单个属性
- 数组名['index']或者数组名[index](下标不加引号也会自动给你加上引号,属性名都会被变成字符串)
索引越界问题
- 数组索引越界是指arr['index']中的下标index应该取0到arr.length-1,但是如果取得下标不是这个范围,那arr['index']就不存在,这就叫索引越界,这时arr[index]===undefined
查找某个元素是否在数组中
- 数组名.indexOf(item) 如果这个item存在于该数组中则返回下标,否则返回-1
- 或者for循环
使用条件查找元素
- 数组名.find(自写函数)
- 我们要把这个自写函数写成每次返回布尔值。元素就是这个函数的参数
- 然后find函数会遍历数组,遍历每个元素。把元素给自写函数当参数。当有第一个元素在自写函数中返回了true,find函数就会返回这个元素
- 数组名.findIndex(自写函数)
增
- 数组名[index] = item 因为会根据下标来改变长度length 不推荐
arr = [1,2,3]
arr[100] = 101
//length从3变成101,中间会自动补齐下标'3'到'99',而这些下标对应的元素则为empty。这个数组就变成了稀疏数组了
在尾部加元素
- 数组名.push(newitem)
- 数组名.push(newitem1,newitem2)
- 修改了该数组,并且返回新长度
在中间加元素
- 数组名.splice(index,0,'x','100',100)
- 从index下标开始,啥也不删,再加上这个元素00
- 修改了该数组,并且返回新长度
在头部加元素
- 数组名.unshift(newitem)
- 数组名.unshift(newitem1,newitem2)
- 修改了该数组,并且返回新长度
修改元素
- 数组名[index] = item,注意只能修改已存在的下标。不然会改变长度搞不好变成稀疏数组
- 数组名.splice(index,n,'x','100',100)
- 反转顺序,数组名.reverse() 翻转字符串
let s = 'abcde'
s.split('') //["a", "b", "c", "d", "e"] 把字符串分开成数组
s.split('').reverse() //["e", "d", "c", "b", "a"] 把数组反转顺序
s.split('').reverse().join('') //"edcba" 把数组合成字符串
排序
- 我们只管自己写个函数判断数组中元素a、b的大小,然后必须返回1,0,-1,不用管排序。
- sort函数会根据返回的1,0,-1来排列元素
- sort函数默认把元素从小到大排列,但是谁大谁小sort函数不知道,他必须通过我们返回的1,0,-1来判断
- sort函数认为
- 1表示最后算a大,往后排
- -1表示最后算a小,往前排
- 0表示a、b元素一样大
数组转化
- map n变n
arr.map(item=>item*item) - filter n变少
arr.filter(item=>item%2 === 0 ? true : false) - reduce n变1
arr.reduce((sum,item)=>{return sum+item},0)第2个参数是数组值,第一个值赋值为零是最终返回值,可以用reduce,push最后reduce成一个数组
其他
- console.log(`temp的值为: ${temp}`)
- map,reduce,filter方法都会要求一个函数参数,参数函数里都会要求一个参数存放数组的值
- 理解map中数组长度等于调用次数即return次数,其他类推,map函数会自动把return回来的东西装进另一个数组里
疑惑
- 函数有没有作用域
- 函数作用域和块作用域区别