js深拷贝和浅拷贝

在这里插入图片描述

  1. 深拷贝的含义
  2. 浅拷贝的含义
  3. js实现深、浅拷贝
  4. 深、浅拷贝的实际应用
  5. 深浅拷贝和序列化的关系

深浅拷贝都是对 引用类型而言。【数组,对象,函数】
对于基本类型【number,boolean,string,undefined, null】
js基本数据类型:undefined, number,string,null, boolean

1. 深拷贝的含义

拷贝,拷贝。其实就是指复制。深拷贝其实就是指复制的多的意思。顾名思义,浅拷贝就是复制的少的意思。
换个方式来解释:深拷贝是个富人,看中了的东西就买买买!买个一模一样的就可以了。这样我想怎么用就怎么用,雨你无瓜
浅拷贝就是个穷人,没钱买那就借着用用呗。能用是能用,但毕竟是别人的东西,会受人家的影响 o(╥﹏╥)o

好了,言归正传,官方解释:铛铛铛铛。。。。七个隆咚锵咚锵~
深拷贝就是 对目标的完全拷贝 拷贝多层,每一级别的数据都会拷贝出来

2. 浅拷贝的含义

浅拷贝就是只复制一层,深层次的对象级别就拷贝引用

3. js实现深、浅拷贝

  1. 浅拷贝
    额。。。浅拷贝更简单,我就先来吃掉它吧!

先创建一个里面含有引用类型的对象

var obj = {
 'name' : 'yyy',
  'age' = '18',
  'arr' = {'hallow', 'deep'},
}

接着 来个比较吧

var obj1 = obj; // 纯种复制
var  obj2 = shallowCopy(obj); // 浅拷贝

function shallowCopy(src) {
const dst = src.constructor === Array ? [] : {};
for(let prop in src) {
if(src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}

obj1.name = "ywj";
obj2.age = "20";
obj1.arr[1] = "heihei";
obj2.arr[3] = "haha";

4. 深、浅拷贝的实际应用

5. 深浅拷贝和序列化的关系

const originArray = [1, 2, 3, 4, 5];
const cloneArray = JSON.parse(JSON.stringify(originArray));

对象中含有一个函数时,就不能用这种当时深拷贝。

const originObj  = {
'name': 'ywj',
'age': '18',
sayHelli: function() {
console.log('Hello');
}
}

const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj);

因为name,age都是基本类型。
对复制过去的obj1来说,相当于指向了同一个空间。
所以设置的name和arr[1]都会对原对象,即obj造成影响,即改变了obj内的数据

而对于浅拷贝而言,基本类型相当于开辟了一块新的空间,即拷贝了一层。但对于引用类型而言,就只是拷贝了其引用,即深层次的对象级别就拷贝引用。
所以对于obj2来说,改变基本类型不会对原对象造成影响(因为开辟了新的空间存放)。但引用类型就会和原对象互相影响。(因为只拷贝了引用,数据是共享的)

  1. 深拷贝
    深拷贝由浅拷贝可推出,就是引用类型和基本类型都开辟了新的空间,不会对原对象造成影响。

实现深拷贝有两种方式:

  1. 深拷贝与序列化
  2. 利用递归来实现每一层都重新创建对象并赋值

由于后面有一栏专门讲解拷贝和序列化的关系,所以这里通过递归来实现
其实和浅拷贝差不多,多了一行:
// 判断如果src里有这个属性,而且类型为object
// 就判断是数组类型还是,对象类型,并重新赋初值,再递归一次deepClone

function deepClone(src) {
var dst = src.constructor === Array ? [] : {};
for(let prop in src) {
if(src.hasOwnProperty(prop)) {
if(src[prop] && typeof src[prop] === 'object') {
dst[prop] = src[prop].constructor === Array ? [] : {};
dst[prop] = deepClone(drc[prop]);
} else {
dst[prop] = src[prop];
} 
}
}
return dst;
}

const obj = {
 "name" : "yyy",
 "age" : "18",
"arr": ["deep", "shallow"],
}

obj1 = shallowClone(obj);
obj2 = deepClone(obj);

console.log(obj2 === obj); //false



深拷贝
浅拷贝
深拷贝与浅拷贝的区别
内存中的存储类型不同
堆,栈都是内存中划分出来用来存储的区域
栈,自动分配的内存空间,系统自动释放
堆,动态分配的内存空间,大小不定也不会自动释放
如何进行深拷贝
如何进行浅拷贝

var obj1 = {
        'name' : 'zhangsan',
        'age' :  '18',
        'language' : [1,[2,3],[4,5]],
    };

    var obj2 = obj1;


    var obj3 = shallowCopy(obj1);
    function shallowCopy(src) {
        var dst = {};
        for (var prop in src) {
            if (src.hasOwnProperty(prop)) {
                dst[prop] = src[prop];
            }
        }
        return dst;
    }

    obj2.name = "lisi";
    obj3.age = "20";

    obj2.language[1] = ["二","三"];
    obj3.language[2] = ["四","五"];

    console.log(obj1);  
    //obj1 = {
    //    'name' : 'lisi',
    //    'age' :  '18',
    //    'language' : [1,["二","三"],["四","五"]],
    //};

    console.log(obj2);
    //obj2 = {
    //    'name' : 'lisi',
    //    'age' :  '18',
    //    'language' : [1,["二","三"],["四","五"]],
    //};

    console.log(obj3);
    //obj3 = {
    //    'name' : 'zhangsan',
    //    'age' :  '20',
    //    'language' : [1,["二","三"],["四","五"]],
    //};

obj1: 原始值
obj2: 赋值
obj3: 浅拷贝

改变基本数据类型
obj2 的name改变的时候,obj1的name也改变了,证明obj2只是将指针改变,所指向的内容没有改变,所以obj2的改变会影响到obj1
obj3的name未对obj1造成影响,证明obj3对obj1是浅拷贝,浅拷贝是重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响

改变引用数据类型
obj2 和obj3 都改变了原始数据里的language,obj1里的language都发生了改变,证明改变了对象中的引用数据类型
因为浅拷贝只会复制一层对象的属性,并不包括对象里面为引用类型的数据。所以改变浅拷贝内的引用类型时,会使原始数据改变

在这里插入图片描述
深拷贝 – 对对象以及对象的所有子对象进行拷贝

// 内部方法:用户合并一个或多个对象到第一个对象
// 参数:
// target 目标对象  对象都合并到target里
// source 合并对象
// deep 是否执行深度合并
function extend(target, source, deep) {
    for (key in source)
        if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
            // source[key] 是对象,而 target[key] 不是对象, 则 target[key] = {} 初始化一下,否则递归会出错的
            if (isPlainObject(source[key]) && !isPlainObject(target[key]))
                target[key] = {}

            // source[key] 是数组,而 target[key] 不是数组,则 target[key] = [] 初始化一下,否则递归会出错的
            if (isArray(source[key]) && !isArray(target[key]))
                target[key] = []
            // 执行递归
            extend(target[key], source[key], deep)
        }
        // 不满足以上条件,说明 source[key] 是一般的值类型,直接赋值给 target 就是了
        else if (source[key] !== undefined) target[key] = source[key]
}

// Copy all but undefined properties from one or more
// objects to the `target` object.
$.extend = function(target){
    var deep, args = slice.call(arguments, 1);

    //第一个参数为boolean值时,表示是否深度合并
    if (typeof target == 'boolean') {
        deep = target;
        //target取第二个参数
        target = args.shift()
    }
    // 遍历后面的参数,都合并到target上
    args.forEach(function(arg){ extend(target, arg, deep) })
    return target
}

在 Zepto 中的 $.extend 方法判断的第一个参数传入的是一个布尔值,判断是否进行深拷贝。

在 $.extend 方法内部,只有一个形参 target,这个设计你真的很巧妙。因为形参只有一个,所以 target 就是传入的第一个参数的值,并在函数内部设置一个变量 args 来接收去除第一个参数的其余参数,如果该值是一个布尔类型的值的话,说明要启用深拷贝,就将 deep 设置为 true,并将 target 赋值为 args 的第一个值(也就是真正的 target)。如果该值不是一个布尔类型的话,那么传入的第一个值仍为 target 不需要进行处理,只需要遍历使用 extend 方法就可以。
这里有点绕,但是真的设计的很精妙,建议自己打断点试一下,会有意外收获(玩转 js 的大神请忽略)。
而在 extend 的内部,是拷贝的过程。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值