前端面试题——对象的深浅拷贝

在实际项目中,我们往往需要对一个对象进行拷贝,其目的可能是为了拷贝一个对象做其他处理,可能是为了扩充一个对象使其拥有另一个对象的属性,也可能是为了其他的目的

但是在 js 中,对象的拷贝分为深浅两种,而不仅仅是将这个对象的属性赋给另一个对象那么简单。同时对象的深浅拷贝也是很对前端面试中常见的一题,所以下面就来详细说说 js 中的 对象深浅拷贝,如果有误,还请多多指正,共同进步

值类型与引用类型

首先理解一下两个概念:值类型引用类型

  • 值类型

简单来说,值类型就是将一个变量赋值给另一个变量后,两个变量完全独立,改变其中的一个并不会影响另一个

var a = 1;
var b = a; // b = 1
a = 2; // a = 2   b = 1

像上面的例子中,虽然后声明的变量b赋予了a的值,但是改变a的值,b却没有改变

除了数值类型,与此类似的 js 中的值类型还有布尔值字符串nullundefined

  • 引用类型

引用类型刚好与值类型相反,原始的变量被改变后,被赋值的变量也会被改变

引用类型会在内存中开辟一块区域保存它的值,而被赋予了这个值的原始变量本质上是将指向了这块内存,而被赋值的另一个变量获得的也只是这个指向而已

所以一旦内存上的值改变,所有只想这块内存的变量的值都会被改变

var c = [1,2,3];
var d = c; // d = [1,2,3]
c[0] = 0; // c = [0,2,3]   d = [0,2,3]

可能有的小伙伴会说,不对呀,下面这种情况 d 并没有被改变

var c = [1,2,3];
var d = c; // d = [1,2,3]
c = [4,5,6]; // c = [4,5,6]   d = [1,2,3]

其实并没有不对,上面例子中的 c[0] 改变的是原内存地址中存储的值,因为c、d指向相同,所有都被改变;而后一个例子中,为 c 重新赋值,相当于是在内存中重新开辟了一块区域存储新值,改变了 c 原来的指向,但 d 的指向却没有改变,所以我们看到的值也就没变

js 中常见引用类型有 数组对象函数

了解了值类型和引用类型,下面我们开始进入正题

对象的浅拷贝

对象的浅拷贝简单,就是将一个变量赋给另一个变量

var obj1 = {
    name: 'test name',
    age: 18
}

var obj2 = obj1;

上面的例子中 obj2 经过浅拷贝拥有了 obj1 的属性

封装浅拷贝方法

    var easyCopy = function ( extendObj ) {
        var newObj = extendObj.constructor === Array ? [] : {};
        if (typeof extendObj != 'object') return;
        for (var key in extendObj) {
            if (extendObj.hasOwnProperty(key)) {
                newObj[key] = extendObj[key];
            }
        }
        return newObj
    };

    var obj2 = {
        tall: 1.8,
        weight: 75
    }

    var obj1 = easyCopy( obj2 );

    console.log( obj1 );

浅拷贝存在的问题

我们知道引用类型的赋值其实是改变了变量的指向,那么如果在需要拷贝的对象中存在某个属性的值是引用类型,如数组或子对象,那么浅拷贝后的原对象的属性获得的也只是这个指向

所以如果改变被拷贝对象的属性值,那么原对象的相应属性也会跟着改变

    var obj2 = {
        names: ['test0', 'test1', 'test3']
    }

    obj1 = easyCopy( obj2 );

    console.log( obj1, obj2 );

    obj2.names[1] = 'test0';

    console.log( obj1, obj2 );

    // 打印结果为:obj1.name[1] 的值从原来的 'test1' 变成了 'test0'

日常项目中使用比较多的是浅拷贝,但是如果某些情况下使用了浅拷贝,可能会产生一些极不容易发现的bug,所以这时候就需要用到深拷贝了

对象的深拷贝

深拷贝其实就是将对象中的数组、子对象进行深度递归遍历,直到其不是引用类型位置再进行复制,这样即使改变了其中一个的值,也不会影响到另一个

深拷贝的封装

    var deepCopy = function( extendObj ){
        var str, newObj = extendObj.constructor === Array ? [] : {};
        if(typeof extendObj !== 'object'){
            return;
        } else if(window.JSON){
            str = JSON.stringify(extendObj);
            newObj = JSON.parse(str);
        } else {
            for(var key in extendObj){
              if (!extendObj.hasOwnProperty(key)) return;
                newObj[key] = typeof extendObj[key] === 'object' ?
                        cloneObj(extendObj[key]) : extendObj[key];
            }
        }
        return newObj;
    };

    var obj2 = {
        names: ['test0', 'test1', 'test3']
    }

    var obj1 = deepCopy( obj2 );

    console.log( obj1, obj2 );

    obj2.names[1] = 'test0';

    console.log( obj1, obj2 );

深拷贝的缺点

虽然深拷贝能够避免浅拷贝出现的问题,但是却会带来性能上的问题,如果一个对象非常复杂或数据庞大,所消耗的性能将会是很可观的

补充

** for … in**

for … in 可以用来遍历任何一个对象,它会将该对象上的所有属性全部遍历出来,包括原型链上的属性

由于可以遍历出原型链上的属性,所以需要使用 hasOwnProperty 这个方法来判断到底是不是这个对象自身的属性

由于数组也是对象,for … in 也可以用来遍历数组,但是 for … in 损耗性能较多,所以如果是遍历数组的话最好使用 for 语句

递归调用

一个方法重复调用自身的情况叫做递归,但是需要注意的是,一定要有一个条件来结束递归,否则将会陷入无限的循环

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

推荐阅读更多精彩内容

  • 1.写一个NSString类的实现 +(id)initWithCString:(c*****t char *)nu...
    韩七夏阅读 3,737评论 2 37
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,531评论 18 399
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,112评论 29 470
  • 累 最近这段时期只能用这个字来诠释 工作压力巨大,奈何猪一样的队友同事让我无奈。 生活压力巨大,无他,囊中羞涩,与...
    HowieGao阅读 104评论 0 1
  • “傻”!这个字从不同人嘴里蹦出来,感觉是不一样的。恋人口中可能是,小傻子,小笨蛋这是一种爱称,浓情蜜意的感觉;特别...
    亻亻卩卩玬刐阅读 522评论 0 0