浅拷贝引用类型变量引起的问题
const outObj = { name: 'out' }
const Demo = () => {
const inObj = { name: 'in' }
const [state1,setState1] = useState(inObj)
const [state2,setState2] = useState(inObj)
const [state3,setState3] = useState(outObj)
useEffect(() => {
setState1((oldValue) => {
oldValue .age = 18
return {...oldValue}
})
setState3((oldValue) => {
oldValue .type = 'outchange'
return {...oldValue}
})
}, [])
useEffect(() => {
console.log('改变\n')
console.log(state1, '1')
console.log(state2, '2')
console.log(inObj, 'in')
console.log(state3, '3')
console.log(ouObj, 'out')
}, [state3])
...
}
得到的结果会有两次打印
改变
{ name: 'in', age: 18 } '1'
{ name: 'in', age: 18 } '2'
{ name: 'in', age: 18 } 'in'
{ name: 'out' } '3'
{ name: 'out' } 'out'
改变
{ name: 'in', age: 18 } '1'
{ name: 'in', age: 18 } '2'
{ name: 'in' } 'in'
{ name: 'out', type: 'outchange' } '3'
{ name: 'out' , type: 'outchange' } 'out'
可以看到,第一次打印时机应为首次赋值给state3,此时inObj已经被修改了
而第二次打印则是改变state3,此时inObj又被初始化了
但改变了state3却引起外层outObj的改变
原因在于outObj是一个引用类型,useState只进行了浅拷贝,他们引用了同一个存储地址,因此修改后会造成相互影响。
结论
简单数据类型可以浅拷贝,复杂数据类型时使用深拷贝
相似方法
concat、slice、map、扩展运算符等都可能为浅拷贝
深拷贝的方法举例
- 不含方法的引用类型可以直接使用JSON转换大法
JSON.parse(JSON.stringify(obj))
递归实现
网上有很多方法,就不举例了第三方库如lodash的cloneDeep(推荐使用,比网上找的递归更靠谱)