JavaScript中拷贝对象方法总结

对象拷贝是在js中最基本的对象操作。

浅拷贝

function sallowCopy(source) {
    // source 不是对象,而是【原始类型】的情况
    // 原始类型说明详见http://www.jianshu.com/p/b161aeecb6d6
    if (null == source || "object" != typeof source) return source;
    
    // 其他情况都将 source 当作简单对象来处理
    var target = {};
    for (var key in source) {
        if (source.hasOwnProperty(key)) {  // 仅拷贝自身的属性
            target[key] = source[key];
        }
    }
    return target;
}
/*
这个浅拷贝会将source对象上的所有[可枚举属性](http://www.jianshu.com/p/7b8da1db32b3)都拷贝到target对象上,不包括原型链上的属性。
*/

浅复制仅仅复制嵌套对象的地址

var outter = {
    outter_property:333,
    // inner 是嵌套对象
    inner: {
        inner_property:222
    }
};

var copy = sallowCopy(outter);
// copy.inner 与 outter.inner 是同一个对象,它们指向同一个内存地址。

copy.inner.inner_property = 'new value!';
console.log(outter.inner.inner_property);   // new value!
//改变了copy.inner对象,也就改变了outter.inner对象

拷贝需要注意的问题有很多:

  • 需要拷贝的对象是Array
  • 需要拷贝的对象是Number、String、Boolean包装对象
  • 需要拷贝的对象是Date、RegExp等特别的内置对象
  • 需要拷贝的对象是Function
  • 需要拷贝的对象存在环,比如:
    var b = {}; 
    a.child = b;
    b.child = a;
    

这些问题,上面这个方法都没有考虑,它只适用于简单对象的复制,这个方法仅用于示范浅拷贝的原理。事实上很难写出也没必要写出一个能够应付所有情况的完美方法。只要根据实际情况选择,往往都能找到一个满足需要的方法。文末会列举现有的、常见的拷贝函数。

深拷贝

function deepClone(obj) {
    var copy;

    // Handle number, boolean, string, null and undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = deepClone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = deepClone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

深拷贝会复制嵌套的对象:

var outter = {
    outter_property:333,
    inner: {
        inner_property:222
    }
};

var copy = deepClone(outter);
// copy.inner 与 outter.inner是不同的两个对象,从此互不干涉
copy.inner.inner_property = 'new value!';
console.log(outter.inner.inner_property);   // 222
// 修改copy.inner.inner_property,outter.inner.inner_property不会改变

上面这个深拷贝方法除了可以处理原始类型和简单对象以外,还能处理Date和Array,依然不能处理Number对象、RegExp对象、Function对象等。不过已经比较实用了。

现成的拷贝方法

  • var cloneOfA = JSON.parse(JSON.stringify(a));可以用于简单对象的深拷贝。这个方法的原理是将对象转换成json字符串以后再将json字符串转换成对象。因此要注意那些转换成json以后无法恢复的类型,最好只用来处理属性值是原始类型的对象和数组,或者它们的嵌套。此外,这个方法也不能处理存在环的对象。
  • Object.assign(target, ...sources)是ES6提供的浅拷贝方法,与我们给出的浅拷贝方法作用类似,拷贝对象自身的、可枚举的属性。Object.assign可以传入多个source对象,并且target不要求是空对象。需要注意的是它拷贝streing、number、boolean原始类型的时候,会先将它们装箱,再拷贝这个包装对象:
Object.assign({}, 'abcd')
// Object {0: "a", 1: "b", 2: "c", 3: "d"}
  • var copiedObject = jQuery.extend({}, originalObject) 是jQuery提供的方法。默认是浅拷贝,它拷贝自身和原型链上的所有可枚举属性。可以通过设置第一个参数为true来进行深拷贝:
    var copiedObject = jQuery.extend(true, {}, originalObject)

    extend、assign这些单词的名字的意思是“扩展”、“赋值”,拷贝对象只是它们的用途之一,它们的target参数不一定是要{}

  • Underscore的 _.clone(source) 浅拷贝,返回拷贝出的新对象。它拷贝自身和原型链上的所有可枚举属性。
  • lodash 的 _.clone(value) 和 _.cloneDeep()能够很好地处理很多内置对象:arrays, array buffers, booleans, date objects, maps, numbers, Object objects, regexes, sets, strings, symbols, and typed arrays,并且能处理存在环的对象,更接近完美。

参考资料

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

推荐阅读更多精彩内容