您的位置:首页 > Web前端 > JavaScript

JS堆与栈 拷贝

2019-01-11 15:09 106 查看

搜了一些教程,归类整理

栈(stack)和堆(heap)

  • stack为自动分配的内存空间,它由系统自动释放;
  • heap则是动态分配的内存,大小不定也不会自动释放

JS中有基本类型和引用类型

  • 基本类型:Undefined、Null、Boolean、Number 和 String。存放在内存中的简单数据段,直接按值存放的。 直接访问
  • 引用类型:存放在内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置。每个空间大小不一样,要根据情况开进行特定的分配。
      访问,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据

    传值与传址

    • 前面之所以要说明什么是内存中的堆、栈以及变量类型,就是为了更好的理解什么是“浅拷贝”和“深拷贝”。
    • 基本类型与引用类型最大的区别实际就是传值与传址的区别

    基本类型

    var a = 1;
    var b =a;
    b=2;
    console.log(a,b) // 1 2

    引用类型

    var c = [1,2,3,4];
    var d =c;
    d[0]=2;
    console.log(c,d) // [2,2,3,4] [2,2,3,4]

    声明一个基本类型,都会在栈里新开辟一个新空间,哪怕值一样,存放值,所以var a ,var b=a 时,有两个,之后改值是互不影响的。
    但声明一个引用类型,变量是定义的地址指针,所以 var d= c,导致地址指针是一样的,都同时指向堆里的同一个存储空间

    浅拷贝和深拷贝

    浅拷贝的时候如果数据是基本数据类型,那么就如同直接赋值那种,会拷贝其本身,如果除了基本数据类型之外还有一层对象,那么对于浅拷贝而言就只能拷贝其引用,对象的改变会反应到拷贝对象上;但是深拷贝就会拷贝多层,即使是嵌套了对象,也会都拷贝出来。所以会有人粗略地说:浅拷贝是一层,深拷贝是多层。如果拷贝不彻底,改变新数组中嵌套的引用对象,原数据会改变。

    浅拷贝

    基本类型数据互不影响,虽然拷贝对象一开始新建,但浅拷贝里面的引用类型的地址指针是同一个

    方法一

    var obj1 = {
    a:1,
    b:true,
    c:"hello world",
    d:function(){
    console.log("我是一个方法")
    },
    e:[1,2,3],
    f:{
    g:1,
    h:"我是一个h",
    i:{
    j:2,
    k:3
    }
    },
    }
    
    function shallowCopy(obj){
    var objCopy ={};
    for (var i in obj){
    objCopy[i]=obj[i]
    }
    return objCopy
    }
    
    var obj2 = shallowCopy(obj1)
    console.log(obj1,obj2)


    如果我们改变值呢

    obj2.a=2
    obj2.b=false
    obj2.e=[4,5,6]
    obj2.f.g=3
    console.log(obj1,obj2)


    方法二

    ES6中的Object.assign方法,Object.assign是ES6的新函数。Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

    //target:目标对象。
    //sources:任意多个源对象。
    //返回值:目标对象会被返回
    Object.assign(target, ...sources)
    var obj2 =  Object.assign({}, obj1)

    深拷贝

    新旧数据互不干涉

    方法一
    一层一层赋值,不建议

    var obj1 = { a: 10, b: 20, c: 30 };
    var obj2 = {
    a: obj1.a,
    b: obj1.b,
    c: obj1.c
    };
    obj2.b = 100;
    console.log(obj1);
    // { a: 10, b: 20, c: 30 }
    console.log(obj2);
    // { a: 10, b: 100, c: 30 }

    方法二
    用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象
    只有可以转成JSON格式的对象才可以这样用,像function没办法转成JSON

    JSON.parse(JSON.stringify(obj))
    var obj1 = {
    a:1,
    b:true,
    c:"hello world",
    d:function(){
    console.log("我是一个方法")
    },
    e:[1,2,3],
    f:{
    g:1,
    h:"我是一个h",
    i:{
    j:2,
    k:3
    }
    },
    }
    var obj2 = JSON.parse(JSON.stringify(obj1));
    obj2.a = 20;
    obj2.f.g =3
    console.log(obj1,obj2);
    console.log(obj1 === obj2);
    // false
    console.log(obj1.f === obj2.f);
    // false

    方法三

    递归

    function shallowCopy(initalObj, finalObj) {
    var obj = finalObj || {};
    for (var i in initalObj) {
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {
    continue;
    }
    if (typeof prop === 'object') {
    obj[i] = (prop.constructor === Array) ? [] : {};
    shallowCopy(prop, obj[i]);
    } else {
    obj[i] = prop;
    }
    }
    return obj;
    }
    var str = {};
    var obj = { a: {a: "hello", b: 21} };
    shallowCopy(obj, str);
    console.log(str.a);

    方法四

    直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果

    var obj1 = {
    a:1,
    b:true,
    c:"hello world",
    d:function(){
    console.log("我是一个方法")
    },
    e:[1,2,3],
    f:{
    g:1,
    h:"我是一个h",
    i:{
    j:2,
    k:3
    }
    },
    }
    function shallowCopy(initalObj, finalObj) {
    var obj = finalObj || {};
    for (var i in initalObj) {
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {
    continue;
    }
    if (typeof prop === 'object') {
    obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
    } else {
    obj[i] = prop;
    }
    }
    return obj;
    }
    console.log(deepClone(obj1,{}))

    改变值

    var test = shallowCopy(obj1,{});
    test.a=2
    test.f.g=3

    如有问题,欢迎斧正

  • 内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: