数据类型的区别
- 基本数据类型
数字, 字符串, 布尔值, undefined, null
- 引用数据类型
函数, 对象, 数组
- 存储的区别
-
基本数据类型存储在 栈内存中, 比如:
string; number; undefined; null; boolean -
复杂数据类型(引用数据类型)
- 将数据本体存放在 对内存中, 比如对象或者数组, 然后将指向该堆内存的地址, 存放在数组名或者对象名中
- 数组名或者对象名放在栈内存中
-
两种数据类型的区别
- 基础数据类型直接存储在 栈内存中, 变量中存储的是数据
- 复杂数据类型会将数据本体存放在堆内存中, 变量名存储在堆内存中,变量名内部存储着指向堆内存的地址
var arr1 = [1,2,3] //假设地址为QF 001
var arr1 = [1,2,3] //假设地址为QF 002
var arr3 = arr1 //QF001
console.log('arr1: ', arr1) // [1, 2, 3]
console.log('arr2: ', arr2) // [1, 2, 3]
console.log('arr3: ', arr3) // [1, 2, 3]
console.log('===============================')
arr3[0] = '我是一个新的数据'
console.log('arr1: ', arr1) // ['我是一个新的数据', 2, 3]
console.log('arr2: ', arr2) // [1, 2, 3]
console.log('arr3: ', arr3) // ['我是一个新的数据', 2, 3]
- 赋值的区别
- 基本数据类型: 将变量中的数据赋值给另一个变量
- 赋值以后, 两个变量没有关系了,相当于将我自己的某一个东西, 复制一份给到你, 然后你的是你的, 我的是我的
- 复杂数据类型:将变量中的地址赋值给另一个变量
- 赋值以后,两个变量操作一个存储空间, 相当于我将我房间的钥匙复制一份给到你,你可以自由进出该房间或者对这个房间的布局进行一些调整, 我可以自由进出该房间并且可以对房间的布局做调整
-
比较的区别
- 基本数据类型是 '值' 的比较
- 引用数据类型是 '地址' 的比较
-
传参的区别
- 基本数据类型: 将变量内部的数据复制一份, 传递给对应的形参,所以函数内对这个形参的修改不会影响外界
- 引用数据类型: 将变量内部的地址复制一份, 传给对应的形参,所以此时函数内形参和变量的内部是同一个地址
- 所以在函数内部对这个形参的一些修改, 会影响外界
// 1. 基本数据类型
var num = 100
function fn(a) {
console.log('fn 函数内部的形参 a, 被修改前: ', a)
a = 'QF001'
console.log('fn 函数内部的形参 a, 被修改后: ', a)
}
fn(num)
console.log(num) // 100
// 2. 引用数据类型
var arr = [1, 2, 3]
// function fn(a) {
// /**
// * 因为 fn 函数在传递实参的时候是传递的全局变量 arr, 根据规则, 会将arr内部的地址, 复制一份给到 形参a
// *x
// * 所以我们可以说 形参a 和 全局变量 arr, 内部的地址完全相同
// *
// * 那么如果在函数内部对形参这个地址内的数据做了一些修改, 函数外的全局变量也会被影响
// */
// a[0] = 'QF001'
// }
function fn(a) {
/**
* 当前函数 fn 调用的时候传递的是一个数组 arr, 那么根据规则会将 arr 内部的地址 复制一份给到形参a
*
* 根据规则的说明, 我们在函数中对这个形参的一些修改会影响外界
*
* 但前提是形参和外边变量的内部都是存着同一个地址
*
* 我们的处理方式是将 形参a内部的一个地址重新更改为一个字符串
* 我们没有修改这个地址内部的任何东西, 我们只是将形参a内部存储的地址更改为了一个字符串,
*
* 所以这一步操作相当于 切断了当前形参和全局变量之间的唯一联系
*/
console.log('修改前的形参: ', a)
a = 'QF001'
}
fn(arr)
console.log('arr: ', arr)
课堂案例
var arr = [1, 2, 3]
function fn(arr) {
arr[0] = '新的字符串'
arr = [4, 5, 6]
arr[0] = '最新的字符串'
console.log(arr)
}
fn(arr)
console.log(arr) // ['新的字符串', 2, 3]
/*
var arr = [1, 2, 3] // 1. 假设内部的地址为 QF001
function fn(arr) { // 3. 因为调用传递的实参, 所以可以确定当前形参 arr 和 全局变量 arr, 是同一个地址
arr[0] = '新的字符串' // 4. 因为函数的形参就是 arr, 所以此时不会访问到全局 arr, 我们这里是通过形参arr给 QF001 这个地址内部的数据 [0] 的位置重新更改了一个值, 注意: 此时会影响全局变量的值
arr = [4, 5, 6] // 5. 将 形参 arr 重新赋值为一个 新的数组, 那么此时就是将原本的地址 QF001 更改为了 QF002, 注意: 此时切断了 形参arr 和 全局变量arr 的联系
arr[0] = '最新的字符串' // 6. 通过 形参arr 对 QF002 内的数据 [0] 的值做一个修改, 注意: 此时的修改不会影响全局变量 arr
}
fn(arr) // 2. 调用 fn 函数, 并将 全局变量 arr 内部的地址(QF001) 复制一份传递给 形参 arr
console.log(arr) // ['新的字符串', 2, 3]
*/
面试题
// 题目1
// var obj = {
// name: '张三'
// }
// function fn() {
// obj.name = '李四' // 此时开始在当前作用域内寻找变量 obj, 但是没有找到, 不过在上一层的全局作用域有一个变量叫做 obj, 正好是一个对象 也有 name 属性, 全局变量 obj 的name属性被修改为 '李四'
// obj = {} // 将全局变量 obj 重新修改为 一个空对象
// obj.name = '王五' // 将全局变量 obj 内部的 新对象中 添加一个 name 属性, 值为 '王五'
// }
// fn()
// console.log(obj.name) // 王五
// 题目2
// var obj = {
// name: '张三'
// }
// function fn() {
// obj.name = '李四'
// var obj = {}
// obj.name = '王五'
// /*
// 函数内部原本的代码:
// obj.name = '李四'
// var obj = {}
// obj.name = '王五'
// 变量提升后:
// var obj
// obj.name = '李四'
// obj = {}
// obj.name = '王五'
// */
// }
// fn()
// console.log(obj.name) // 当前代码根本运行不到这里, 因为函数内部会报错
// 题目3
var obj = {
name: '张三'
}
function fn(obj) { // 因为传递的实参obj是引用数据类型的, 所以是将地址复制一份传递给形参obj, 所以此时形参obj和全局变量obj指向同一个对象(地址)
obj.name = '李四' // 在当前行执行的时候还是会发生变量提升, 但是此时因为当前函数有一个形参, 所以在定义并赋值变量前我们使用的是 形参 obj, 所以此时我们是通过 形参 obj 修改了一个地址内的数据, 又因为 全局变量 obj 和 形参obj 是同一个 地址, 所以此时会影响全局变量 obj
var obj = {} // 在当前函数内 声明并赋值了一个 变量, 变量名 obj, 变量的值 空对象
obj.name = '王五' // 将上述定义的局部变量内部新增一个属性 name, 对应的值为 '王五'
}
fn(obj)
console.log(obj.name) // 李四
数组的常用方法
-
push
- 语法: 数组.push(参数)
- 作用: 向数组的末尾新增一条数据
- 返回值: 新增数据后,数据的最新长度 当前方法一般没人用 返回值
// 1. push
var arr = [1, 2, 3]
console.log('原数据: ', arr)
arr.push('一个新的数据')
// var res = arr.push('一个新的数据')
console.log('push 之后的数组: ', arr, res)
- pop
- 语法: 数组.pop()
- 作用: 删除数组末尾最后一个元素
- 返回值: 被删除的数据 当前方法一般没人用
// 2. pop
var arr = [1, 2, 3, '我会被删除']
console.log('原数组arr: ', arr)
// arr.pop()
var res = arr.pop()
console.log('新数组arr: ', arr, res)
- unshift
- 语法: 数组.unshift(参数)
- 作用: 向数组的开头新增一条数据
- 返回值: 新增数据后, 数组的最新长度 当前方法一般没人用
// 3. unshift
var arr = [1, 2, 3]
console.log('原数组arr: ', arr)
// arr.unshift('我是新增的数据')
var res = arr.unshift('我是新增的数据')
console.log('新数组arr: ', arr, res)
- shift
- 语法: 数组.shift()
- 作用: 删除数组开头的第一个元素
- 返回值: 被删除的数据 当前方法一般没人用
// 4. shift
var arr = ['我是要被删除的数据', 1, 2, 3]
console.log('原数组arr: ', arr)
// arr.shift()
var res = arr.shift()
console.log('新数组arr: ', arr, res)
- reverse
- 语法: 数组.reverse
- 作用: 反转数组
- 返回值: 反转后的数组
// 5. reverse
var arr = [1, 2, 3, 4, 5]
console.log('原数组: ', arr)
var res = arr.reverse()
console.log('返回值: ', res)
console.log('新数组: ', arr)
- sort
- 语法1: 数组.sort()
- 作用: 将数组中的所有的元素成员, 转换成字符串, 然后一位一位的对比
- 语法2: 数组.sort(function (a, b) {return a - b})
- 作用: 将我们数组内的数据按照数字的从小到大排序
- 语法3: 数组.sort(function (a, b) { return b - a})
- 作用: 将我们数组内的数据按照数字从大到小排列
- 返回值: 排序后的数组
// 6. sort 语法1: 数组.sort()
var arr = [1, 2, 3, 10, 20, 30, 41, 100, 1000, 50000, 6, 11, 12]
console.log('原数组: ', arr)
var res = arr.sort()
console.log('返回值: ', res)
console.log('新数组: ', arr)
// 6. sort 语法2: 数组.sort(function (a, b) { return a - b })
var arr = [1, 2, 3, 10, 20, 30, 41, 100, 1000, 50000, 6, 11, 12]
console.log('原数组: ', arr)
var res = arr.sort(function (a, b) { return a - b })
console.log('返回值: ', res)
console.log('新数组: ', arr)
// 6. sort 语法3: 数组.sort(function (a, b) { return b - a })
var arr = [1, 2, 3, 10, 20, 30, 41, 100, 1000, 50000, 6, 11, 12]
console.log('原数组: ', arr)
var res = arr.sort(function (a, b) { return b - a })
console.log('返回值: ', res)
console.log('新数组: ', arr)
-
splice
- 语法1:数组.splice(开始位置/开始下标,多少个)
- 作用: 类似于 剪切 的功能
- 语法2: 数组.splice(开始位置/开始下标,多少个,新增的数据1, 新增的数据2, 新增的数据3.....)
- 作用: 首先会按照两个参数指明的位置去剪切出来一个数据,然后将第二个数据以后的(第三个开始) 所有的数据,放在刚才剪切的位置
- 返回值: 剪切到的数据
// 7. splice 语法1: 数组.splice(开始位置/开始下标, 多少个)
var arr = [1, 2, 3, 4, 5, 6, 7]
// 0 1 2 3 4 5 6
console.log('原数组: ', arr)
// var res = arr.splice(2, 3)
// console.log('返回值: ', res)
// console.log('新数组: ', arr)
// 7. splice 语法1: 数组.splice(开始位置/开始下标, 多少个)
var res = arr.splice(1, 3, '新增的数据1', '新增的数据2', '新增的数据3', '新增的数据4')
console.log('返回值: ', res)
console.log('新数组: ', arr)
数组的其他方法(不影响数组)
- slice
- 语法: 数组.slice(开始下标,结束下标)
- 参数的特点
- 包前不包后
- 两个参数都可以省略不写
- 参数可以支持 负数, 相当于 数组.length + 负数
- 作用: 复制指定范围的内容
- 返回值: 复制到的内容
// 1. slice
var arr = [1, 2, 3, 4, 5, 6, 7]
// 0 1 2 3 4 5 6
// 1.3 参数可以支持 负数
var res = arr.slice(3, -1) // (3, -1) => (3, arr.length + (-1)) => (3, arr.length - 1)) => (3, 7 - 1)) => (3, 6))
console.log(res) // [4, 5, 6]
// 1.2 省略结束下标, 那么会从开始下标复制到数组的末尾
var res = arr.slice(2)
console.log('返回值: ', res) // [3, 4, 5, 6, 7]
// 1.2 开始与结束都省略, 相当于复制整个数组
var res = arr.slice()
console.log('返回值: ', res) // [1, 2, 3, 4, 5, 6, 7]
// 1.1 基本演示
console.log('原数组: ', arr)
var res = arr.slice(2, 5)
console.log('返回值: ', res) // [3, 4, 5]
console.log('新数组: ', arr)
- concat
- 语法: 数组.concat(数据1, 数据2, 数据3, 数据4........)
- 作用: 将传入的数据, 合并到指定的数组中, 然后返回出来,
// 2. concat
var arr = [1, 2, 3]
var res = arr.concat(4, 5, 6, [7, 8, 9], true, false, undefined, {name: 'QF001'})
console.log('返回值: ', res)
console.log('方法调用后的 arr: ', arr)
- join
- 语法: 数据.join('连接符') 连接符如果不传递默认按照 , 连接
- 作用: 根据传入的连接符 将数组内的所有的元素, 拼接成一个 完整的字符串
- 返回值:拼接好的字符串
// 3. join
var arr = [1, 2, 3, 999]
// var res = arr.join() // 如果不传参数, 默认按照 , 连接
// var res = arr.join('$')
// var res = arr.join(' ')
// var res = arr.join('')
var res = arr.join('|')
console.log(res)
-
indexOf
- 语法:数组.indexOf(要查询的数据, 从哪里开始查询(下标)) 第二个参数不写默认为 0
- 作用: 按照 从左到右(按照下标的从大到小) 的顺序 检查数组中是否包含指定数据
- 返回值:
- 查询到: 返回从左到右数据第一次出现的下标
- 没有查询到: 返回-1
-
lastIndexOf
- 语法: 数组.lastIndexOf(要查询的数据, 从哪里开始查询(下标)) 第二个参数默认不写默认为 数组最后一个下标
- 作用: 按照 从右到左(按照下标从大到小) 的顺序 检查数组中是否包含指定数据
- 返回值:
- 查询到: 返回从右到左数据第一次出现的下标
- 没有查询到 : 返回 -1
var arr = [1, 2, 3, 4, 5,1,2,3,4,5]
// 0 1 2 3 4 5 6 7 8
// console.log(arr.indexOf(1)) // 不传递第二个参数, 默认从 下标[0] 开始 0
// console.log(arr.indexOf(1, 0)) // 从 下标 [3] 开始 在数组中查询 数字 1 8
// console.log(arr.indexOf(100, 3)) // 从 下标 [3] 开始 在数组中查询 数字 100 -1
// console.log(arr.lastIndexOf(1)) // 不传递第二个参数, 默认从 最后一个元素的下标 开始 8
// console.log(arr.lastIndexOf(1, 3)) // 从 下标 [3] 开始 在数组中查询 数字 1 0
// console.log(arr.lastIndexOf(100, 3)) // 从 下标 [3] 开始 在数组中查询 数字 100 -1
for(let i = 0; i < arr.length;i++){
var arr1=[]
// console.log(arr.indexOf(i,0))
if(arr.indexOf(i,0) === -1){
// arr1.push(arr[i])
// console.log(arr[i])
}
}
console.log(arr1)
遍历数组的方法
- forEach
- 语法: 数组.forEach(function(item, index, origin) { 遍历数组后你想要做的事 } )
- 参数
- item: 数组: 数组中每一个元素
- index: 每个元素对应的下标
- origin: 原数组 (一般不用)
- 作用: 根据数组的元素内容, 循环遍历数组, 拿到数组的每一项
- 返回值: 没有返回值
- 语义: 遍历数组
// 1. forEach
var res = arr.forEach(function (item, index, origin) {})
console.log(res) //undefined
arr.forEach(function (item) {})
arr.forEach(function (item, index) {})
arr.forEach(function (a, b, c) {})
arr.forEach(function (item, index, origin) {
console.log('123', item, index, origin)
})
for (var i = 0; i < arr.length; i++) {
console.log(arr[i], i, arr)
}
- map
- 语法: 数组.map( function (item, index, origin) { 遍历数组后你想要做的事} )
- 参数:
- item: 数组中的每一个元素
- index: 每一个元素对应的下标
- origin: 原数组
- 作用: 根据原数据映射出来一个新数组
- 返回值: 是一个映射出来的新数组(需要在函数内部书写return)
- 语义: 映射数组
var arr = [100, 200, 300, 400, 500]
// 0 1 2 3 4
// 2. map
// 基本演示
// arr.map(function (item, index, origin) {
// console.log(item, index, origin)
// })
var res = arr.map(function (item) {
return item * 2
})
console.log(res)