数组对象,区别于其他语言,比如JAVA,JS其实没有真正的数组,只是用对象模拟数组,即内置对象(built-in Object)
JS的数组是非经典数组
典型的数组
- 元素的数据类型相同
- 使用连续的内存存储
- 通过数字下标获取元素
JS的“数组”不同之处
- 元素的数据类型可以不同
- 内存不一定是连续的
- 不能通过数字下标,而是通过字符串下标
- 可以有任何
key
画个内存图
|
|
|
|
每个数组一创建就有
length
属性,根据下标自动更新
创建一个数组
新建
|
|
语法
|
|
elementN
Array 构造器会根据给定的元素创建一个 JavaScript 数组,但是当仅有一个参数且为数字时除外(详见下面的 arrayLength 参数)。注意,后面这种情况仅适用于用 Array 构造器创建数组,而不适用于用方括号创建的数组字面量。
arrayLength
一个范围在 0 到 2^32-1 之间的整数,此时将返回一个 length 的值等于 arrayLength 的数组对象(言外之意就是该数组此时并没有包含任何实际的元素,不能理所当然地认为它包含 arrayLength 个值为 undefined 的元素)。如果传入的参数不是有效值,则会抛出 RangeError 异常。
下标,“键值”
|
|
转化
|
|
将下标隐式转换为字符串
|
|
伪数组(类数组对象)
|
|
|
|
|
|
伪数组有length属性
伪数组的原型链中并没有数组的原型,而是直接指向Object的原型
没有数组共有属性的「数组」,就是伪数组
拿到伪数组第一个要做的就是用
Array.from()
变为真·数组
- 可用
Array.isArray(arr)
判断(ES5)
|
|
创建一个数组(续)
合并两个数组
|
|
不改变原数组,得到(返回)新数组
截取
一个数组的一部分,或全部截取
|
|
JS原生(til 2020)只提供浅拷贝
增删改查数组中的元素
删元素
和对象一样,可用delete
操作符
|
|
有长度属性,但没有对应下标的数组,叫***「稀疏数组」***,只会带来bug
数组长度并未改变
delete
方法是适用对象的,并不适合用在数组上
直接改length
|
|
不随便改
length
不规范的删法
- 删除数组元素不要用
delete
和改变length
的错误方法
需要改length
的场景
删元素(续)
删除头部元素
|
|
删除尾部元素
|
|
删除中间元素
|
|
加参数,就变改掉数组元素
|
|
|
|
查元素
查看所有元素
查看所有属性名
Object.keys(arr)
|
|
用另一种查看普通对象的遍历方法,试试
|
|
访问数组的时候并不希望访问到下标属性之外的
对象的遍历方法用在数组上不靠谱,只适合纯对象,不适合数组
所以要用其他的方法获取数组所有
keys
和values
查看数组的正确姿势
for let i 循环
查看数组属性名和值
区别于
for i in arr
,遍历所有属性,指定具体要遍历的下标
|
|
- 所以访问数组,不要用
for in
和Object.keys()
- 都是访问对象的,有可能获取到非预期的东西
- 访问数组的标准做法就是
for(let i = 0; i < arr.length; i++){}
- 要让
i
从0
增长到length - 1
数组提供的另一种方法,在数组的原型中可以看到
arr.forEach()
查看数组属性名和值
|
|
继续打印[1,2,3,4,5]和对应下标
|
|
forEach
用到了回调,怎么理解,自己手写一个
第一个版本,把每一个数组下标元素传到一个函数里
|
|
- 两种写法几乎等价:数组、forEach、函数三者都有
第二个版本,把每一个数组下标和下标元素传都到一个函数里
|
|
第三个版本,把数组本身也到函数里
|
|
forEach
的原理就是遍历数组,每一次都调用一个函数
- 基本用到前两个参数就够了,参数名无所谓,顺序对就可以了
- 第三个参数在特殊情况下会用到,下节介绍_坑
问for循环遍历下标的方法和
forEach
的区别是什么(有什么第一种可做到,而第二种做不到的一点)几乎通用,,但并不是语法糖的关系,因为以下
区别一:for循环里支持
break
和continue
,而forEach
只是一个函数,不支持区别二:
for
循环的for
是一个关键字,没有函数作用域,只有块级作用域,而forEach
是个函数,有函数作用域
|
|
用for循环遍历,可以随时
break
或continue
而
forEach
按字面意思就是从开头到结尾每个都遍历到也可以用
forEach/map
等原型上的函数
forEach
是个坎
自己写一遍
forEach
才能理解
|
|
forEach
用for
访问array
的每一项- 对每一项调用
fn(array[i],i,array)
- 为什么要传入
array
,龟腚
|
|
arr.every()
和arr.some()
按条件判断
arr.every()
仅当所有回调函数都返回true
的时候,才返回true
,与筛选arr.some()
有任意一个回调函数返回true
的时候,就返回true
,或筛选- 不改变原数组
|
|
查看单个元素
和对象一样
|
|
索引越界
|
|
索引不存在
|
|
举例
|
|
报一个错:
Cannot read property 'toString' of undefined
意思是不可读取undefined
的toString
属性,不是toString
是undefined
x.toString()
,其中x
如果是undefined
,就报这个错用
console.log()
大法解决
|
|
注意数组索引不要越界
查看单个属性(续)
arr.indexOf(item)
查找某个元素是否存在于数组里,并返回该元素的下标
|
|
你可以笨笨地遍历数组
|
|
通常做法
|
|
还有
lastIndexOf()
方法,找数组中最后一个满足条件元素的下标
使用条件查找元素
|
|
find()
方法返回数组中满足条件的第一项的值。否则返回undefined
这个条件就是,提供的一个条件判断的函数
find()
方法不会改变原数组。
|
|
find()
可以返回满足条件的元素(键)的值,但无法返回下标(键)用
findIndex()
使用条件查找元素的索引
|
|
返回下标
|
|
查数组元素小结
- 用
Array.indexOf()
按下标来查找某个元素,如果下标越界,则返回-1
- 用
Array.find()
,在find()
里加条件,来查找符合这个条件(即返回true)的第一个元素,如果没有符合条件的元素,返回undefined
- 用
Array.findIndex()
,在findIndex()
里加条件,来查找符合这个条件(即返回true)的第一个元素对应的索引,如果没有符合条件的元素,返回-1
增元素
用对象的属性做一个错误的示范
|
|
正确的方式有
在尾部增加元素
|
|
|
|
在头部增加元素
|
|
|
|
在中间增加元素
|
|
|
|
数组增删改查小结
改变原来的数组(栈、队列方法)
|
|
数组变换–修改数组中的元素
拿到数组的索引,就可以赋值,即修改数组
下面说几种特别的API
替换元素
|
|
反转顺序
|
|
问:如何把字符串换个方向(排列)
|
|
即用
s.split('').reverse().join('')
,一次搞定
自定义顺序
|
|
默认从小到大排序
|
|
sort()
可以接受一个函数作为参数
|
|
反过来
|
|
JS 默认较小的值在前,大的在后,局限于数字和字符串的比较
比较其他,需指定数值为参数
|
|
快速简写法
|
|
方法用原地算法对数组的元素进行排序,并返回数组。
默认排序顺序是在将元素转换为字符串( ASCII 字符的字符串),然后比较它们的UTF-16代码单元值序列时构建的
由于它取决于具体实现,因此无法保证排序的时间和空间复杂性。
数组变换(续)
按条件处理数组每项的值
|
|
map
语法糖
n
变n
- 举例:把数组每项都做平方处理
|
|
按条件过滤数组
|
|
filter
语法糖
n
变少
- 举例:过滤掉奇数
|
|
按条件合并数组每项值
|
|
reduce
语法糖
- ES5新增的常规数组方法之一
- 定义:对数组中的每个元素执行一个自定义的累计器,将其结果汇总为单个返回值
- 形式:
array.reduce((t, v, i, a) => {}, initValue)
- 参数
callback
:回调函数(必选) - 回调函数的参数 - - total(t):累计器完成计算的返回值(必选) - - value(v):当前元素(必选) - - index(i):当前元素的索引(可选) - - array(a):当前元素所属的数组对象(可选)initValue
:初始值(可选)
- 过程
- 以t作为累计结果的初始值,不设置t则以数组第一个元素为初始值
- 开始遍历,使用累计器处理v,将v的映射结果累计到t上,结束此次循环,返回t
- 进入下一次循环,重复上述操作,直至数组最后一个元素
- 结束遍历,返回最终的t
reduce的精华所在是将累计器逐个作用于数组成员上,把上一次输出的值作为下一次输入的值
|
|
reduce
实质上是一个累计器函数,通过用户自定义的累计器对数组成员进行自定义累计,得出一个由累计器生成的值。另外reduce还有一个胞弟
reduceRight
,两个方法的功能其实是一样的,只不过reduce
是升序执行,reduceRight
是降序执行。对空数组调用
reduce()
和reduceRight()
是不会执行其回调函数的,可认为reduce()
对空数组无效
基本用法
n
变1
- 举例:求和
|
|
抢钱的比喻
|
|
合成“一个”,不一定只有一个
- 举例:用
reduce
实现把数组每项都做平方处理
|
|
有了
reduce()
就不需要map
了,map
就是一个语法糖而已
- 举例:用
reduce()
配合concat()
实现过滤掉数组中的奇数
|
|
arr.reduce((val1,val2)=>{...})
arr.reduce((result,item)=>{...},initValue)
- 遍历数组,调用回调函数,将数组元素合成一个值
- 初始值可选
reduce()
高级用法总结
25种场景来应用reduce的高级用法
累加累乘
权重求和
代替reverse
代替map和filter
数组过滤
代替some和every
数组分割,部分代替splice
数组填充
数组扁平
数组去重
数组最大最小值
数组成员独立拆解
数组成员个数统计
数组成员位置记录
数组成员特性分组
数组成员所含关键字统计
字符串翻转
数字千分化
异步累计
斐波那契数列
练手题
把数字变成星期
|
|
- 或者定义一个映射表,这是
map
的本意用法
|
|
找出所有大于60分的成绩
|
|
算出所有奇数之和
|
|
面试题
数据变换
|
|
数组变成对象,其中两项变成第一项的属性
|
|
分析
n
变1
,用reduce
初始 空对象
改变结构 子属性
|
|
参考文章
相关文章
- 无
- 作者: Joel
- 文章链接:
- 版权声明
- 非自由转载-非商用-非衍生-保持署名
- 河 掘 思 知 简