Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Javascript深浅拷贝 #57

Open
Wscats opened this issue Oct 13, 2016 · 28 comments
Open

Javascript深浅拷贝 #57

Wscats opened this issue Oct 13, 2016 · 28 comments
Labels

Comments

@Wscats
Copy link
Owner

Wscats commented Oct 13, 2016

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}
@Wscats Wscats changed the title Javascript Javascript基本数据类型 Oct 13, 2016
@Wscats Wscats changed the title Javascript基本数据类型 Javascript基本数据类型&&深浅拷贝 Oct 16, 2016
@Wscats Wscats added the notes label Oct 16, 2016
@Wscats Wscats changed the title Javascript基本数据类型&&深浅拷贝 Javascript深浅拷贝 Oct 16, 2016
@Wscats
Copy link
Owner Author

Wscats commented Nov 5, 2016

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
Copy link

dcy0701 commented Dec 16, 2016

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

@ghost
Copy link

ghost commented Dec 18, 2016

深拷贝

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

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

@Wscats
Copy link
Owner Author

Wscats commented Dec 18, 2016

谢谢,已更正~

@GreenMelon
Copy link

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

@HeftyKoo
Copy link

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

@flynntsc
Copy link

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

@flynntsc
Copy link

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

@HeftyKoo
Copy link

HeftyKoo commented Dec 28, 2016

@flynntsc 嗯嗯,谢谢

@specialCoder
Copy link

specialCoder commented Jan 17, 2017

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

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;
}

@Joephy2012
Copy link

test.html:12 Uncaught RangeError: Maximum call stack size exceeded
报错了 @specialCoder

@radicalviva
Copy link

radicalviva commented Mar 30, 2017

基本类型在ES6还有一个Symbol,漏掉了

嗯嗯 by @Wscats

@jiao852jiujiu
Copy link

不错不错👍🏻

@kokpapa
Copy link

kokpapa commented Sep 5, 2017

写得挺好的

@huchenh
Copy link

huchenh commented Oct 9, 2017

涨知识啦

@yunbiji
Copy link

yunbiji commented Mar 30, 2018

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

@plane-hjh
Copy link

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

@LiuL0703
Copy link

LiuL0703 commented Apr 8, 2018

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

@LiuL0703
Copy link

LiuL0703 commented Apr 8, 2018

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

@AdamssBryan
Copy link

AdamssBryan commented Apr 10, 2018

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

@refanbanzhang
Copy link

refanbanzhang commented Oct 11, 2018

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

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
Copy link

zpj4206 commented Nov 28, 2018

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

@huchenh
Copy link

huchenh commented Dec 11, 2018 via email

@tanf1995
Copy link

tanf1995 commented Jan 12, 2019

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
Copy link

ZSH0A0 commented Feb 13, 2019

我觉的你的理解有点问题,对象的赋值操作不能算是浅拷贝。在对对象进行浅拷贝时,对象中的基本数据类型会开辟新的空间,引用类型指向的还是同一个地址。而赋值操作,是将对象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
Copy link

tanf1995 commented Feb 21, 2019 via email

@Rotten-Egg
Copy link

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

@zhelingwang
Copy link

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

assign不是深克隆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests