资讯专栏INFORMATION COLUMN

JavaScript的赋值、深拷贝和浅拷贝

godlong_X / 722人阅读

摘要:内存空间分为两种,栈内存与堆内存栈是系统自动分配的内存空间,由系统自动释放,堆则是动态分配的内存,大小不定不会自动释放。

JavaScript的内存空间

在JavaScript中,每一个数据都需要一个内存空间。内存空间分为两种,栈内存(stack)与堆内存(heap)

栈是系统自动分配的内存空间,由系统自动释放,堆则是动态分配的内存,大小不定不会自动释放。

基本数据类型

JavaScript中的基本数据类型,这些值都有固定的大小,保存在内存中,由系统自动分配存储空间在栈内存空间的值,我们可以直接进行操作,因此基础数据类型都是按照值访问

在栈内存中的数据发生复制的行为时,系统会自动为新变量开辟一个新的内存空间,当复制执行后,两个内存空间的值就互不影响,改变其中一个不会影响另一个

栈内存空间数据复制示例
var a = `I am variable a`;
var b = a; 
console.log(b); //`I am variable a`
b = `I am variable b`;
console.log(a); //`I am variable a`
console.log(b); //`I am variable b`
引用数据类型

引用类型的值是保存在内存中的对象,在JavaScript中我们不能直接操作对象的堆内存空间。因为引用类型的值都是按引用访问的,所以在操作对象时,实际上是操作对象的引用而不是实际的对象。引用可以理解为保存在栈内存中的一个地址,该地址指向堆内存中的一个实际对象

引用类型值的复制,系统会为新的变量自动分配一个新的栈内存空间这个栈内存空间保存着与被复制变量相同的指针,尽管他们在栈内存中的内存空间的位置互相独立但是在堆内存中访问到的对象实际上是同一个,因此,当我们改变其中一个对象的值时,实际上就是改变原来的对象

栈内存空间保存指针(地址),堆内存空间保存实际的对象,我们通过变量访问对象时,实际上访问的是对象的引用(地址)

内存中的栈区域存放变量(基本类型的变量包括变量声明和值)以及指向堆区域存储位置的指针(引用类型的变量包括变量声明和指向内容的指针)

var a = {
    name : `I am object a`,
    type : "object"
}

var b = a;
console.log(b);
// {name: "I am object a", type: "object"}

b.name = `I am object b`;

console.log(a);
// {name: "I am object b", type: "object"}

console.log(b);

// {name: "I am object b", type: "object"}
基本类型总结

基本数据类型

包括:null、undefined、number、string、boolean、symbol(es6)

存放位置:内存中的栈区域中

比较:值的比较,判断是否相等,如果值相等,就相等。一般使用===进行比较,因为==会进行类型的转换

拷贝:赋值(通过(=)赋值操作符 赋值),赋值完成后,两个变量之间就没有任何关系了,改变其中一个变量的值对另一个没有任何影响

引用类型总结

引用数据类型

包括:数组、对象、函数

存放位置:内存的栈区域中存放变量和指针,堆区域存储实际的对象

比较:是引用的比较(就是地址的比较,变量在栈内存中对应的指针地址相等就指向同一个对象)判断是否为同一个对象,示例如下

变量a和变量b的引用不同,对象就不是同一个对象
var a = {name:"Jay"};
var b = {name:"Jay"};
a===b //false

我们对JavaScript中引用类型进行操作的时候,都是操作其对象的引用(保存在栈内存中的指针)

赋值、深拷贝和浅拷贝 (Assignment, deep copy and shallow copy)

赋值:两个变量的值(指针)都指向同一个对象,改变其中一个,另一个也会受到影响

所谓拷贝就是复制,通过复制原对象生成一个新的对象

浅拷贝:重新在堆内存中开辟一个空间,拷贝后新对象获得一个独立的基本数据类型数据,和原对象共用一个原对象内的引用类型数据,改变基本类型数据,两个对象互不影响,改变其中一个对象内的引用类型数据,另一个对象会受到影响

var obj = {
    name: "Jay Chou",
    age: 32,
    song:{
        name:"发如雪",
        year:2007
    }
}
var obj1 = obj;
function shallowCopy(obj){
    var scObj = {};
    for(var prop in obj){
        if(obj.hasOwnProperty(prop)){
            scObj[prop] = obj[prop]
        }
    }
    return scObj;
}
var obj2 = shallowCopy(obj);
console.log(obj === obj1,"obj === obj1","赋值");
console.log(obj === obj2,"obj === obj2","浅拷贝");
// true "obj === obj1" "赋值"
// false "obj === obj2" "浅拷贝"
console.log(obj.song === obj2.song);
//true
obj2.song.name="双截棍";
obj2.name="Jay";
console.log(obj)
// {name: "Jay Chou", age: 32, song: {name:"双截棍",year:2007}}
console.log(obj1);
// {name: "Jay Chou", age: 32, song: {name:"双截棍",year:2007}}
console.log(obj2);
{name: "Jay", age: 32, song: {name:"双截棍",year:2007}}
console.log(obj===obj1)
//true
console.log(obj===obj2)
//false

深拷贝:不论是对象内的基本类型还是引用类型都被完全拷贝,拷贝后两个对象互不影响

一种比较简单实现方法是使用var dcObj = JSON.parse(JSON.stringify(obj))

var obj = {
    name: "Jay Chou",
    age: 32,
    song:{
        name:"发如雪",
        year:2007
    }
}

var dcObj=JSON.parse(JSON.stringify(obj));

console.log(dcObj);
// {name: "Jay Chou", age: 32, song: {name:"发如雪",year:2007}}
console.log(dcObj.song === obj.song);
//false
dcObj.name="Jay";
dcObj.song.name="双截棍";
console.log(obj);
// {name: "Jay Chou", age: 32, song: {name:"发如雪",year:2007}}
console.log(dcObj);
//{name: "Jay", age: 32, song: {name:"双截棍",year:2007}}

需要注意的是,使用JSON.Stringify()序列化对象时会把对象内的function和原型成员忽略掉,示例如下

var obj = {
    name: "Jay Chou",
    job: "artist",
    say:function(){
        alert(this.job);
    }
}
JSON.stringify(obj);
//"{"name":"Jay Chou","job":"artist"}"

通过递归浅拷贝函数实现深拷贝

function deepCopy(obj){
    if(!obj || typeof obj !== "object"){
        return ;
    }
    var dcObj = Array.isArray(obj) ? [] : {};
    for(var key in obj){
        if(obj.hasOwnProperty(key)){
            if(obj[key] && typeof obj[key] === "object"){
                dcObj[key] = Array.isArray(obj[key]) ? [] : {};
                dcObj[key] = deepCopy(obj[key]);
            }
            dcObj[key] = obj[key]
        }
    }
    return dcObj;
}

比较:赋值、深拷贝、浅拷贝

赋值:变量获得原对象的引用,改变该引用指向的对象的值(基本类型引用类型)其实就是修改原对象的值

浅拷贝:改变新对象基本类型的值不会使原对象对应的值一起改变,但是改变新对象引用类型的值使原对象对应的值一同改变

深拷贝:改变新对象基本类型引用类型的值,都不会影响原对象,两者互相独立,互不影响

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/103594.html

相关文章

  • 拷贝和浅拷贝区别

    摘要:深拷贝和浅拷贝的区别背景最近在用框架写页面,赋值给中的对象时会出现一个问题,赋值和被赋值对象之中任何一个有变化,另一个也会随之变化。 深拷贝和浅拷贝的区别 背景:最近在用vue框架写页面,赋值给Vue.$data中的对象时会出现一个问题,赋值和被赋值对象之中任何一个有变化,另一个也会随之变化。例如: var b = { foo: 123 }; var vm = new Vue(...

    suemi 评论0 收藏0
  • JavaScript拷贝

    摘要:实际上,是禁止这样做的。传值和传址基本数据类型赋值基本数据类型的赋值是在内存中新开辟一段栈内存,然后再把再将值赋值到新的栈中。结果见输出,可以看出来,无论是修改赋值得到的对象和浅拷贝得到的都会改变原始数据。 存储问题:深拷贝和浅拷贝的主要区别:在内存中的存储类型(堆和栈)不同堆:动态分配的内存,大小不定也不会自动释放栈:自动分配的内存,由系统自动释放数据类型: 基本数据类型: jav...

    zhjx922 评论0 收藏0
  • JavaScript拷贝和浅拷贝

    摘要:深拷贝和浅拷贝问题的本质还是不同数据类型的存储方式差异,尤其是引用数据类型的特殊。 深拷贝和浅拷贝问题的本质还是不同数据类型的存储方式差异,尤其是引用数据类型的特殊。showImg(https://segmentfault.com/img/bVbb8XH?w=1058&h=409); 现分别对赋值、浅拷贝、深拷贝做深入研究: 1.赋值 原理:直接将对象指针直接赋值给另一个变量 代码: ...

    TalkingData 评论0 收藏0
  • 一篇文章理解JS数据类型、拷贝和浅拷贝

    摘要:接下来我们进入正片数据类型六种基本数据类型布尔值,和一个表明值的特殊关键字。一种数据类型,它的实例是唯一且不可改变的。在中是没有方法是可以改变布尔值和数字的。参考资料深拷贝浅拷贝 前言 笔者最近整理了一些前端技术文章,如果有兴趣可以参考这里:muwoo blogs。接下来我们进入正片: js 数据类型 六种 基本数据类型: Boolean. 布尔值,true 和 false. nu...

    EddieChan 评论0 收藏0

发表评论

0条评论

godlong_X

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<