六、数组
1.认识数组
- 数组 是一种数据类型, 他也是属于 引用数据类型(复杂数据类型)
- 根据字面意思来说, 存放数字的一个组合, 但这样说有点片面了
- 更完善的说法: 数组是存放一些数据的集合
- 换句话说: 我们把数据放在一个盒子中, 这个盒子就叫做数组, 注意! 数组内的数据是有顺序的
2.创建数组
-
分两种方式
-
字面量的方式
- 语法:
var arr = [1, 2, 3, 'q', 'w', 'e']
- 语法:
-
内置构造函数的方式
- 语法1:
var arr = new Array() 创建一个空数组 - 语法2:
var arr = new Array(5) 创建一个有长度的数组 - 语法3:
var arr = new Array(1, 2, 3) 创建一个有内容的数组
- 语法1:
-
3.数组的 length 属性
- length 翻译过来就是 长度的意思
- 代表 这个数组内, 有多少个成员
- 语法:
数组名.length
4.数组的索引
- 索引 也有人叫做 下标
- 就是指一个数据, 在这个数组内排列在第几个 位置上
- 注意: 在 JS 中, 索引(下标) 是从 0 开始计算的
- 如果想要获取到数组指定位置的值, 可以通过下标来获取
- 语法:
数组名[下标]-> 能够获取到这个数组中对应下标的成员具体的值
5. 遍历数组
- 想办法 拿到 数组的每一个成员
- 想拿到数组的所有成员, 需要先想办法拿到数组的所有下标
- 规律: 所有数组的下标都是从
0开始的, 然后到数组.length - 1结束
var arr = ['b', 'a', 'c', 1, 2, 3]
// 需求, 就是根据 arr 这个数组, 拿到他的所有下标
for (var i = 0; i < arr.length; i++) {
// console.log(i) // 0 1 2 3 4 5
// 需求: 拿到数组所有的值, 输出在控制台
console.log(arr[i])
}
复制代码
6. 冒泡排序
-
属于数组排序的算法之一
- 其实就是通过一种算法, 将 一个乱序的数组, 调整为指定顺序的数组(从大到小/从小到大)
-
什么是算法?
- 解决某一个问题最简单的方式 / 最高效的方式
//准备一个乱序数组
var arr = [9, 3, 6, 2, 4, 1, 8, 5, 7]
console.log('原始数组: ', arr)
// 冒泡排序的核心: 对比数组前一项和后一项, 如果前一项的值较大, 那么就往后挪 (这个排序之后是按照从小到大的顺序)
for (var i = 0; i < arr.length; i++) {
if (arr[i] > arr[i + 1]) {
var temp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = temp
}
}
console.log('1 轮冒泡排序后的数组: ', arr)
复制代码
//基础版
var arr = [9, 3, 6, 2, 4, 1, 8, 5, 7]
for (var k = 0; k < arr.length; k++) {
//console.log("这是第", k + 1, '循环')
for (var i = 0; i < arr.length; i++) {
//console.log(arr[i], arr[i + 1])
if (arr[i] > arr[i + 1]) {
var temp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = temp
}
}
}
console.log('基础版冒泡排序后的数组: ', arr)
//优化版
var arr = [9, 3, 6, 2, 4, 1, 8, 5, 7]
for (var k = 0; k < arr.length - 1; k++) {
//console.log("这是第", k + 1, '循环')
for (var i = 0; i < arr.length - 1 - k; i++) {
//console.log(arr[i], arr[i + 1])
if (arr[i] > arr[i + 1]) {
var temp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = temp
}
}
}
console.log('优化版冒泡排序后的数组: ', arr)
//另一个版本
var arr = [9, 3, 6, 2, 4, 1, 8, 5, 7]
for (var k = arr.length; k >= 0; k--) {
for (var i = 0; i <=k; i++) {
if (arr[i] > arr[i + 1]) {
var temp = arr[i]
arr[i] = arr[i + 1]
arr[i + 1] = temp
}
}
}
console.log('另一个版本冒泡排序后的数组: ', arr)
复制代码
7.选择排序
var arr = [9, 8, 7, 6, 5, 4, 3, 2, 1]
// 0 1 2 3 4 5 6 7 8
// 第几次循环 假设谁是最小值 和谁交换 内层循环从几开始
//k === 0 1 0 0 1
//k === 1 2 1 1 2
//k === 2 3 2 2 3
for (var k = 0; k < arr.length; k++) {
var minIndex = k
for (var i = k + 1; i < arr.length; i++) {
if (arr[minIndex] > arr[i]) {
minIndex = i
}
}
var temp = arr[k]
arr[k] = arr[minIndex]
arr[minIndex] = temp
}
console.log(arr)
复制代码
8.数据类型之间的区别
-
数据类型分为两种
- 基本数据类型(简单数据类型)
- 引用数据类型(复杂数据类型)
- 存储
-
变量的数据存储的地方是 内存中, 内存分为两个
- 栈内存
- 堆内存
-
基本数据类型存储在 栈内存中, 比如: string number undefined null boolean
-
复杂数据类型, 将数据本体存放在堆内存中, 比如对象或者数组或者函数,然后将指向该内存的地址, 存放在数组名或者对象名或者函数名中
-
数组/对象/函数 名 存放在 栈内存中
-
面试官: 数据类型之间有什么区别?
- 基本数据类型有哪些, 然后他们存储的地方是 栈内存中
- 引用数据类型有哪些, 然后他们数据本体存放的地方是 堆内存中, 然后变量名存储的位置是 栈内存中
- 基本数据类型内部存储的是值; 引用数据类型内部存储的是地址
2. 赋值
-
基本数据类型: 赋值以后, 两个变量之间没有任何关系, 相当于将我自己的某一个东西, 复制一份给你, 然后你的就是你的, 我的就是我的
- 例子: 我有一张考试卷, 然后我复制一份给你, 然后你在卷子上书写答案, 并不会影响到我自己原本的这张卷子
-
复杂数据类型: 因为变量内部存储的是指向堆内存的地址, 所以在赋值的时候, 其实是将 这个地址给到了另外一个变量
- 那么相当于这两个变量存储的是 同一个 钥匙, 所以操作其中一个变量的时候, 会影响另外一个变量
- 例子: 我房间有一个开门的钥匙, 我将我的钥匙复制一份, 给到你, 那么此时我们两个共同拥有了一个房间的钥匙
- 此时如果我对房间的布局做了修改, 那么你进入房间的时候你能看到布局的修改
- 此时如果你将房间的所有东西全都偷走, 那么我进入房间的时候 能看到房间所有东西都被偷走了
var obj1 = {
name: 'QF001',
age: 18
}
// 这一步相当于将 变量 obj1 内部存储的 "钥匙", 给到了 变量 obj2, 那么此时 obj2 和 obj1 相当于操作的是一个内存空间
var obj2 = obj1
// console.log(obj2) // {name: 'QF001', age: 18}
obj2.name = 'QF666'
console.log(obj2) // {name: 'QF666', age: 18}
console.log(obj1) // {name: 'QF666', age: 18}
复制代码
var obj1 = {
name: 'QF001',
age: 18
}
var obj2 = {
name: 'QF001',
age: 18
}
obj2.name = 'QF666'
console.log(obj1.name) // QF001
/**
* 1. 创建一个对象 obj1 内部存储的地址假设为 XF001
* 2. 创建一个对象 obj2 内部存储的地址假设为 XF002
*
* 注意此时两个对象除了长得一模一样之外 毫无关系
*
* 也就是说 操作 其中一个对象, 并不会影响另外一个
*/
复制代码
- 比较
- 基本数据类型: 就是 值 的比较
- 引用数据类型: 比较的时候 比较的是 存储地址
var obj1 = {
name: 'QF001',
age: 18
}
var obj2 = {
name: 'QF001',
age: 18
}
console.log(obj1 === obj2) // false
/**
* 引用数据类型在对比的时候, 对比的是 地址
* 而这两个对象的地址完全不同, 所以返回的结果就是 false
*/
// var num1 = 100
// var num2 = '100'
// console.log(num1 === num2) // false
// var arr1 = [1, 2, 3]
// var arr2 = arr1
// console.log(arr1 === arr2) // true
/**
* 引用数据类型在对比的时候, 对比的是 地址 因为 他们两个的地址完全相同, 所以返回的结果是 true
*/
复制代码
- 传参
- 基本数据类型: 将值拷贝一份传递给形参, 在函数内修改不会影响外界
- 引用数据类型: 将存储地址赋值给形参, 在函数内修改会影响外界
9.数组的常用方法
- push
- 语法: 数组.push(数据)
- 作用: 向数组末尾添加数据
- 返回值: 追加数据后, 数组最新的长度(length)
- pop
- 语法: 数组.pop()
- 作用: 删除数组最后一条数据
- 返回值: 被删除的数据
- unshift
- 语法: 数组.unshift(数据)
- 作用: 向数组开头添加数据
- 返回值: 添加数据后, 数组最新的长度(length)
- shift
- 语法: 数组.shift()
- 作用: 删除数组第一条数据
- 返回值: 被删除的数据
- reverse
- 语法: 数组.reverse()
- 作用: 反转数组
- 返回值: 反转后的数组
- sort
-
语法1: 数组.sort()
-
作用: 会将数据转换为 字符串后, 一位一位的对比
-
语法2: 数组.sort(function (a, b) {return a - b})
- 作用: 会按照数字大小升序排列
-
语法3: 数组.sort(function (a, b) {return b - a})
- 作用: 会按照数字大小降序排列
- 返回值: 排序后的数组
- splice
-
语法1: 数组.splice(开始索引, 多少个)
- 作用: 截取数组部分内容
-
语法2: 数组.splice(开始索引, 多少个, 插入的数据1, 插入的数据2, 插入的数据3...)
- 作用: 截取数组部分内容, 并插入新的数据
- 返回值: 截取出来的部分内容 组成的 数组
- 数组的方法 能够改变原数组的 就只有上边说的 7 个
- slice
-
语法: 数组.slice(开始索引, 结束索引)
-
参数:
- 包前不包后: 包含开始索引位置的数据, 不包含结束索引位置的数据
- 不写开始索引, 默认是0; 不写 结束索引, 默认是 数组的length
- 参数支持写负数, 表示倒数第几个, 其实就是 length + 负数
-
作用: 截取数组部分内容
-
返回值: 截取出来的部分内容组成的新数组
-
- 面试题: 数组中有两个方法, splice 与 slice, 你能描述一下他们两个的区别吗?
-
参数含义不同, 然后介绍一下 参数哪里不同 复制代码 -
splice 会改变原数组, 而 slice 不会 复制代码
var arr = [1, 2, 3]
console.log('原始数组: ', arr)
// 1. push
var len = arr.push(500)
console.log(len) // 4
console.log(arr) // [1, 2, 3, 500]
// 2. pop
var po = arr.pop()
console.log(po) // 3
console.log(arr) // [1, 2]
// 3. unshift
var len = arr.unshift(666)
console.log(len) // 4
console.log(arr) // [666, 1, 2, 3]
// 4. shift
var st = arr.shift()
console.log(st) // 1
console.log(arr) // [2, 3]
// 5. reverse
var newArr = arr.reverse()
console.log('newArr: ', newArr)
console.log('arr: ', arr)
// 6. sort
var arr = [100, 101, 200, 10, '999', 'qwe', '123abc']
console.log('原始数组: ', arr)
var newArr = arr.sort()
console.log('newArr', newArr)
console.log('arr', arr)
var newArr = arr.sort(function (a, b) {return a - b})
console.log('newArr', newArr)
console.log('arr', arr)
var newArr = arr.sort(function (a, b) { return b - a })
console.log('newArr', newArr)
console.log('arr', arr)
// 7. splice
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
// 0 1 2 3 4 5 6 7 8
console.log('原始数组: ', arr)
// 7.1
var newArr = arr.splice(3, 4) // 从 下标3 开始截取, 截取 4 个 成员/数据
console.log('newArr', newArr) // [4, 5, 6, 7]
console.log('arr', arr) // [1, 2, 3, 8, 9]
// 7.2
var newArr = arr.splice(2, 3, '数据1', '数据2', '数据3', '数据4', '数据5') // 不管插入多少个数据, 都是从下标2开始的
console.log('newArr', newArr)
console.log('arr', arr)
// 8. slice
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
// 0 1 2 3 4 5 6 7 8
console.log('原始数组: ', arr)
var newArr = arr.slice(3, 4) // 从 下标3 开始截取, 截取到 下标4
console.log('newArr', newArr) // [4]
console.log('arr', arr)
var newArr = arr.slice(5) // 不写 结束索引, 相当于 写了数组.length, 所以这里相当于写了 slice(5, arr.length)
console.log(newArr) // [6, 7, 8, 9]
/**
* 此时 开始索引与结束索引都没有填写, 那么 开始索引相当于是写了一个 0, 结束索引相当于写了 数组.length
*
* arr.slice() 相当于写了 arr.slice(0, arr.length)
*/
var newArr = arr.slice()
console.log(newArr)
var newArr = arr.slice(3, -2)
console.log(newArr) // [4, 5, 6, 7]
/**
* arr.slice(3, -2)
* slice 这个方法 允许写 负数
*
* 写负数的时候 就相当于写了 length + 负数
*
* arr.slice(3, -2) -> arr.slice(3, arr.length + (-2)) -> arr.slice(3, arr.length - 2)
* -> arr.slice(3, 9 - 2)
* -> arr.slice(3, 7)
*/
复制代码
10.数组的常用方法2
- concat
-
语法: 原始数组.concat(数组1, 数组2, ...., 数据1, 数据2, ....)
- 作用: 进行数据拼接, 把数组...数据之类的小括号里的内容, 拼接在原始数组中
- 返回值: 拼接好的数组
- join
-
语法: 数组.join('连接符')
- 作用: 使用 "连接符", 把数组内的每一个数据连接成一个字符串 (不写连接符, 默认使用的是 逗号)
- 返回值: 连接好的字符串
- indexOf
-
语法1: 数组.indexOf(要检查的数据)
- 作用: 从前到后(从左到右) 检查该数据第一次在该数组内出现 索引
-
语法2: 数组.indexOf(要检查的数据, 开始索引)
-
作用: 在开始索引的位置, 按照从左到右的顺序, 检查该数据第一次在该数组内出现的 索引
-
返回值: 找到数据的情况下, 会将该数据第一次出现的下标(索引)返回
- 没找到的情况下, 会直接返回一个 -1
- 备注: 开始索引不写的时候 默认是0
-
- lastIndexOf
-
语法1: 数组.lastIndexOf(要检查的数据)
- 作用: 从后向前(从右向左), 检查该数据第一次在该数组内出现的 索引
-
语法2: 数组.lastIndexOf(要检查的数据, 开始索引)
-
作用: 在开始索引的位置, 按照从右向左的顺序, 检查该数据第一次在该数组内出现的 索引
-
返回值: 找到数据的情况下, 返回第一次出现的下标(索引)
- 没找到的情况下, 直接返回一个 -1
-
var arr = [1, 1, 2, 2, 3, 3, 0, 4, 0]
// 0 1 2 3 4 5 6 7 8
console.log('原始数组: ', arr)
复制代码
// 9. concat
var newArr = arr.concat([4, 5, 6], [10, 20], ['a', 'b', 'c'], 'qwer')
console.log('newArr', newArr) // [1, 2, 3, 4, 5, 6, 10, 20, 'a', 'b', 'c', 'qwer']
console.log('arr', arr)
//10. join
var newArr = arr.join() // 不传递连接符, 默认使用的是 逗号连接
var newArr = arr.join('!') // 使用 ! 将数组内的所有数据拼接成一个 字符串
// console.log(newArr)
// 11. indexOf
var num = arr.indexOf(100) // 此时要检查的数据是 数字100, 但是数组中并没有出现过 数字 100, 所以返回值应该是 -1
var num = arr.indexOf(0) // 此时要检查的数据是 数字0, 数字0按照从左到右的顺序, 第一次出现的下标为 6, 所以返回值应该是 6
var num = arr.indexOf(1) // 此时要检查的数据是 数字1, 数字1按照从左到右的顺序, 第一次出现的下标为 0, 所以返回值应该是 0
var num = arr.indexOf(1, 3) // 此时要检查的数据是 数字1, 但是是从下标3的位置开始按照从左往右的顺序查找, 因为后续并没有数字1, 所以此处应该返回 -1
console.log(num)
// 12. lastIndexOf
var num = arr.lastIndexOf(3) // 此时按照从右向左的顺序查找, 发现第一次出现的位置是 下标 5 的位置
var num = arr.lastIndexOf(3, 2) // 此时在下标2的位置按照从右向左的顺序查找, 但是此时在数组中后续的位置并没有出现数字3, 所以按照规则, 应该返回 -1
console.log(num)
复制代码
11.数组遍历的常用方法
- forEach
-
语法: 数组.forEach(function (item, index, origin) {})
- item: 数组的每一项 的值
- index: 数组的每一项 对应的下标
- origin: 原始数组 (了解即可, 一般没人用)
-
作用: 遍历数组
-
返回值: 该方法永远没有返回值 (undefined)
- map
- 语法: 数组.map(function (item, index, origin) {}) 三个参数的意义与 forEach 相同
- 作用: 映射数组
- 返回值: 返回一个和原数组长度相同的数组, 但是内部数据可以经过我们的映射加工
- 映射加工: 就是在函数内 以 return 的形式书写
有一道面试题: 数组常用的遍历方法中, 有一个forEach 和 一个 map, 这两个方法有什么区别?
-
- forEach 的作用是用来遍历数组, 而 map 的作用是用来映射数组
-
- forEach 没有返回值, 而 map 是可以有返回值的
- filter
- 语法: 数组.filter(function (item, index, origin) {}) 三个参数的意义与 forEach 相同
- 作用: 过滤数组
- 返回值: 返回一个新数组, 内部存储的是原始数组过滤出来的部分内容
- 过滤条件: 过滤条件以 return 的形式书写
- find
-
语法: 数组.find(function (item, index, origin) {}) 三个参数的意义 与 forEach 相同
-
作用: 在数组内查找满足条件的第一项
-
返回值: 找到的数据, 如果没找到返回的是 undefined
- 查找条件以 return 的形式书写
- findIndex
- 语法: 数组.findIndex(function (item, index, origin) {}) 三个参数的意义 与 forEach 相同
- 作用: 在数组内查找满足条件的第一项 的下标
- 返回值: 找到的数据 的下标, 如果没找到返回的是 -1
- 查找条件以 return 的形式书写
- some
- 语法: 数组.some(function (item, index, origin) {}) 三个参数的意义 与 forEach 相同
- 作用: 判断数组内是否有一个满足条件
- 返回值: 一个布尔值 true/false
- 判断条件以 return 的形式书写
- every
-
语法: 数组.every(function (item, index, origin) {}) 三个参数的意义 与 forEach 相同
-
作用: 判断数组内是否全都满足条件
-
返回值: 一个布尔值 true/false
- 判断条件以 return 的形式书写
- reduce
-
语法: 数组.reduce(function (prev, item, index, origin) {}, init)
- prev: 表示初始值或者上一次的运算结果
- item: 表示数组的每一项 的值
- index: 表示数组的每一项 的下标(索引)
- origin: 原始数组
-
作用: 用来实现叠加效果
-
返回值: 最终叠加的结果
-
注意:
- 叠加条件以 return 的形式书写
- prev 第一次的值, 如果你传递了 init, 就是 init 的值, 如果没有传递 init, 那么就是 数组[0] 的值
- 如果传递了 init, 循环执行 数组.length 次, 如果没有传递 init, 循环执行 数组.length - 1 次
var arr = [100, 200, 300, 400, 500, 600]
console.log('原始数组: ', arr)
//1. forEach
arr.forEach(function (item, index, origin) {
console.log(item, index)
console.log(origin)
})
//2. map
var newArr = arr.map(function (item, index, origin) {
// console.log(item, index, origin)
return item * 2
})
var newArr = arr.map(function (item) {
return item * 2
})
console.log('映射出来的数组: ', newArr)
//3. filter
var newArr = arr.filter(function (item, index, origin) {
return item > 350 // 过滤数组的内容, 只留下 item 大于 350 的成员
})
console.log(newArr)
//4. find
var newArr1 = arr.find(function (item, index, origin) {
return item > 3500 // 在数组中查找第一个符合条件的成员
})
console.log(newArr1)
//5. findIndex
var newArr2 = arr.findIndex(function (item, index, origin) {
return item > 3500
})
console.log(newArr2)
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log('原始数组: ', arr)
//6. some
var bo1 = arr.some(function (item, index, origin) {
// return item % 2 === 0 // 判断数组内是否有 一个 满足条件的
return item > 500 // 判断数组内是否有 一个 满足条件的
})
console.log(bo1) // true / false
//7. every
var bo2 = arr.every(function (item, index, origin) {
// return item % 2 === 0 // 判断数组内 是否 全都 满足条件
return item > 0 // 判断数组内 是否 全都 满足条件
})
console.log(bo2)
var arr = [1, 2, 3, 4]
console.log('原始数组: ', arr)
// 8. reduce
var str = arr.reduce(function (prev, item, index, origin) {
return prev + item
}, 0)
console.log(str) // 10
复制代码
12.数组塌陷
13.数学方法
在 JS 中 Math 对象给我们提供了操作数据的一些方法(数学的方法)
- random
- 语法: Math.random()
- 作用: 得到一个随机数, 每次生成的数字都不一样, 但一定是0~1之间的, 包含0, 不包含1, 也就是说最大值可能是 0.99999....
- round
- 语法: Math.round(数字)
- 作用: 将这个数字(小数), 按照四舍五入的形式变成整数
- ceil
- 语法: Math.ceil(数字)
- 作用: 将这个数字(小数) 向上取整
- floor
- 语法: Math.floor(数字)
- 作用: 将这个数字(小数) 向下取整
- abs
- 语法: Math.abs(数字)
- 作用: 返回这个数字的绝对值
- sqrt
- 语法: Math.sqrt(数字)
- 作用: 求 平方根、
- pow
- 语法: Math.pow(基数, 幂)
- 作用: 返回基数的几次幂
- max
- 语法: Math.max(数字1, 数字2, 数字3...)
- 作用: 返回传入的数字中 最大的哪一个
- min
- 语法: Math.min(数字1, 数字2, 数字3...)
- 作用: 返回传入的数字中 最小的哪一个
- PI
- 语法: Math.PI
- 作用: 返回 π
14.JS的严格模式
- JS 是一个相对不是很严谨的语言, 在开发的时候一些代码也不是很严格
- 换句话说严格模式就是对开发的时候, 你写的代码做了一些要求
- 严格模式的规则
- 声明变量必须要 var 关键字
- 函数的形参不可以重复
- JS 中默认是没有开启严格模式, 如果想要开启严格模式, 需要手动在代码最开始的位置(script标签内第一行), 写一个字符串 'use strict'
- 现在的公司的项目中, 基本都是按照严格模式开发的
15.字符集(了解)
-
计算机只能存储二进制数据 0101010
-
我们的 大写字母 小写字母 符号之类的内容 都是由 二进制数字组成
-
或者说我们在敲一个字符的时候, 都有一个对应的编号, 计算机存储的时候存储的是这些编号,
-
只不过我们看到的时候, 是通过这些编号解析成我们看到的内容
-
前身: ASCII as key (128) 只够 美国人用英语的使用
-
国内 推出了一个属于中国的 GBK 国标码 前128位 ASCII码, 后边从129位开始就是汉字
-
unicode (万国码)
- 前 128 位还是 ASCII码, 后边开始是各个国家的文字码
-
- 八位十六进制编码 容量小, 但是占用内存也小 UTF-8
-
十六位的十六进制编码 容量大, 但是占用内存也大