浅拷贝与深拷贝的区别。如何实现深拷贝。

关于拷贝应该是大家面试中经常被问到的问题,特此总结一下,为以后积累经验。

1,数据类型

巩固拷贝之前必须先回顾下数据的分类,知道其存储的位置。

基本数据类型:
1)分类 (String Number Boolean Undefined Null Symbol)
2)特点(数据存在栈内存中)

引用数据类型:
1)分类 (Object)
2)特点(栈内存中存的是指向堆内存中数据对象的一个地址值,可以理解成是一个指针。当寻找引用类型的值时,会先在栈内存中找到指针,然后堆内存中找到该引用对象。)

2,浅拷贝和深拷贝

何为拷贝:简易理解为重新复制一份数据。
浅拷贝和深拷贝主要针对引用类型数据而言。基本类型数据都是拷贝的栈内存中的值,改变拷贝后的值对原值不会产生影响。

赋值:
不产生新对象,当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。两个对象指向同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容。

var a = 1;
var b = a;
b = 2;
console.log(a) //1
console.log(b) //2

var obj = {
    name: '小明',
    age: 18,
    eat: [1,[2,3],[4,5,6]]
}
var obj1 = obj
obj1.name = '小明1'
obj1.eat[1] = [1,1]
console.log('obj',obj) //{ name: '小明1', age: 18, eat: [ 1, [ 1, 1 ], [ 4, 5, 6 ] ] }
console.log('obj1',obj1) //{ name: '小明1', age: 18, eat: [ 1, [ 1, 1 ], [ 4, 5, 6 ] ] }

浅拷贝:
产生新对象,如果被拷贝对象的属性中有引用类型的值,则拷贝的是数据在堆内存中的地址值,通过拷贝后得到的变量修改数据,源对象中的数据发生改变
即浅拷贝只复制对象的第一层属性。

var obj2 = {
    name: '小明',
    age: 18,
    eat: [1,[2,3],[4,5,6]]
}
var obj3 = clone(obj2)
obj3.name = '小明1'
obj3.eat[1] = [1,1]
console.log('obj2',obj2) //obj2 { name: '小明', age: 18, eat: [ 1, [ 1, 1 ], [ 4, 5, 6 ] ] }
console.log('obj3',obj3) //obj3 { name: '小明1', age: 18, eat: [ 1, [ 1, 1 ], [ 4, 5, 6 ] ] }
function clone(src){
    const cloneObj = src instanceof Array ? [] : {}
    for(var prop in src){
        if(src.hasOwnProperty(prop)){
            cloneObj[prop] = src[prop]
        }
    }
    return cloneObj
}

深拷贝:
产生新对象, 如果被拷贝对象的属性中有引用类型的值,拷贝得是数据在堆内存中的值,通过拷贝后得到的变量修改数据,源对象中的数据不发生改变
即深拷贝可以对对象的属性进行递归复制;

//拷贝对象
var obj4 = {
    name: '小明',
    age: 18,
    eat: [1,[2,3],[4,5,6]]
}
var obj5 = deepClone(obj2)
obj5.name = '小明1'
obj5.eat[1] = [1,1]
console.log('obj4',obj4) //obj4 { name: '小明', age: 18, eat: [ 1, [ 2, 3 ], [ 4, 5, 6 ] ] }
console.log('obj5',obj5) //obj5 { name: '小明1', age: 18, eat: [ 1, [ 1, 1 ], [ 4, 5, 6 ] ] }

//拷贝数组
let arr = [1,2,3,4,[5,6]]
let arr1 = deepClone(arr)
arr1[4] = [1,1]
console.log('arr',arr) //arr [ 1, 2, 3, 4, [ 5, 6 ] ]
console.log('arr1',arr1) //arr1 [ 1, 2, 3, 4, [ 1, 1 ] ]

function deepClone(src){
    const cloneObj = src instanceof Array ? [] : {}
    for(var prop in src){
        if(src.hasOwnProperty(prop)){
            if(typeof prop == Object || typeof prop == Array){
                cloneObj[prop] = deepClone(prop) //关键的一步。如果拷贝时,数据为引用类型,则对属性再进行一次拷贝取值
            }else{
                cloneObj[prop] = src[prop]
            }
        }
    }
    return cloneObj
}

本质区别:修改拷贝后的对象的值,是否影响源对象。

3,浅拷贝的实现方式

1,Object.assign()

const h = {
    age: 18,
    eat: {
        food: 'apple'
    }
}
const i = Object.assign({},h)
i.eat.food = 'noodle'
console.log(h) //{ age: 18, eat: { food: 'noodle' } }
console.log(i) //{ age: 18, eat: { food: 'noodle' } }

2,Array.prototype.concat()

const arr2 = [1,2,3,{name: '小明'},[2,2]]
const arr3 = arr2.concat()
arr3[3].name = '小虹'
arr3[4] = [1,1]
console.log(arr2) //[ 1, 2, 3, { name: '小虹' },[2,2] ]
console.log(arr3) //[ 1, 2, 3, { name: '小虹' },[1,1] ]

3,for in(可以拷贝原型上的属性)

Obj.prototype.run = {
    item: 'sport'
}
const obj1 = new Obj(18,['red'])
const obj2 = {}
for(let i in obj1){
  obj2[i] = obj1[i]
}

obj1.age = 20
obj1.eat.food = 'noodle'
obj1.color.push('green')
console.log(obj1) //{ age: 20, color: [ 'red', 'green' ], eat: { food: 'noodle' } }
console.log(obj2) //{ age: 18,color: [ 'red', 'green' ],eat: { food: 'noodle' },run: { item: 'sport' } }
console.log(obj1.run) //{ item: 'sport' }
console.log(obj2.run)  //{ item: 'sport' }

4,…扩展运算符(不可以拷贝原型上的属性)

function Obj(age,color){
    this.age = age
    this.color = color
    this.eat = {
        food: 'apple'
    }
}
Obj.prototype.run = {
    item: 'sport'
}
const obj1 = new Obj(18,['red'])
const obj2 = {...obj1}

obj1.age = 20
obj1.eat.food = 'noodle'
obj1.color.push('green')
console.log(obj1) //{ age: 20, color: [ 'red', 'green' ], eat: { food: 'noodle' } }
console.log(obj2) //{ age: 18, color: [ 'red', 'green' ], eat: { food: 'noodle' } }
console.log(obj1.run) //{ item: 'sport' }
console.log(obj2.run)  //undefined

4,深拷贝的实现方式

JSON.parse(JSON.stringify())

const arr4 = [1,2,3,{name: '小明'}]
const arr5 = JSON.parse(JSON.stringify(arr4))
arr5[3].name = 'json'
console.log(arr4) //[ 1, 2, 3, { name: '小明' } ]
console.log(arr5) //[ 1, 2, 3, { name: 'json' } ]

封装递归的方法(见上图)

函数库lodash

var _ = require('lodash')
const arr6 = [1,2,3,{name: '小明'}]
const arr7 = _.cloneDeep(arr6)
arr7[3].name = 'lodash'
console.log(arr6)
console.log(arr7)

猜你喜欢

转载自blog.csdn.net/qq_43004614/article/details/90712106