简介
js中数据类型:number
string
boolean
undefined
object
function
symbol
根据各个类型的特征,我们会将这些类型分为两大类:基本类型
,引用类型
基本类型:number string boolean undefined Symbol null
引用类型: Array Object Function
基本类型和引用类型的区别
1. 存储方式不同
基本类型存储在:栈
引用数据存储在:堆
引用类型:将值存储在堆中,将堆中存储数据的内存地址,存储在栈中
2. 赋值的时候操作不同
基本类型: 将变量a赋值给变量b,是将变量b的值,复制了一份,放在b栈空间中
例:
let a = 1
let b = a
// !将a拷贝一份赋值给b
a = 2 // 将栈中的a的数据数据修改
console.log(b); // 1
基本类型赋值后,改变其中的一个值,另一个值没有被重新赋值,所以另一个的值是不变的
引用类型 将变量arr的值赋值给变量brr,将变量arr在栈中存储的内存地址,复制了一份放在变量brr的栈内存中
例:
let arr = [1,2,3]
let brr = arr
arr = [4,5,6]
console.log(brr); // 1,2,3
arr[0] = 9
console.log(brr); // 9,2,3
两个变量共享一个堆内存,只要一个变量将其中的数据改变了,另一个变量也就改变了
3. 比较方式不同
基本类型
- == 等于判断值是否相同,不比较类型
=== 全等先判断类型是否相同,如果类型相同,才会判断其中的值是否相等
例:let a = 1 let b = 1 console.log(a == b ); // true 相等:等价值 console.log(a === b); // true 全等:完全相同 let n = 1 let i = '1' console.log(n == i); // true console.log(n === i); // false
引用类型
- == 等于判断栈中的内存地址是否相同
=== 全等先判断类型,类型形同在判断栈中的内存地址的值是否相等
let arr = [1,2,3] let brr = [1,2,3] console.log(arr == brr); //false 内存地址不同 console.log(arr === brr); //false
深拷贝浅拷贝
基本类型太简单,没有深和浅的区别,所以深拷贝和浅拷贝针对的都是引用类型
拷贝的原因:在某些操作中,我们需要一个和原数组一模一样的数据,但希望原数据和新数据完全没有关联浅拷贝
原数据和新数据不共用一个堆地址,但其中包含的引用类型数据还会共享一个地址
例:
JavaScript 数组浅拷贝分方法有
扩展运算符
const arr = ['张三','李四','王五']
const brr = [ ...arr ]
console.log(arr === brr); // false
console.log(arr,brr);
console.log(arr[1] === brr[1]); // true
slice()
const arr = ['张三','李四','王五']
const brr = arr.slice(0)
console.log(arr === brr); // false
console.log(arr,brr);
console.log(arr[1] === brr[1]); // true
concat()
const arr = ['张三','李四','王五']
const brr = arr.concat()
console.log(arr === brr); // false
console.log(arr,brr);
console.log(arr[1] === brr[1]); // true
手动进行遍历
const arr = ['张三','李四','王五']
const brr = []
for(let a in arr){
brr.push(arr[a])
}
console.log(arr === brr); // false
console.log(arr,brr);
console.log(arr[1] === brr[1]); // true
Object.assign
const arr = ['张三','李四','王五']
const brr = Object.assign([],arr)
console.log(arr === brr); // false
console.log(arr,brr);
console.log(arr[1] === brr[1]); // true
对象浅拷贝
手动遍历放在新对象中
let obj = {
name: '张三',
age: 12,
children: ['大毛', '二毛', '小明']
}
// 手动遍历
let pbj = {}
for(let key in obj) {
pbj[key] = obj[key]
}
console.log(pbj, obj);
console.log(pbj === obj); // false
console.log( obj.children === pbj.children );
扩展运算
let obj = {
name: '张三',
age: 12,
children: ['大毛', '二毛', '小明']
}
// 扩展运算
let pbj = {...obj}
console.log(pbj, obj);
console.log(pbj === obj);
console.log(pbj.children === obj.children);
assign方法
let obj = {
name: '张三',
age: 12,
children: ['大毛', '二毛', '小明']
}
// assign方法
let pbj = Object.assign({}, obj)
console.log(pbj, obj);
console.log(pbj === obj);
console.log(pbj.children === obj.children);
深拷贝
深拷贝:会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即 发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。
利用json转换
//但是这种方式存在弊端,会忽略undefined、symbol和函数
const obj = {
name: '张三',
name1: undefined,
name3: function() {},
name4: Symbol('A')
}
const obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); // {name: "A"}
手动封装递归函数实现深拷贝
function deepCopy(data) {
// 定义最终返回的数据,初始值为null(后面需要根据原数据的类型,决定这个数据的类型)
let newData = null
// 判断如果原数据是数组
if (Object.prototype.toString.call(data) === '[object Array]') {
newData = [] // 最终返回的数据也应该是数组
} else if (Object.prototype.toString.call(data) === '[object Object]') { // 原数据是对象
newData = {} // 最终返回的数据也应该是对象
} else {
// 原数据既不是数组也不是对象 ===> 将原数据返回出来就是一种数据的拷贝
return data
}
// 遍历原数据
for (let key in data) {
if (typeof data[key] === 'object') { // 判断其中包含的数据的类型是否是数组或对象
// 调用递归函数,处理包含的数组或对象,得到一个新的数据,放在最终返回的数据中
newData[key] = deepCopy(data[key])
} else {
// 遍历出来的是一个基本数据,就直接放在最终返回的数据中
newData[key] = data[key]
}
}
// 将最终返回的数据返回出去
return newData
}
小结
前提为拷贝类型为引用类型的情况下:
- 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
- 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。