精华内容
下载资源
问答
  • JavaScript对象浅拷贝和深拷贝

    千次阅读 2020-12-03 15:51:32
    JavaScript对象浅拷贝和深拷贝1. 基本概念2. 浅拷贝3. 深拷贝3.1 深拷贝JSON方法3.2 深拷贝手写递归方法4, 1. 基本概念 对象是引用类型,在声明引用类型的时候,对象是存储在堆内存中的,而js不能直接操作内存,...

    1. 基本概念

    对象是引用类型,在声明引用类型的时候,对象是存储在堆内存中的,而js不能直接操作内存,所以引用类型的变量实际上是一个指针,指针指向存储在堆内存中的对象,例:

    const obj = {}
    

    在这里插入图片描述

    2. 浅拷贝

    const obj1 = {
    	name: '小明'
    }
    const obj2 = obj1
    obj1.name = '小红'
    console.log(obj1.name) // 小红
    console.log(obj2.name) // 小红
    

    浅拷贝实际上拷贝的只是对象的引用,他们的指针指向是堆内存中同一个对象,当修改obj1中name的值为小红时,因为obj2也指向的这个对象,所以obj2中name的值也为小红。
    在这里插入图片描述

    3. 深拷贝

    浅拷贝因为指针都指向堆内存中的同一个对象,所以修改对象的值所有指向这个对象的变量都会改变,有时候我们并不想所有都改变,所以就要用到深拷贝,深拷贝是将对象在内存中完全的拷贝一份出来,让变量指针都指向不同的对象,这样修改其中一个对象的值就不会影响其他对象,如图:
    在这里插入图片描述

    3.1 深拷贝JSON方法

    深拷贝最简单的方法是使用JSON.parse和JSON.stringify,如下:

    const obj1 = {
    	name: '小明'
    }
    const obj2 = JSON.parse(JSON.stringify(obj1))
    obj1.name = '小红'
    console.log(obj1.name) // 小红
    console.log(obj2.name) // 小明
    

    可以看到改变obj1的name之后obj2的name并没有受到影响,但是这个方法有个问题,当对象中有值为function、undefined、symbol,不会被拷贝,如:

    const obj1 = {
    	name: '小明',
    	show() {},
    	age: undefined,
    }
    const obj2 = JSON.parse(JSON.stringify(obj1))
    console.log(obj1) // {name: "小明", age: undefined, show: ƒ}
    console.log(obj2) // {name: "小红"}
    obj1.show() // true
    obj2.show() // obj2.show is not a function
    

    3.2 深拷贝手写递归方法

    这个方法挺好,缺点是如果对象嵌套过深会有一定的性能消耗。

    const deepCopy = (obj) => {
    	const result = Array.isArray(obj) ? [] : {};
    	for (let i in obj) {
    		if (obj.hasOwnProperty(i)) {
    			if (typeof obj[i] === 'object' && obj[i]!==null) {
    				result[i] = deepCopy(obj[i]);   //递归复制
    			} else {
    				result[i] = obj[i];
    			}
    		}
    	}
    	return result;
    }
    const obj1 = {
    	name: '小明',
    	show() {},
    	age: undefined,
    }
    const obj2 = deepCopy(obj1)
    obj1.name = '小红'
    console.log(obj1.name) // 小红
    console.log(obj2.name) // 小明
    

    4. 第三方库的浅拷贝和深拷贝

    推荐直接用第三方库,省事~

    4.1 jquery

    jquery中浅拷贝与深拷贝都是通过extend方法,通过参数区分,如:

    $.extend(true, obj1, obj2) // 深拷贝
    $.extend(obj1, obj2) // 浅拷贝
    

    4.2 lodash

    lodash中使用cloneDeep方法进行深拷贝,这也是我用的最多的方法。

    import _ from 'lodash'
    const obj1 = {
    	name: '小明',
    	show() {},
    	age: undefined,
    }
    const obj2 = _.cloneDeep(obj1)
    

    5. 其他补充

    5.1 Object.assign是浅拷贝还是深拷贝

    属于浅拷贝,引用阮一峰ECMAScript 6中的一段话来解释:

    Object.assign()方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。

    展开全文
  • dict2 = dict1.copy() #浅拷贝 拷贝一级对象,但是如果字典里有个列表,改变dict1中这个列表的值的时候,dict2相应改变,可以自己递归实现深拷贝 import copy dict2 = copy.deepcopy(dict1)#深拷贝所有级别的对象都...

    python字典 直接赋值 copy deepcopy

    dict2 = dict1 #对象的引用
    dict2 = dict1.copy() #浅拷贝 拷贝一级对象,但是如果字典里有个列表,改变dict1中这个列表的值的时候,dict2相应改变,可以自己递归实现深拷贝
    import copy
    dict2 = copy.deepcopy(dict1)#深拷贝所有级别的对象都拷贝
    
    
    浅拷贝只拷贝父对象的意思
    a = {1: [1,2,3]}
    b = a.copy() #a, b分别为({1: [1, 2, 3]}, {1: [1, 2, 3]})
    a[1].append(4) #a, b分别为({1: [1, 2, 3, 4]}, {1: [1, 2, 3, 4]})

    OpenCV mat 直接赋值//Mat image1(image) ;//仅是创建了Mat的头部分,image1与image共享数据区

    cv::Mat image;
    cv::Mat image1(image) ;//仅是创建了Mat的头部分,image1与image共享数据区
    cv::Mat image1 = image ;//仅是创建了Mat的头部分,image1与image共享数据区  
    cv::Mat image1 = image.clone() ;//深拷贝,把image中的所有信息拷贝到image1中
    cv::Mat image1;
    image.copyTo(image1) ;//拷贝image的数据区到image1中,在拷贝数据前会有一步:image1.create(this->size , this->type)
    
    //注意 cv::Mat 作为函数参数,无论写不写& ,看似传值或者传引用这种方式,原图都会发生改变,不使用clone或者copyto或者手动赋值,都不是深拷贝,都共享数据区

    c++ 拷贝构造函数的浅拷贝和深拷贝

    c++中类对象的默认拷贝构造函数是浅拷贝,类a=类b,两个类的成员变量指向同一块内存,如果类b析构了之后,类a的成员变量还指向原来那块内存,就会有野指针的问题,会出错。

    想要深拷贝,应该自己自定义拷贝构造函数,为对应的成员变量开辟新空间。这才是好的编程习惯。

    //深拷贝的例子
    class Example{
    public:
        Example(int num, char* str) {
            num_ = num;
            str_ = new char[num];
            strcpy(str_, str);
        }
        Example(const Example& e) {
            num_ = e.num;
            str_ = new char(num_);
            if(str_ != 0)
                strcpy(str_, e.str);
        }
        ~Example(){
            delete str_;
        }
    
    private:
        int num_;
        char* str_;
    }
    展开全文
  • 我们经常会用到对一个数组或对象的拷贝,而不是操作原数组或对象。...1.数组和对象浅拷贝: function shallowCopy(source){ if (typeof source !== 'object') { throw TypeError('the source must...

    我们经常会用到对一个数组或对象的拷贝,而不是操作原数组或对象。下面总结几种js数组和对象深浅拷贝的几种方式:

    一、es5实现数组和对象的浅拷贝与深拷贝

    1.数组和对象浅拷贝:

    function shallowCopy(source){
        if (typeof source !== 'object') {
            throw TypeError('the source must be an array or object')
        }
        let target = Array.isArray(source) ? [] : {};
        for (let key in source){
            //原型链上的属性不拷贝
            if(source.hasOwnProperty(key)){
                target[key] = source[key]
            }
        }
        return target;
    }

    2.数组和对象深拷贝:

    function deepCopy(source){
        if(typeof source !== 'object'){
            throw TypeError('the source must be an array or object');
        }
        let target = Array.isArray(source) ? [] :{};
        for(let key in source){
            if(source.hasOwnProperty(key)){//不拷贝原型链上的属性
                if(typeof source[key] === 'object'){
                    //递归调用多维数组、对象
                    target[key] = deepCopy(source[key]);
                }else{
                    target[key] = source[key];
                }
            }
        }
        return target;
    }

    二、使用扩展运算符[...]实现浅拷贝

    es6中扩展运算符是实现数组和对象浅拷贝的最简单的实现方式

    浅拷贝:其实我下面集成一下反而显得繁琐不简洁,使用的时候根据数组或对象直接【...source】{...source}才是最简洁的。

    function shallowCopy(source){
        if(typeof source !== 'object'){
            throw TypeError('the source must be an array or object');
        }
        if(Array.isArray(source)){
            return [...source];
        }else{
            return {...source};
        }
    }

    深拷贝:这个玩意用来实现浅拷贝是最好的,用这个实现深拷贝?想啥呢!!!

    三、使用Array.from实现数组的浅拷贝和深拷贝

    Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。实际应用中,常见的类似数组的对象是 DOM 操作返回的 NodeList 集合,以及函数内部的arguments对象。Array.from都可以将它们转为真正的数组。

    扩展运算符背后调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署这个接口,就无法转换。Array.from方法还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from方法转为数组,而此时扩展运算符就无法转换。

    数组浅拷贝:Array.from实现浅拷贝其实跟用扩展运算符原理类似

    function shallowCopy(source) {
        if(Array.isArray(source)){
            return Array.from(source);
        }else{
            throw TypeError('the source must be an array');
        }
    }

    数组深拷贝:利用Array.from的第二个参数回调函数进行递归调用

    Array.from还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组。

    Array.from(arrayLike, x => x * x);
    // 等同于
    Array.from(arrayLike).map(x => x * x);
    
    Array.from([1, 2, 3], (x) => x * x)
    // [1, 4, 9]

    所以可以利用这个map实现数组深拷贝:

    function deepCopy(source) {
        return Array.isArray(source) ? Array.from(source, arguments.callee) : source;
    }

    四、数组和对象深拷贝黑科技:JSON.parse()和JSON.stringify()

    function deepCopy(source){
        return JSON.parse(JSON.stringify(source));
    }

    但是这个黑科技是有缺点的:具体见 详情

    1. 如果对象中有函数如({mm:function(){...}})不会被拷贝
    2. 会将对象原型链上的属性一并给拷贝了

     

     

    展开全文
  • JAVA对象拷贝分为两种方式,一种是引用拷贝,一种是对象拷贝 引用拷贝:和对象拷贝的不同之处在于,引用拷贝只会生成一个新的对象引用地址,但两个地址其最终指向的还是同一个对象对象拷贝:这种方式会重新生成...

    JAVA对象拷贝分为两种方式,一种是引用拷贝,一种是对象拷贝

    引用拷贝:和对象拷贝的不同之处在于,引用拷贝只会生成一个新的对象引用地址,但两个地址其最终指向的还是同一个对象;

    对象拷贝:这种方式会重新生成一个新的对象,生成的新对象与原来的对象没有任何关联。

    1、引用拷贝

    引用拷贝也就是我们常用的对象赋值,这种方式不会生成新的对象,只会在原对象上增加了一个新的对象引用,两个引用指向的对象还是是同一个;

    java 对象默认的赋值方式都是引用拷贝,比如说下面代码对象赋值的过程就是引用拷贝:

    User user1 = new User();


    User user2 = user1;

    引用拷贝需要注意的地方

    下面我们来看一下我们程序里面常见的例子:

    (1)首先定义一个User类


    public class User {


    private int age;//年龄
    private String name;//姓名
    }

    (2)测试代码:

    public static void main(String[] args) {
    //1、实例化一个user1对象,并对属性赋值
    User user1=new User();
    user1.setName("我是user1");
    user1.setAge(18);

    //2、把user1对象放到一个List里面
    List<User> userList=new ArrayList<User>();
    userList.add(user1);

    //3、然后创建user2 对象并从List里面拿出user1对象赋值给user2
    User user2= userList.get(0);

    //4、给user2的属性值
    user2.setAge(1);
    user2.setName("我是user2");

    //5、这个的时候我们再输出user1的对应属性值
    System.out.println(user1);

    System.out.println(user2);
    }

    最后我们看看打印结果:

    举了一个比较绕的例子是想更好的提现出对象赋值的方式可能会给我们带来的问题;

    结合引用拷贝的图解我想我们不难理解这个结果是怎么出现的,因为user1和user2其实指向的是同一个对象,所以当我们修改user2的属性时其实修改的也是user1这个对象。

    程序中如果像这种在一个对象上多次赋值再并使用其实是很危险的,有时候调用的层次多了被传递的使用者修改了对象属性会造成业务逻辑上的错误(想想上面的例子,如果在user2修改属性值之后,还有业务代码要拿user1来进行业务操作的话,那么此时的user1属性值都已经被修改了,这样势必会产生业务上的错误),而这样的问题又比较难发现,并且这样也会造成代码的理解成本变高,可读性也会变差,所以开发中我们尽量避免对象多层传递赋值。

    2、对象浅拷贝

    浅拷贝与引用拷贝不同,被浅拷贝的对象是会重新生成一个新的对象,新的对象和原来的对象是没有任何关系的,但是如果对象中的某个属性是引用类型的话,那么该属性对应的对象是不会重新生成的,浅拷贝只会重新当前拷贝的对象,并不会重新生成其属性引用的对象。

    实现浅拷贝

    需要拷贝的对象实现Cloneable 接口,再调用对象的clone方法可以实现对象的浅拷贝。

    (1)实现 Cloneable 接口 并重写clone 方法

    Teacher 类

    public class Teacher

    {
    //老师姓名
    private String teacherName;
    }

    User类

    public class User implements Cloneable{
    //名字
    private String name;
    //老师
    private Teacher teacher;
    }

    (2)测试

    public static void main(String args[]) throws Exception {
    //user1有一个teacher对象的属性
    Teacher teacher=new Teacher();
    teacher.setTeacherName("我是teacher一号");
    User user1 = new User();
    user1.setName("我是user一号");
    user1.setTeacher(teacher);

    //对user1进行浅拷贝,再重新赋值其属性
    User user2 = (User)user1.clone();
    user2.setName("我是user二号");
    user2.getTeacher().setTeacherName("我是teacher二号");

    //最后我们再打印user1的对象属性
    System.out.println("user1 的name"+user1.getName());
    System.out.println("user1 的techerName"+user1.getTeacher().getTeacherName());
    }

    (3)打印结果

    从打印结果我们可以看出:

    (1)user2修改了name之后并没有影响user1的name,这说明user2和user1对象是独立的。

    (2)user2修改了teacher 对象属性之后user1的teacher对象属性也同时改变了,这说明对象的clone方法并不会把其对象中引用的其他对象进行拷贝,这也是我们俗称的浅拷贝。

    为什么浅拷贝不会拷贝其引用的对象?

    也许你也有个疑问,为什么clone的方式不能把其引用的对象也重新生成一份,那多省事情,我想应该有以下几个原因;

    1、不给其他类强加意义

    这个就好比,User类为了能进行浅拷贝就实现了Cloneable 接口,但是其引用对象Teacher没有实现Cloneable 也许说明他本身就不想被拷贝,如果在拷贝User的情况下,同时也把Teacher拷贝了,这不就等于干了一件没有遵循他人同意的事,干了之后人家还不知道,傻傻的以为没人可以通过clone来拷贝出另外一个Teacher。

    2、不破坏其原来对象的代码逻辑

    如果User引用的Teacher 是个单例模式的对象,那如果在User拷贝的时候同时也拷贝出了一个Teacher 那是不是就会破坏Teacher这个单例模式对象的逻辑初衷。

    3、对象深拷贝

    深拷贝相比浅拷贝的不同就是,深拷贝会把拷贝的对象和其属性引用的对象都重新生成新的对象。

    如何实现深拷贝

    (1)对象实现序列化接口

    User类
    public class User implements Serializable {
    //名字
    private String name;
    //老师
    private Teacher teacher;
    }

    Teacher 类

    public class Teacher implements Serializable {
    //老师姓名
    private String teacherName;

    }

    (2)把对象进行序列化后再反序列化


    public static void main(String args[]) throws Exception {

    //user1有一个teacher对象的属性
    Teacher teacher=new Teacher();
    teacher.setTeacherName("我是teacher一号");
    User user1 = new User();
    user1.setName("我是user一号");
    user1.setTeacher(teacher);

    //序列化写入到流里
    ByteOutputStream bots=new ByteOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bots);
    oos.writeObject(user1);


    //反序列化成user2对象
    ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bots.toByteArray()));
    User user2 = (User) ois.readObject();
    user2.setName("我是user二号");
    user2.getTeacher().setTeacherName("我是teacher二号");

    //最后我们再打印user1的对象属性
    System.out.println("user1 的name"+user1.getName());
    System.out.println("user1 的techerName"+user1.getTeacher().getTeacherName());
    }

    (3)最后看结果,进行深拷贝后,改变User2对象中的属性不会对原来User1对象中的属性有任何影响,这说明,User1和User2 不管是属性还是其引用对象都是重新生成互不关联的两个对象:

    (4)最后找了一个深度拷贝工具类供大家使用


    public abstract class BeanUtil {

    public static <T> T cloneTo(T src) throws RuntimeException {
    ByteArrayOutputStream memoryBuffer = new ByteArrayOutputStream();
    ObjectOutputStream out = null;
    ObjectInputStream in = null;
    T dist = null;
    try {
    out = new ObjectOutputStream(memoryBuffer);
    out.writeObject(src);
    out.flush();
    in = new ObjectInputStream(new ByteArrayInputStream(memoryBuffer.toByteArray()));
    dist = (T) in.readObject();
    catch (Exception e) {
    throw new RuntimeException(e);
    finally {
    if (out != null) {
    try {
    out.close();
    out = null;
    catch (IOException e) {
    throw new RuntimeException(e);
    }
    }
    if (in != null) {
    try {
    in.close();
    in = null;
    catch (IOException e) {
    throw new RuntimeException(e);
    }
    }
    }
    return dist;
    }
    }

    展开全文
  • 浅拷贝是拷贝一层,如果数据是基本数据类型,会拷贝其本身,如果除了基本数据类型之外还有一层对象,那么只能拷贝其引用,对象的改变会反应到拷贝对象上。 深拷贝是拷贝多层,每一层的数据都会拷贝出来,对象的...
  • 1.浅拷贝 拷贝就是把父对像的属性,全部拷贝给子对象。 下面这个函数,就是在做拷贝: var Chinese = {  nation:'中国' } var Doctor = {  career:'医生' }  function extendCopy(p) {  var c = {};  for ...
  • 对象A浅拷贝对象B,A和B实际是同一个引用,改变A就会改变B。 对象A深拷贝到对象B,B是一个全新的对象,与A只是值相同。  具体实现:继承接口ICloneable public class Class1:ICloneable  {  int m_member1;
  • 对象浅拷贝深拷贝方法总结

    千次阅读 2019-08-05 10:26:06
    浅拷贝就是把这份钥匙复制了一份,但内存当中的数据并没有复制,所以如果改变a相应b也会改变 而深拷贝则需要在栈中在生成一个密码箱,生成一把新钥匙(钥匙2),这样深拷贝的两种数值不会相互影响, 也可以说没有...
  • js 对象浅拷贝和深拷贝

    千次阅读 2016-10-30 00:53:28
    var model={name:"boy",age:13}; var CopyModel=model; console.log(CopyModel.name); model.name="girl";...CopyModel复制model对象,修改model对象,再输出CopyModel对象,结果却是修改后model对象的值,为
  • 浅拷贝和深拷贝就是在这个基础之上做的区分,如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有重新创建一个新的对象,则认为是浅拷贝。反之,在对引用数据...
  • 浅层拷贝 该类型拷贝针对引用类型的数据类型(如数组和对象),拷贝的地址,改变其中一个数据内容,另一...而剩余两个因为值数组和对象,所以是浅拷贝) //定义一个对象 var obj = { name:“zs”, teg: [“数据结构...
  • java对象浅拷贝与深拷贝区别

    千次阅读 2012-05-11 15:46:34
    Java的所有类都默认继承java.lang.Object类,在java.lang.Object类中有一个方法clone()。JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点:一是拷贝对象返回的是一个...浅拷贝默认是不拷贝对
  • 对象浅拷贝 第一种 第二种 对象深拷贝 第一种 第二种 第三种 数组浅拷贝 数组深拷贝 对象浅拷贝 第一种 对象的合并 Object.assign(),第一个参数必须是个空对象 var obj1 = { a : 1 , b : 2 } ; ...
  • 拷贝 Python 对象 浅拷贝和深拷贝

    千次阅读 2011-01-20 14:53:00
    也就是说当你创建一个 对象,然后把它赋给另一个变量的时候,Python 并没有拷贝这个对象,而是拷贝了这个对象的 引用。 比如,假设你想创建一对小夫妻的通用档案,名为 person.然后你分别为他俩拷贝一份。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 93,827
精华内容 37,530
关键字:

对象的浅拷贝