Skip to content

Javascript深浅拷贝 #57

Open
Open
@Wscats

Description

@Wscats
Owner

Javascript有六种基本数据类型(也就是简单数据类型),它们分别是:Undefined,Null,Boolean,Symbol,Number和String。还含有一种复杂数据类型,就是对象

注意Undefined和Null的区别,Undefined类型只有一个值,就是undefined,Null类型也只有一个值,也就是null
Undefined其实就是已声明未赋值的变量输出的结果
null其实就是一个不存在的对象的结果

var c;
console.log(c)//undefined

console.log(document.getElementById('wsscat'))//没有id为wsscat的节点,输出null

简单的数据类型和复杂的数据类型有以下重要的区别

对于简单数据类型

它们值在占据了内存中固定大小的空间,并被保存在栈内存中。当一个变量向另一个变量复制基本类型的值,会创建这个值的一个副本,还有就是不能给基本数据类型的值添加属性

var a = 1;
var b = a;
a.attr = 'wsscat';
console.log(a.attr)//undefined

上面代码中a就是简单数据类型(Number),b就是a的副本,它们两者都占有不同位置但相等的内存空间

对于复杂的数据类型

复杂的数据类型即引用类型,它的值是对象,保存在堆内存中,包含引用类型值的变量实际上包含的并不是对象本身,而是一个指向该对象的指针。从一个变量向另一个变量复制引用类型的值,复制的其实是指针,因此两个变量最终都指向同一个对象。

var obj = {
    name:'wsscat',
    age:0
}
var obj2 = obj;
obj2['c'] = 5;
console.log(obj);//Object {name: "wsscat", age: 0, c: 5}
console.log(obj2);////Object {name: "wsscat", age: 0, c: 5}

qq20161016-0

我们可以看到obj赋值给obj2后,当我们更改其中一个对象的属性值,两个对象都发生了改变,究其原因局势因为obj和obj2这两个变量都指向同一个指针,赋值只是复制了指针,所以当我们改变其中一个的值就会影响另外一个变量的值

浅拷贝

其实这段代码就是浅拷贝,有时候我们只是想备份数组,但是只是简单让它赋给一个变量,改变其中一个,另外一个就紧跟着改变,但很多时候这不是我们想要的

var obj = {
    name:'wsscat',
    age:0
}
var obj2 = obj;
obj2['c'] = 5;
console.log(obj);//Object {name: "wsscat", age: 0, c: 5}
console.log(obj2);////Object {name: "wsscat", age: 0, c: 5}

深拷贝

数组
对于数组我们可以使用slice()concat()方法来解决上面的问题
slice

var arr = ['wsscat', 'autumns', 'winds'];
var arrCopy = arr.slice(0);
arrCopy[0] = 'tacssw'
console.log(arr)//['wsscat', 'autumns', 'winds']
console.log(arrCopy)//['tacssw', 'autumns', 'winds']

concat

var arr = ['wsscat', 'autumns', 'winds'];
var arrCopy = arr.concat();
arrCopy[0] = 'tacssw'
console.log(arr)//['wsscat', 'autumns', 'winds']
console.log(arrCopy)//['tacssw', 'autumns', 'winds']

对象
对象我们可以定义一个新的对象并遍历新的属性上去实现深拷贝

var obj = {
    name:'wsscat',
    age:0
}

var obj2 = new Object();
obj2.name = obj.name;
obj2.age = obj.age

obj.name = 'autumns';
console.log(obj);//Object {name: "autumns", age: 0}
console.log(obj2);//Object {name: "wsscat", age: 0}

当然我们可以封装好一个方法来处理对象的深拷贝,代码如下

var obj = {
    name: 'wsscat',
    age: 0
}
var deepCopy = function(source) {
    var result = {};
    for(var key in source) {
        if(typeof source[key] === 'object') {
            result[key] = deepCopy(source[key])
        } else {
            result[key] = source[key]
        }
    }
    return result;
}
var obj3 = deepCopy(obj)
obj.name = 'autumns';
console.log(obj);//Object {name: "autumns", age: 0}
console.log(obj3);//Object {name: "wsscat", age: 0}

Activity

changed the title [-]Javascript[/-] [+]Javascript基本数据类型[/+] on Oct 13, 2016
changed the title [-]Javascript基本数据类型[/-] [+]Javascript基本数据类型&&深浅拷贝[/+] on Oct 16, 2016
changed the title [-]Javascript基本数据类型&&深浅拷贝[/-] [+]Javascript深浅拷贝[/+] on Oct 16, 2016
Wscats

Wscats commented on Nov 5, 2016

@Wscats
OwnerAuthor

Javascript数组存放函数

在javascript中函数也是一种数据,能够像操作一个对象对它进行操作。并且javascript不进行数据类型检查,数组可以存放任何东西,在下面代码中我们不但在数组中存放了函数,并且也可以在存放一个执行函数的返回值,所以数组前两个数据存放都是函数执行返回值

var funcA = function() {
    console.log("funcA");
    return "hello funA";
}
var funcB = function() {
    console.log("funcB");
    return "hello funB";
}
var funcC = function() {
    console.log("funcC");
    return "hello funC";
}
var arr = [funcA(), funcB(), funcC];
console.log(arr);
arr[2]();

输出的结果如下
qq20161105-0

dcy0701

dcy0701 commented on Dec 16, 2016

@dcy0701

对象中不含有函数的话。JSON解析反解析就行了

ghost

ghost commented on Dec 18, 2016

@ghost

深拷贝

数组
对于数组我们可以使用slice()和concat()方法来解决上面的问题
silce
...

应该是手滑了吧,silce -> slice

Wscats

Wscats commented on Dec 18, 2016

@Wscats
OwnerAuthor

谢谢,已更正~

GreenMelon

GreenMelon commented on Dec 18, 2016

@GreenMelon

ES6中有 Object.assign() 方法,应该也可以解决你对于深克隆的需求吧?

HeftyKoo

HeftyKoo commented on Dec 22, 2016

@HeftyKoo

image
concat 和slice对对象数组没有效果的?

flynntsc

flynntsc commented on Dec 28, 2016

@flynntsc

@GreenMelon Object.assign()方法不适于深拷贝,MDN上有说明

flynntsc

flynntsc commented on Dec 28, 2016

@flynntsc

@yeyuqiudeng concat和slice主要是针对基本数据类型数组的深拷贝,牵涉到多层复杂数据类型即对象的就要再对{a:{b:1}进行深拷贝。有兴趣的话,可对Underscore、Lodash 和 JQuery的源码进行学习,相关文章《深入剖析 JavaScript 的深复制

HeftyKoo

HeftyKoo commented on Dec 28, 2016

@HeftyKoo

@flynntsc 嗯嗯,谢谢

specialCoder

specialCoder commented on Jan 17, 2017

@specialCoder

我这里实现了对象包括数组的深拷贝:

Object.prototype.deepCopy=function(){
    var obj=null;//用于最后返回一个对象,这个对象是深复制的结果
    for(var attr in this){//遍历这个对象的每一个属性
        if(this.hasOwnProperty(attr)){//主要是递归自有属性
            if(typeof (this[attr]==='object')){//如果对象的属性是一个对象,就递归复制它的每一个属性
                if(this[attr]===null){//如果对象为null
                    obj[attr]=null;
                }else if(Object.prototype.toString(this[attr])==='[object Array]'){//如果是个数组
                    obj[attr]=[];
                    for(var i=0;i<this[attr].length;i++){
                        obj[attr].push(this[attr][i].deepCopy());
                    }
                }else{//object
                    obj[attr]=this[attr].deepCopy();
                }
            }else{
                obj[attr]=this[attr];
            }
        }
    }
    return obj;
}

5 remaining items

yunbiji

yunbiji commented on Mar 30, 2018

@yunbiji

没看到对循环引用的处理啊

plane-hjh

plane-hjh commented on Apr 1, 2018

@plane-hjh

function deepCopy(jsonData){
return JSON.parse(JSON.stringify(jsonData));
};
这样也可以吧

LiuL0703

LiuL0703 commented on Apr 8, 2018

@LiuL0703

@plane-hjh 这样有其他问题 具体可以看我 这篇文章

LiuL0703

LiuL0703 commented on Apr 8, 2018

@LiuL0703

@kscript 循环引用解决方案可以看这个具体可以看这篇文章

AdamssBryan

AdamssBryan commented on Apr 10, 2018

@AdamssBryan

slice、concat、assign都是浅拷贝,如果数据中没有函数,可以直接用JSON来实现拷贝,如果有函数,就得用递归来实现深拷贝,递归实现时,函数依然是公用的;
更高级的可以参考楼上的 文章LiuL0703/blog#19

refanbanzhang

refanbanzhang commented on Oct 11, 2018

@refanbanzhang

我这里实现了对象包括数组的深拷贝:

Object.prototype.deepCopy=function(){
    var obj=null;//用于最后返回一个对象,这个对象是深复制的结果
    for(var attr in this){//遍历这个对象的每一个属性
        if(this.hasOwnProperty(attr)){//主要是递归自有属性
            if(typeof (this[attr]==='object')){//如果对象的属性是一个对象,就递归复制它的每一个属性
                if(this[attr]===null){//如果对象为null
                    obj[attr]=null;
                }else if(Object.prototype.toString(this[attr])==='[object Array]'){//如果是个数组
                    obj[attr]=[];
                    for(var i=0;i<this[attr].length;i++){
                        obj[attr].push(this[attr][i].deepCopy());
                    }
                }else{//object
                    obj[attr]=this[attr].deepCopy();
                }
            }else{
                obj[attr]=this[attr];
            }
        }
    }
    return obj;
}

typeof 对array和object都返回object,所以没有必要加一层array的判断吧?

zpj4206

zpj4206 commented on Nov 28, 2018

@zpj4206

function deepCopy(jsonData){
return JSON.parse(JSON.stringify(jsonData));
};
这样也可以吧
只适合对象吧这个

huchenh

huchenh commented on Dec 11, 2018

@huchenh
tanf1995

tanf1995 commented on Jan 12, 2019

@tanf1995
let deep_copy = function f(obj) {
    let new_value;
    if(typeof obj === 'object' && obj != null){
        if(obj instanceof Array){
            new_value = [];
            for(let i=0;i<obj.length;i++){
                if(typeof obj[i] !== 'object' || obj[i] === null){
                    new_value[i] = obj[i];
                }else{
                    new_value.push(f(obj[i]));
                }
            }
        }else{
            new_value = {};
            for(let item in obj){
                if(obj.hasOwnProperty(item)){
                    if(typeof obj[item] !== 'object' || obj[item] === null){
                        new_value[item] = obj[item];
                    }else{
                        Object.assign(new_value, { [item]: f(obj[item])});
                    }
                }
            }
        }
    }else{
        new_value = obj;
    }
 
    return new_value;
};
ZSH0A0

ZSH0A0 commented on Feb 13, 2019

@ZSH0A0

我觉的你的理解有点问题,对象的赋值操作不能算是浅拷贝。在对对象进行浅拷贝时,对象中的基本数据类型会开辟新的空间,引用类型指向的还是同一个地址。而赋值操作,是将对象B的地址指向对象A。
举个例子

var obj1 = {
      name: 'zhangsan',
      num: [1, 2, 3]
}
// 赋值操作
var obj2 = obj1
obj2.name = 'lisi'
console.log(obj1) // {name: "lisi", num: [1, 2, 3] }
console.log(obj2) // {name: "lisi", num: [1, 2, 3] }
// 浅拷贝
var obj3 = Object.assign({}, obj1)
obj3.name = 'wanger'
console.log(obj1) // {name: "lisi", num: [1, 2, 3] }
console.log(obj3) // {name: "wanger", num: [1, 2, 3] }
tanf1995

tanf1995 commented on Feb 21, 2019

@tanf1995
Rotten-Egg

Rotten-Egg commented on Nov 27, 2019

@Rotten-Egg

如果需要深拷贝 函数和正则 呢

zhelingwang

zhelingwang commented on Jul 23, 2020

@zhelingwang

ES6中有 Object.assign() 方法,应该也可以解决你对于深克隆的需求吧?

assign不是深克隆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @flynntsc@HeftyKoo@dcy0701@GreenMelon@refanbanzhang

        Issue actions

          Javascript深浅拷贝 · Issue #57 · Wscats/articles