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 的内部,是拷贝的过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值