堆栈

作者: 撑船的摆渡人 | 来源:发表于2019-02-22 16:52 被阅读0次

    栈 (stack)和 堆 (heap)

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

    基本类型和引用类型

    基本类型: 存放在栈内存中的简单数据段,数据大小确定,内存空间大小可以分配。
    6种基本数据类型有undefined、 Null、 Boolean、 Number、 String、 Symbol,它们是直接按值存放的,所以可以直接访问。
    引用类型:存放在堆内存中的对象,变量实际保存的是一个指针,这个指针指向另一个位置。每个空间大小不一样,要根据情况进行特定的分配。
    当我们需要访问引用类型(如对象, 数组, 函数 等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需要的数据

    传值与传址

    基本类型与引用类型最大的区别实际就是传值与传址的区别。测试用例

    let a = [1,2,3,4,5];
    let b = a;
    let c = a[0];
    b[4] = 6;
    c = 7;
    console.log(a,a[4],a[0])
    //[1, 2, 3, 4, 6] 6 1
    

    当我们改变b中的数据时,a中数据也发生了变化;但是当我们改变c的数据时,a却没有发生变化。
    这就是传值与传址的区别。因为a是数组属于引用类型,所以它赋予给b的时候传的是栈中的地址(相当于新建了一个不同名“指针”),而不是堆内存中的对象。而c仅仅是从a堆内存中获取的一个数据值,并保存在栈中。所以b修改的时候,会根据地址回到a堆中修改,c则直接在栈中修改,并且不能指向a堆内存中。


    stackHeap.png

    浅拷贝

    在定义一个对象或数组时,变量存放的只是一个地址。当使用对象拷贝时,如果属性是对象或数组时,这时候传递的也是一个地址。因此子对象在访问该属性时,会根据地址回溯到父对象指向的堆内存中,即父子对象发生了关联,两者的属性值会指向同一内存空间。

    let a = {
        name:'Lilei'
    }   
    function Copy(p){
        let c = {};
        for(var i in p){
            c[i] = p[i];
        }
        return c;
    }
    a.hobby = ['football','basketball'];
    let b = Copy(a);
    b.age = 18;
    b.name = 'Han'
    console.log(b.name); // Han
    console.log(b.age); // 18
    console.log(a.name); // LiLei
    console.log(a.age); // undefined
    

    a对象中name属性是字符串,hobby属性是数组。a拷贝到b,name、hobby属性均顺利拷贝。给b对象新增一个字符串类型的属性age时,b能正常修改,而a中无定义。说明子对象的age (基本类型) 并没有关联到父对象中,所以undefined。

    b.hobby.push('badminton');
    console.log(b.hobby,a.hobby); 
    // ["football", "basketball", "badminton"]   ["football", "basketball", "badminton"]
    

    若修改的属性变为对象或数组时,那么父子对象之间就会发生关联。对b对象进行修改,a、b的hobby属性值 (数组) 均发生了改变。


    对象关联.png

    原因是name的值属于基本类型,所以拷贝的时候传递的就是该数据段;但是hobby的值时堆内存中的对象,所以hobby在拷贝的时候传递的是指向hobby对象的地址,无论复制多少个hobby其值始终是指向父对象的hobby对象的内存空间。

    深拷贝

    在实际编码中,不希望父子对象之间产生关联,就需要深拷贝。既然属性值类型是数组或对象时只会传址,那我们用递归来解决这个问题,把父对象中所有属于对象的属性类型都遍历赋给子对象即可。

    let a = { name:'Lilei' }  
    function Copy(p, c){
        var c = c || {};
        for(let i in p){
            if(typeof p[i] === 'object'){
                c[i] = (p[i].constructor === Array) ? [] : {};
                Copy(p[i], c[i]);
            }else{
                c[i] = p[i];
            }
        }
        return c;
    }    
    a.hobby = ['football','basketball'];
    let b = {};
    b = Copy(a,b);
    b.hobby.push('mspaint');
    console.log(a.hobby,b.hobby);
    //["football", "basketball"]   ["football", "basketball", "mspaint"]
    

    由上可知,修改b的hobby数组时,没有使a父对象中的hobby数组改变,即子对象没有影响到父对象a中的hobby。其存储模式大致如下:


    深拷贝.png

    相关文章

      网友评论

          本文标题:堆栈

          本文链接:https://www.haomeiwen.com/subject/ujpzdqtx.html