js 中引用类型 的深拷贝 和 浅拷贝的区别

一、曾经在读JQ源码的时候,对深拷贝算是有了一点的理解。我们在项目中是不是经常会遇到这样的问题呢?

后台返回一个数组对象(引用类型).次数在页面渲染中需要对部分数据进行处理 比如:银行卡62345092534 (这么长) 但在页面显示的时候,

只显示中国银行(3118)但是传给后台的时候。又要传623445242整个号码,我们也许会把var oldData = res.data;

但是我们发现两个数据都变了? 这是为什么呢? 其实就是一个深浅拷贝的问题。

二、浅拷贝

比如数组,对象,这样的引用类型。

var arr = [‘js‘,‘html‘];
        var oldArr = arr;
        oldArr[0] = ‘css‘;
        console.log(arr);  // [‘css‘,‘html‘]
        /*
            这其实是一个地址的引用。 相当于他们赋值的是指针。
        */
        //在数组的方式中可以有两种方式来避免这种问题
        //1.slice
        var b = [‘vue‘,‘react‘];
        var oldB = b.slice(0);
        b[0] = ‘angular‘;
        console.log(b); //[‘angular‘,‘react‘]
        console.log(oldB); // [‘vue‘,‘react‘]
        //2.concat();
        var c = [‘d3‘,‘three‘,‘webgl‘];
        var oldC = c.concat([]);
        c[0] = ‘earthgL‘;
        console.log(c,oldC);  // "earthgL", "three", "webgl"] ["d3", "three", "webgl"]

对象的浅复制也是一个道理。是对地址的引用而已。

 var obj = {
            name:"前端",
            age:"10"
        }
        var oldObj = obj;
        obj.name = "html + css + js";
        console.log(oldObj); //{name: "html + css + js", age: "10"}

三、深拷贝。

1.简单来说 深拷贝就事创建了一个新的内存空间。 他们不在会公用同一个内存空间。是两个完全独立的对象或数组。

var defaults = {
  name: ‘quber‘,
  age: [1, 2, 3, 4],
  child: [
   { name: ‘qubernet‘, fun: function () { return 1; } },
   { name: ‘qubernet1‘, fun: function () { return 2; } }
  ]
};
var newDefaults = $.extend(true, {},defaults ); console.log(JSON.stringify(newDefaults ));

2.JQ深拷贝源码部分

jQuery.extend = jQuery.fn.extend = function() {

    var options, name, src, copy, copyIsArray, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;
    /*
    变量 options:指向某个源对象。
    变量 name:表示某个源对象的某个属性名。
    变量 src:表示目标对象的某个属性的原始值。
    变量 copy:表示某个源对象的某个属性的值。
    变量 copyIsArray:指示变量 copy 是否是数组。
    变量 clone:表示深度复制时原始值的修正值。
    变量 target:指向目标对象,申明时先临时用第一个参数值。
    变量 i:表示源对象的起始下标,申明时先临时用第二个参数值。
    变量 length:表示参数的个数,用于修正变量 target。
    变量 deep:指示是否执行深度复制,默认为 false。

    ps:源对象指的是把自己的值付给别人的对象;目标对象指的是被源对象赋值的对象
    */

    // 如果第一个参数传入的是布尔值
    if ( typeof target === "boolean" ) {
        deep = target;//设置deep变量,确定是深拷贝还是浅拷贝
        target = arguments[1] || {};//将目标对象设为第二个参数值。
        i = 2;//源对象的起始下标设为2(即从第三个参数开始算源对象)
    }

    // Handle case when target is a string or something (possible in deep copy)
    //嗯,原英文解释的很清楚
    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
        target = {};
    }

    // 如果没有目标对象,那么目标对象就是jquery对象
    if ( length === i ) {
        target = this;
        --i;
    }

    拷贝的核心部分代码
    for ( ; i < length; i++ ) {//遍历源对象
        // Only deal with non-null/undefined values
        if ( (options = arguments[ i ]) != null ) {//options就是源对象
            // Extend the base object
            for ( name in options ) {//遍历源对象的属性名
                src = target[ name ];//获取目标对象上,属性名对应的属性
                copy = options[ name ];//获取源对象上,属性名对应的属性

                // 如果复制值copy 与目标对象target相等,
                //为了避免深度遍历时死循环,因此不会覆盖目标对象的同名属性。
                if ( target === copy ) {
                    continue;
                }

                //递归地将源对象上的属性值合并到目标对象上
                //如果是深拷贝,且待拷贝的对象存在,且是普通对象或是数组
                //这一个判断条件非常关键,这正是之前疑问的症结
                //首先,普通对象的定义是:通过 "{}" 或者 "new Object" 创建的
                //回到之前的疑问,目标对象tobeCloned的属性o对象的obj不是普通对象,也不是数组,所以程序不会走到下面的分支
                if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                    if ( copyIsArray ) {
                        //如果是数组
                        copyIsArray = false;
                        clone = src && jQuery.isArray(src) ? src : [];

                    } else {
                        clone = src && jQuery.isPlainObject(src) ? src : {};
                    }

                    // 递归地拷贝
                    target[ name ] = jQuery.extend( deep, clone, copy );

                } else if ( copy !== undefined ) {
                //会走到这个分支,这个分支的处理很暴力,就是把源对象的属性,直接赋给源对象。
                //对于上文中tobeCloned对象的属性o,没有进一步递归地拷贝,而是直接把引用赋给源对象
                //所以改变tobeCloned的o属性时,目标对象的o属性也被改变了。
                    target[ name ] = copy;
                }
            }
        }
    }

    // Return the modified object
    return target;
};

3.SE6提供的深拷贝的方法Object.assign();

四、总结。

简单来说:浅拷贝就是多个变量共用一个地址,深拷贝就是创建了多个内存空间。 (希望对你有帮助)

时间: 2025-01-01 23:03:20

js 中引用类型 的深拷贝 和 浅拷贝的区别的相关文章

探究JS中对象的深拷贝和浅拷贝

深拷贝和浅拷贝的区别 在讲深拷贝和浅拷贝的区别之前,回想一下我们平时拷贝一个对象时是怎么操作的?是不是像这样? var testObj1 = {a: 1, b:2}, testObj2=testObj1; testObj1.a = 7; console.log(testObj1); //{a: 7, b:2} console.log(testObj2); //{a: 7, b:2} 发现问题了吗?当testObj1变化时,testObj2相应的属性跟着变化了.这就是属于浅拷贝了,而所谓的深拷贝就

Python中复制、深拷贝和浅拷贝的区别

深拷贝定义(deepcopy) 在Python中,由于一切皆对象,所以任何变量都可以被引用,也即可以被赋值给任何变量.但是在Python中,给变量赋值,是区分的,一般情况下,Python中的变量赋值都是浅拷贝,如果需要使用深拷贝,需要特别指定. 深拷贝是对原对象的"复制以及粘贴",其实就是在内存中重新开辟了一个新的内存空间来存放这一份数据,两个变量其实是两个不一样的变量,仅仅是数据值相同而已,对两个变量的操作不会相互影响. 浅拷贝(copy) 在Python中进行数据的浅拷贝时,如果此

JS中深拷贝与浅拷贝的区别,实现深拷贝的几种方法

JS中深拷贝与浅拷贝的区别,实现深拷贝的几种方法 如何区分深拷贝与浅拷贝,简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力. 此篇文章中也会简单阐述到栈堆,基本数据类型与引用数据类型,因为这些概念能更好的让你理解深拷贝与浅拷贝. 我们来举个浅拷贝例子: let a=[0,1,2,3,4], b=a; console.log(a===b); a[0]=1; console.log(a,b); 嗯?明明b复

Python中深拷贝与浅拷贝的区别

Python中深拷贝与浅拷贝的区别: 原创 2017年04月20日 16:58:35 标签: python / python两种拷贝 / 深拷贝浅拷贝 / 拷贝区别 1661 定义: 在Python中对象的赋值其实就是对象的引用.当创建一个对象,把它赋值给另一个变量的时候,python并没有拷贝这个对象,只是拷贝了这个对象的引用而已. 浅拷贝:拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已.也就是,把对象复制一遍,但是该对象中引用的其他对象我不复制 深拷贝:外围和内部元素都进行了拷贝

深拷贝与浅拷贝的区别,实现深拷贝的几种方法

JS的基本数据类型 基本数据类型:String,Boolean,Number,Undefined,Null: 引用数据类型:Object(Array,Date,RegExp,Function): 浅拷贝 浅拷贝是会将对象的每个属性进行依次复制,但是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化. Object.assign. 扩展运算符 ... . Array.prototype.slice(). Array.prototype.concat() 等 深拷贝 深

Python 深拷贝和浅拷贝的区别

python的复制,深拷贝和浅拷贝的区别    在python中,对象赋值实际上是对象的引用.当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用 一般有三种方法,        alist=[1,2,3,["a","b"]] (1)直接赋值,传递对象的引用而已,原始列表改变,被赋值的b也会做相同的改变 >>> b=alist        >>> print b        

关于js中for in和foreach in的区别

js 中for in 和foreach in的区别 两个的作用都用来遍历对象,但为什么有了for in语句了还要foreach in语句呢,后来看了下foreach in开发的文档,foreach in是作为E4X标准的一部分在javascript 1.6中发布的,而且E4X不是ECMAScript标准的一部分. foreach…in语句已被废弃,E4X中的大部分特性已被删除,但考虑到向后兼容,foreach…in只会被禁用而不会被删除,可以使用ES6中新的for…of语句来代替. var 小青

JS中apply()与call()的含义与区别

JavaScript中,apply()与call()的含义一样,均为改变调用函数中的this指向.其中apply()与call()的第一个参数表示所要指向的对象,若调用函数无参数可不写,则默认为window.第一个参数后的参数表示调用函数的参数,其中apply()第一个参数后面的参数为一个数组,call()第一个参数后面为0或多个参数. 例: 1 window.x = 100; 2 3 4 var obj = {}; 5 obj.x = 1; 6 obj.test = function(a) {

详解JS中Number()、parseInt()和parseFloat()的区别

转载:详解JS中Number().parseInt()和parseFloat()的区别 三者的作用: Number(): 可以用于任何数据类型转换成数值: parseInt().parseFloat(): 专门用于把字符串转换成数值: 一.Number( ): (1)如果是Boolean值,true和false将分别转换为1和0. (2)如果是数字值,只是简单的传入和返回. (3)如果是null值,返回0. (4)如果是undefined,返回NaN. (5)如果是字符串,遵循下列规则: 如果字