Swift51.com
麦子学院 头像
麦子学院  2017-07-14 19:40

Javascript学习之深拷贝和浅拷贝详解

回复:0  查看:2059  
JavaScript 中,存在着这样的两种拷贝方式。分别是:深拷贝和浅拷贝,这两种拷贝在实际中非常的常见,如果读者是一个阅读源码的爱好者,相信多多少少对深拷贝和浅拷贝有所了解。本文和大家分享的就是 深拷贝和浅拷贝相关内容,一起来看看吧,希望对大家 学习javascript有所帮助。
  一、浅拷贝
  浅拷贝在现实中最常见的表现在赋值上面,例如
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title> 测试 </title>
</head>
<body>
    <script type="text/javascript">
        // 第一个数组
        var test=["1","2","3"];
        // 第二个数组
        var test2=[];
        test2=test;
        test2[1]="two";
        console.log(test);// 运行的结果是 ["1","two","3"]
    </script>
</body>
</html>
  从上面的例子,我们修改test2 数组的值,最后打印 test 数组,发现 test 也跟着改变了。
  其实这个就是一个最浅的浅拷贝,相当于test2=test 这个阶段是在将 test 数组中的存储地址索引赋值给 test2 数组,所以两个数组都是指向同一块存储地址中去。
  除了这种方法可以实现浅拷贝,还有使用slice concat 进行浅拷贝
  例如:我们测试一次slice 这个方法
<script type="text/javascript">
    var arr=["demo1","demo2","demo3"];
    var arr2=arr.slice(0);
    arr2[1]="test";
    console.log(arr);//["demo1","demo2","demo3"]
    console.log(arr2);//["demo1","test","demo3"]
</script>
  从上面的例子我们可以看出,使用slice 方法对数组进行了深度拷贝,
  同理,concat 的用法如下
<script type="text/javascript">
    var arr=["demo1","demo2","demo3"];
    var arr2=arr.concat();
    arr2[1]="test";
    console.log(arr);//["demo1","demo2","demo3"]
    console.log(arr2);//["demo1","test","demo3"]
</script>
  为何这样已经算得上是深拷贝的东西,我又称之为浅拷贝呢?
  其实使用slice concat 这两个方法部分都是不可以拷贝如下的这种情况的:
  运行如下的代码:
  <script type="text/javascript">
    var arr = [1,3,[4,7,65,9]];
    var arr1=arr.slice(0);
    arr1[2][2]=2;
    console.log(arr1[2][2]);
    console.log(arr);
</script>
  或者是如下的这一段代码:
<script type="text/javascript">
    var arr = [1,3,[4,7,65,9]];
    var arr1=arr.concat();
    arr1[2][2]=2;
    console.log(arr1[2][2]);
    console.log(arr);
</script>
  我们都可以看到,我们拷贝了arr 的值,然后同时修改 arr1 的值,我们可以看到 arr 也被修改了。
  所以对于 Slice concat 这两个方法来说都是浅拷贝,只能拷贝数组中的第一层
  另外除了可以对数组进行浅拷贝,同样的我们也可以对JSON 数据进行浅拷贝
  assign 这个方法可以对 object 对象进行复制,但是这种拷贝是浅拷贝跟直接赋值却又是不一样的。
  assign 其中接受两个参数,第一个参数指代的是拷贝之后需要修改的内容,第一个参数指代的是要拷贝的内容
  <script type="text/javascript">
    var obj = { a: {a: "hello", b: 21} };
    var initalObj = Object.assign({b:{"test":1111}}, obj);
    initalObj.a.a = "changed";
    console.log(obj);
    console.log(initalObj);
    console.log(obj.a.a); // "changed"
    console.log(obj===initalObj);//false
</script>
  运行上面的代码可以看到,obj  里面的值也被修改了
  二、深拷贝
  最简单的深拷贝就莫过于使用JSON 对象提供的方法。
  我们先来个例子测试一下:
  <script type="text/javascript">
    var json={
        "a":"test",
        "b":"test1"
    };
    var b=JSON.parse(JSON.stringify(json));
    b.a="demo";
    console.log(json);//{"a":"test","b":"test1"}
</script>
  我们修改了b 中的值,但是打印 json 对象发现没有被修改到,这个已经就是深度拷贝的。
  但是转换后的原来的值类型会出现丢失,也就是最后的类型一定是Object 类型。
  说道深拷贝,就不能不提Jquery 中中的 extend 方法,这个方法如果是有学习制作插件的同学应该都会知道,这个方法用于生成一个全新的 JSON 对象值。
  其实这个本身就是一种深拷贝的应用,具体的代码如下:
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.js"></script>
    <script type="text/javascript">
        var arr=["test",["demo1","demo2"]];
        var arr1=$.extend({},arr);
        arr[1][0]=1;
        console.log(arr1);
    </script>
  上面的代码运行的时候,我们可以看到即使是数组也是同样可以深度拷贝的。
  具体的解释是:因为上面我们已经说过数组类型其实就是一种object 类型,那么 extend 方法是用来对 object 类型进行深拷贝的,那么也就是满足了数组的这一种情况,当然博主是没有去看源码的,所以如果解释与源码有出入,望请指正。
来源: 博客园