js数据类型
基本数据类型
- boolean
- string
- number
- undefined
- null
- Symbol
- bigInt
复杂数据类型
- Object
- Array
- Function
- Set
- Map
- RegExp
- Date
- 等等.....
数据存储方式
基本数据类型:存储在栈中
复杂数据类型:存储在堆中,会在栈中开辟一个空间,存储的是堆的地址
赋值
基本类型
let a=1
let b = a
a=2
console.log(a) // 2
console.log(b) // 1
复杂类型
let xf={
name:'xf',
age:17
}
let person = xf
console.log(xf===person) // true
xf.name='jelly'
console.log(xf) // {name: "jelly", age: 17}
console.log(person) // {name: "jelly", age: 17}
基本类型赋值,是值的拷贝,复杂类型赋值,是赋值的地址,指向同一个内存地址
浅拷贝
浅拷贝:
- 生成新对象,会开辟新的地址空间来存储新对象
- 属性为基本类型,拷贝值(数据值),属性为复杂类型,拷贝值(地址值),修改拷贝后的数据会影响到源数据(同一个对象)
实现浅拷贝的方式
- 扩展运算符
let idols = [
{
name:'gil',
age:17,
},
{
name:'sa',
age:17,
}
]
let likes = [...idols]
console.log(likes) // [{name: "gil", age: 17}, {name: "sa", age: 17}]
likes[0].age=18 // 影响到了源数据idols
console.log(idols) // [{name: "gil", age: 18}, {name: "sa", age: 17}
- slice
let idols = [
{
name:'gil',
age:18,
},
{
name:'sa',
age:17
}
]
let copyIdols = idols.slice()
console.log(idols===copyIdols) // false
copyIdols[0].name='joy'
console.log(copyIdols) // [{name: "joy", age: 18}, {name: "sa", age: 17}]
console.log(idols) // [{name: "joy", age: 18}, {name: "sa", age: 17}]
- Object.assign()
let xf={
name:'xf',
age:17,
hobby:['sleep','eat']
}
let person = Object.assign({},xf)
console.log(person) // {name: "xf", age: 17, hobby: ["sleep", "eat"]}
person.hobby[0] = 'coding' // 影响到了源数据xf
console.log(xf.hobby) // ["coding", "eat"]
- 手写实现
const shadowClone = function(obj){
if(typeof obj!='object'|| obj === null) return obj
let result = Array.isArray(obj)?[]:{}
for(i in obj){
result[i] = obj[i]
}
return result
}
测试对象:
let xf={
name:'xf',
age:17,
hobby:['sleep','eat']
}
let copyXf = shadowClone(xf)
console.log(copyXf) // {name: "xf", age: 17, hobby: ["sleep", "eat"]}
console.log(copyXf === xf) // false
copyXf.hobby[0] = 'codding'
console.log(xf) // {name: "xf", age: 17, hobby: ["codding", "eat"]}
测试对象数组:
let idols = [
{
name:'gil',
age:17,
},
{
name:'sa',
age:17,
}
]
let copyIdols = shadowClone(idols)
console.log(copyIdols) // [{name: "gil", age: 17}, {name: "sa", age: 17}]
console.log(copyIdols === idols) // false
copyIdols[0].age= 18
console.log(idols) // [{name: "gil", age: 18}, {name: "sa", age: 17}]
测试普通数组:
let idols = [ 'gil','sa']
let copyIdols = shadowClone(idols)
console.log(copyIdols) // ["gil", "sa"]
console.log(copyIdols===idols) // false
copyIdols[0] = 'joy'
console.log(idols) // ["gil", "sa"]
深拷贝
- JSON.Parse(JSON.stringify())
let xf={
name:'xf',
age:17,
hobby:['sleep','eat'],
sex:undefined,
getName:function(){},
[Symbol()]:123
}
let copyObj = JSON.parse(JSON.stringify(xf))
copyObj.hobby[0] = 'codding'
console.log(xf) //
console.log(copyObj)
从上面截图可以看到通过JSON.parse(JSON.stringify())实现的是深拷贝,但是他有一些缺陷,循环引用报错、忽略对象中的Symbol、function、undefined等等,关于JSON.parse和JSON.stringify的具体操作,可以查看我的另一篇文章 juejin.cn/post/723971…
- 手写实现
const deepClone = function(obj,cache = new WeakMap()){
// 基本数据类型-直接返回
if(typeof obj !='object' || obj === null) return obj
// // 检查缓存中是否已存在副本
if(cache.has(obj)) return cache.get(obj)
let result
if(Array.isArray(obj)){
result = []
// 将原对象与新副本关联起来
cache.set(obj,result)
// 递归深拷贝数组元素
for(let i = 0;i<obj.length;i++){
result.push(deepClone(obj[i],cache))
}
// 处理Set类型
}else if(obj instanceof Set){
result = new Set()
cache.set(obj,result)
obj.forEach(value=>{
result.add(deepClone(value,cache))
})
// 处理Map类型
}else if(obj instanceof Map){
result = new Map()
cache.set(obj,result)
obj.forEach((value,key)=>{
result.set(deepClone(key,cache),deepClone(value,cache))
})
}else{
result = {}
// 将原对象与新副本关联起来
cache.set(obj,result)
// 递归深拷贝对象属性
Reflect.ownKeys(obj).forEach(value=>{
result[value] = deepClone(obj[value],cache)
})
}
return result
}
测试用例:
let obj={
name:'xf',
age:17,
isGirl:true,
money:undefined,
job:null,
friends:['z','x','c'],
family:{
father:'xxx',
mother:'ccc',
},
x: new Set('1'),
y: new Map([['food','sugar']]),
z:Symbol(),
[Symbol()]:'hahaha',
a: 1,
b: {
c: 2,
d: 3
}
}
// 循环引用
obj.a = obj.b
obj.b.c = obj.a
let copyObj = deepClone(obj)
console.log(copyObj)
copyObj.friends[0] = 'p'
copyObj.family.father = '999'
copyObj.x.add('3')
copyObj.y.set('drink','water')
console.log(copyObj)
console.log(obj)
copyObj
obj
以上的deepClone()处理了Date,RegExp,Array,Object,Set,Map数据类型,也处理了循环引用问题,应该是考虑到了大部分的情况,如果有遗漏的情况,以后发现了再补充