JavaScript----深拷贝、浅拷贝

JavaScript----深拷贝、浅拷贝

为说清楚二者使用过程中的区别,首先介绍一些JavaScript的基本知识

JavaScript变量包含两种不同数据类型:基本类型和引用类型

基本类型,有以下6种:

number、string、boolean、null、undefined、symbol

引用类型,指那些可能由多个值构成的对象,只有一个:

object(Array、Function、RegExp、Math、Date)

将一个值赋给变量时,解析器必须确定这个值是基本类型还是引用类型。

基本类型按值访问,课题操作保存在变量中实际的值。

引用类型的值是保存在内存中的对象。JavaScript不允许直接访问内存中的位置,即不能直接操作对象的内存空间,在操作对象时,实际上是在操作对象的引用而不是实际的对象

JavaScript变量的存储方式--栈(stack)和堆(heap)

  • :自动分配内存空间,系统自动释放,存放的是 基本数据类型的值引用类型的地址
  • :动态分配的内存,大小不定,不会自动释放。存放的是引用类型的值

JavaScript的值传递与址传递

基本类型和引用类型最大的区别就是 传值与传址的区别

  • 值传递:基本类型采用的是值传递。
let a = 100; // 定义变量a并赋值100
let b = a;   // 将a的值赋给b(a,b都是基本类型:值传递)
b++;         //b=101
console.log(a);// 100
console.log(b);// 101
  • 址传递:引用类型使用地址传递,将存放在栈内存中的地址赋值给接收的变量。
let a = [1, 2, 3];// Array属于引用类型
let b = a;        // 地址传递:将a的地址赋值给b,a和b指向同一块地址
b.push(4);        // b数组的末尾追加一个4
console.log(a);   // [1, 2, 3, 4]
console.log(b);   // [1, 2, 3, 4]

分析

a和b都是引用类型,采用址传递,即a的地址传递给b,那么a和b指向同一个地址(引用类型的地址存放在栈内存中),而这个地址都指向堆内存中引用类型的值。当b改变了这个值的同时,由于a也指向了这个值,所以a的值也跟着改变。

深拷贝和浅拷贝

  • 深拷贝

深拷贝不仅将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上,所以对一个对象的修改并不会影响另一个对象。

  • 浅拷贝

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。”里面的对象“会在原来的对象和它的副本之间共享。

以数组和对象的深浅拷贝为例

浅拷贝的解决就是先设置一个新的对象obj2,通过遍历的方式将obj1对象的值一一赋值给obj2对象。
代码实现:

// 数组的浅拷贝的解决
let a = [1, 2, 3];
let b = [];
for (let i in a) {
    b[i] = a[i];
}
b.push(4);
console.log(a); // [1, 2, 3]
console.log(b); // [1, 2, 3, 4]

// 对象的浅拷贝的解决
let obj1 = {
    'a' : 1,
    'b' : 2,
    'c' : 3
};
let obj2 = {};
for (let i in obj1) {
    obj2[i] = obj1[i];
}
obj2['d'] = 4;
console.log(obj1); // [a:1, b:2, c:3]
console.log(obj2); // [a:1, b:2, c:3, d:4]

但上面代码只能实现一层的拷贝,无法进行深层次的拷贝,封装函数再次通过对象数组嵌套测试,代码如下:

function shallowCopy(obj1, obj2) {
    for (let key in obj1) {
        if (obj1.hasOwnProperty(key)){
            obj2[key] = obj1[key]
        }
    }
}
let obj1 = {
    fruits: ['apple', 'bannar'],
    num: 100
}
let obj2 = {}
shallowCopy(obj1, obj2)
obj2.fruits[0] = 'orange'
console.log(obj1.fruits[0]); // orange
console.log(obj2.fruits[0]); // orange

结果证明,无法进行深层次的拷贝,这个时候我们可以使用深拷贝来完成。
深拷贝:实现真正意义上的数组和对象的拷贝,可以通过递归调用浅拷贝的方式实现。
代码封装实现如下:

function deepCopy(obj) {
    // 定义一个对象,用来确定当前的参数是数组还是对象
    let objArray = Array.isArray(obj) ? [] : {};
    // 如果obj存在,且类型为objecct
    if (obj && typeof obj === "object") {
        for (key in obj) {
            if (obj.hasOwnProperty(key)) {
                // 如果obj的子元素是对象,递归操作
                if (obj[key] && typeof obj[key] === "object") {
                    objArray[key] = deepCopy(obj[key]);
                } else {
                    // 如果不是,直接赋值
                    objArray[key] = obj[key];
                }
            }
        }
    }
    return objArray; // 返回新对象
}
let obj1 = {
    fruits = ['apple', 'bannar'],
    num: 100
}
let obj2 = deepCopy(obj1);
obj2.fruits[0] = 'orange';
console.log(obj1.fruits[0]); // apple
console.log(obj2.fruits[0]); // orange

结果证明,deepCopy函数可以实现深层次的克隆。

jQuery中提供了extend工具方法:

jQuery.extend([deep], target, object1, [objectN]);
参数介绍:

deep: boolean类型,true表示深拷贝

数组深拷贝的方法

  1. for循环
  let arr1 = [1,2,3];
  let arr2 = copyArr(arr1);
  function copyArr(arr) {
    let res = [];
    for (let i = 0, length = arr.length; i < length; i++){
        res.push(arr[i]);
    }
    return res;
  }
  1. slice
 let arr1 = [1,2,3];
 let arr2 = arr1.slice(0);
  1. concat
let arr1 = [1,2,3];
let arr2 = arr1.concat();
  1. 扩展运算符(...)
let arr1 = [1,2,3];
let [...arr2] = arr1;
  1. Array.from
let arr1 = [1,2,3];
let arr2 = Array.from(arr1);

对象深拷贝的方法

  1. for循环
let obj1 = {count : 1, name : 'grace' , age : 1};
    let obj2 = copyObj(obj) {
        let res = {};
        for (let key in obj) {
            res[key] = obj[key];
        }
        return res;
    }
  1. 利用JSON
let obj1 = {count : 1, name : 'grace' , age:1};
let obj2 = JSON.parse(JSON.stringify(obj1));
  1. 扩展运算符
let obj1 = {count : 1, name: 'grace' , age : 1};
let {...obj2} = obj1;

实现数组和对象的深拷贝,合成版

function deepCopy(obj) {
        let result = Array.isArray(obj) ? [] : {};
        if (obj && typeof obj === 'object') {
            for (let key in obj) {
                if (obj.hasOwnProperty(key)) {
                    if (obj[key] && typeof obj[key] === 'object') {
                        result[key] = deepCopy(obj[key]);
                    } else {
                        result[key] = obj[key];
                    }
                }
            }
        }
        return result;
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,968评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,682评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,254评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,074评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,964评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,055评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,484评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,170评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,433评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,512评论 2 308
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,296评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,184评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,545评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,150评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,437评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,630评论 2 335