前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JS基础知识总结(二):浅拷贝与深拷贝

JS基础知识总结(二):浅拷贝与深拷贝

原创
作者头像
前端林子
修改2019-01-05 11:09:04
2.8K0
修改2019-01-05 11:09:04
举报
文章被收录于专栏:前端技术总结前端技术总结

上一篇JS基础知识总结(一)主要了介绍变量类型相关的基础知识,本文将介绍JS的深拷贝和浅拷贝的有关内容。

1.基本概念

(1)深拷贝和浅拷贝针对的是引用类型。基本类型的名值存储在栈中,当复制时,栈内存会开辟一个栈内存。所以二者修改时,彼此不会影响。

(2)浅拷贝复制的是指向对象的指针,并没有开辟新的栈内存,原对象和新对象还是共享同一块内存,修改新对象自然会影响原对象。

深拷贝会开辟新的栈内存,原对象和新对象不共享同一块内存,修改新对象不会影响到原对象。

2.实现方式

2.1浅拷贝的实现方式

2.1.1Object.assign()

可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

Object.assign()只会拷贝所有的属性值到新的对象中,如果属性值是基本类型,则修改其中一个对象,不会影响另一个。而如果属性值是对象的话,拷贝的是对象的引用,而不是对象本身。所以该方法是浅拷贝。

分两种情况:

(1)属性值是基本类型,修改其中一个对象,不会影响另一个对象的该属性:

代码语言:javascript
复制
var iniObj = {
    name: "peter",
 };
 var newObj = Object.assign({}, iniObj);
 newObj.name = "lily";
 console.log('iniObj.name', iniObj.name); //peter

(2)属性值是引用类型,由于拷贝的是对象的引用,共享相同的地址:

代码语言:javascript
复制
var iniObj = {
    info: {
        name: "peter",
        age: 8
    }
};
var newObj = Object.assign({}, iniObj);
newObj.info.name = "lily";
console.log('iniObj.info.name', iniObj.info.name); //lily

2.1.2Array.prototype.concat()

concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。注意这里分两种情况:

(1)对象引用(而不是实际对象):concat将对象引用复制到新数组中。原始数组和新数组都引用相同的对象。也就是说,如果引用的对象被修改,则更改对于新数组和原始数组都是可见的。这包括也是数组的数组参数的元素。

(2)数据类型如字符串,数字和布尔(不是StringNumberBoolean 对象):concat将字符串和数字的值复制到新数组中

示例:

代码语言:javascript
复制
var iniArr = [1,2,{name:"peter"}];
var newArr = iniArr.concat();
newArr[1] = 8;
newArr[2].name = "lily";
console.log('iniArr',iniArr);

结果:

2.1.3Array.prototype.slice()

slice()方法返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。

也分两种情况:

(1)如果该元素是个对象引用(不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。

(2)对于字符串、数字及布尔值来说(不是 StringNumber 或者 Boolean 对象),slice 会拷贝些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。

示例:

代码语言:javascript
复制
var iniArr = [1,2,{name:"peter"}];
var newArr = iniArr.slice();
newArr[1] = 8;
newArr[2].name = "lily";
console.log('iniArr',iniArr);

结果:

和上述concat()一样,修改新数组的对象会影响到原数组,修改新数组的基本类型值不会影响到原数组:

2.1.4引用复制

示例:

代码语言:javascript
复制
            var iniObj = {
                gender: "male",
                info: {
                    name: "peter",
                    age: 8
                },
                test: [1, [2, 3]]
            };

            function shallowCopy(copyObj) {
                var obj = {};
                for (var i in copyObj) {
                    obj[i] = copyObj[i];
                }
                return obj;
            }

            var newObj = shallowCopy(iniObj);
            newObj.gender = "female";
            newObj.info.name = "lily";
            newObj.test[1] = [4, 5];
            console.log('iniObj', iniObj);
            console.log('newObj', newObj)    

结果:

2.2深拷贝的实现方式

2.2.1JSON.parse(JSON.stringify())

说明:

1)用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象。一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。

2)这种方法虽然可以实现数组或对象的深拷贝,但不能处理函数。因为JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,不能接受函数

示例:

代码语言:javascript
复制
var iniArr = [1,2,{name:"peter"},function(){}];
var newArr = JSON.parse(JSON.stringify(iniArr));
newArr[1] = 8;
newArr[2].name = "lily";
console.log('JSON.parse(Json.stringfiy()):iniArr,newArr',iniArr,newArr);

结果:

2.2.2递归方法

原理:

遍历对象、数组,直到里边都是基本数据类型,然后再去复制,即可实现深度拷贝。

代码:

代码语言:javascript
复制
            var iniObj = {
                a: 1,
                b: [1, 2, 3],
                c: {
                    d: {
                        e: 4
                    }
                },
                fun: function(){
                    return 999;
                }
            };

            function deepClone(x) {
                if (x.constructor === Object) {
                    var obj = {}
                    for (var k in x) {
                        obj[k] = deepClone(x[k])
                    }
                    return obj
                } else if (x.constructor === Array) {
                    var arr = [];
                    for (var i = 0; i < x.length; i++) {
                        arr[i] = deepClone(x[i])
                    }
                    return arr
                } else {
                    return x
                }
            }
            var newObj = deepClone(iniObj)

            newObj.b[0] = 6;
            newObj.c.d.e = 8;
            console.log('recursive deepClone,iniObj,newObj',iniObj,newObj);

结果:

该方法的局限性:

若属性值是函数、undefined、symbol时,会忽略掉:

示例:

代码语言:javascript
复制
            var iniObj = {
                name:'peter',
                age:undefined,
                school:function(){}
            }
            var newObj = JSON.parse(JSON.stringify(iniObj));
            console.log('newObj',newObj)                

结果:

尽管有上述局限,不过通常情况下,该函数还是可以解决大部分问题的。

2.2.3jQuery的$.extend()

可以用jQuery的$.extend()实现深拷贝:

代码语言:javascript
复制
jQuery.extend([deep], target, object1, [objectN]);

示例:

代码语言:javascript
复制
var iniObj = {
    a: 1,
    b: [1, 2, 3],
    c: {
           d: {
                  e: 4
           }
    },
};
var newObj = $.extend(true, {}, iniObj);
newObj.b[0] = 6;
newObj.c.d.e = 8;
console.log('$.extend deepClone,iniObj,newObj',iniObj,newObj);

结果:

2.2.4lodash的_.cloneDeep()

可以用js库lodash中的_.cloneDeep()实现深拷贝:

示例:

代码语言:javascript
复制
            var iniObj = {
                a: 1,
                b: [1, 2, 3],
                c: {
                    d: {
                        e: 4
                    }
                }
            };
            var newObj = _.cloneDeep(iniObj);
            newObj.b[0] = 6;
            newObj.c.d.e = 8;
            console.log('lodash deepClone,iniObj,newObj',iniObj,newObj);

结果:

3.赋值

可以和上述2.1.4浅拷贝的示例结果做比较,示例:

代码语言:javascript
复制
            var iniObj = {
                gender: "male",
                info: {
                    name: "peter",
                    age: 8
                },
                test: [1, [2, 3]]
            };

            var newObj = iniObj;
            newObj.gender = "female";
            newObj.info.name = "lily";
            newObj.test[1] = [4, 5];
            console.log('iniObj', iniObj);
            console.log('newObj', newObj)        

结果:

说明把一个对象赋值给另一个变量时,并没有创建一个新对象,而是把原对象在栈中的地址(而非栈中的数据)赋给了新对象,即赋的是原对象在栈中的地址,原对象和新对象指向的是同一个地址。因此,两个对象的联动的,修改其中一个,另一个也会改变。包括里面所有的属性,不论是基本类型的数据,还是对象引用。

4.小结

本篇JS基础知识总结,主要介绍了深拷贝和浅拷贝的基本概念,并且分别又介绍了浅拷贝、深拷贝的实现方式,以及与赋值的区别。如有问题,欢迎指正。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.基本概念
  • 2.实现方式
    • 2.1浅拷贝的实现方式
      • 2.1.1Object.assign()
      • 2.1.2Array.prototype.concat()
      • 2.1.3Array.prototype.slice()
      • 2.1.4引用复制
    • 2.2深拷贝的实现方式
      • 2.2.1JSON.parse(JSON.stringify())
      • 2.2.2递归方法
      • 2.2.3jQuery的$.extend()
      • 2.2.4lodash的_.cloneDeep()
  • 3.赋值
  • 4.小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档