JavaScript 中对象的深拷贝


Posted in Javascript onDecember 04, 2016

对象的深拷贝与浅拷贝的区别如下:

浅拷贝:仅仅复制对象的引用,而不是对象本身;
深拷贝:把复制的对象所引用的全部对象都复制一遍。

一. 浅拷贝的实现

浅拷贝的实现方法比较简单,只要使用是简单的复制语句即可。

1.1 方法一:简单的复制语句

/* ================ 浅拷贝 ================ */
function simpleClone(initalObj) {
    var obj = {};
    for ( var i in initalObj) {
        obj[i] = initalObj[i];
    }
    return obj;
}

客户端调用

/* ================ 客户端调用 ================ */
var obj = {
    a: "hello",
    b: {
        a: "world",
        b: 21
    },
    c: ["Bob", "Tom", "Jenny"],
    d: function() {
        alert("hello world");
    }
}
var cloneObj = simpleClone(obj); // 对象拷贝
 
console.log(cloneObj.b); // {a: "world", b: 21}
console.log(cloneObj.c); // ["Bob", "Tom", "Jenny"]
console.log(cloneObj.d); // function() { alert("hello world"); }
 
// 修改拷贝后的对象
cloneObj.b.a = "changed";
cloneObj.c = [1, 2, 3];
cloneObj.d = function() { alert("changed"); };
 
console.log(obj.b); // {a: "changed", b: 21} // // 原对象所引用的对象被修改了
 
console.log(obj.c); // ["Bob", "Tom", "Jenny"] // 原对象所引用的对象未被修改
console.log(obj.d); // function() { alert("hello world"); } // 原对象所引用的函数未被修改

1.2 方法二:Object.assign()

Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

var obj = { a: {a: "hello", b: 21} }; 
var initalObj = Object.assign({}, obj); 
initalObj.a.a = "changed"; 
console.log(obj.a.a); // "changed"

二. 深拷贝的实现

要实现深拷贝有很多办法,有最简单的 JSON.parse() 方法,也有常用的递归拷贝方法,和ES5中的 Object.create() 方法。

2.1 方法一:使用 JSON.parse() 方法

要实现深拷贝有很多办法,比如最简单的办法是使用 JSON.parse():

/* ================ 深拷贝 ================ */
function deepClone(initalObj) {
    var obj = {};
    try {
        obj = JSON.parse(JSON.stringify(initalObj));
    }
    return obj;
}
/* ================ 客户端调用 ================ */
var obj = {
    a: {
        a: "world",
        b: 21
    }
}
var cloneObj = deepClone(obj);
cloneObj.a.a = "changed";
 
console.log(obj.a.a); // "world"

这种方法简单易用。

但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。

这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象,即那些能够被 json 直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。

2.2 方法二:递归拷贝

代码如下:

/* ================ 深拷贝 ================ */
function deepClone(initalObj, finalObj) {
    var obj = finalObj || {};
    for (var i in initalObj) {
        if (typeof initalObj[i] === 'object') {
            obj[i] = (initalObj[i].constructor === Array) ? [] : {};
            arguments.callee(initalObj[i], obj[i]);
        } else {
            obj[i] = initalObj[i];
        }
    }
    return obj;
}

上述代码确实可以实现深拷贝。但是当遇到两个互相引用的对象,会出现死循环的情况。

为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环。

改进版代码如下:

/* ================ 深拷贝 ================ */
function deepClone(initalObj, finalObj) {
    var obj = finalObj || {};
    for (var i in initalObj) {
        var prop = initalObj[i];
 
        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
        if(prop === obj) {
            continue;
        }
 
        if (typeof prop === 'object') {
            obj[i] = (prop.constructor === Array) ? [] : {};
            arguments.callee(prop, obj[i]);
        } else {
            obj[i] = prop;
        }
    }
    return obj;
}

2.3 方法三:使用Object.create()方法

直接使用var newObj = Object.create(oldObj),可以达到深拷贝的效果。

/* ================ 深拷贝 ================ */
function deepClone(initalObj, finalObj) {
    var obj = finalObj || {};
    for (var i in initalObj) {
        var prop = initalObj[i];
 
        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
        if(prop === obj) {
            continue;
        }
 
        if (typeof prop === 'object') {
            obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
        } else {
            obj[i] = prop;
        }
    }
    return obj;
}

三. 参考:jQuery.extend()方法的实现

jQuery.js的jQuery.extend()也实现了对象的深拷贝。下面将官方代码贴出来,以供参考。

官方链接地址:https://github.com/jquery/jquery/blob/master/src/core.js。

jQuery.extend = jQuery.fn.extend = function() {
    var options, name, src, copy, copyIsArray, clone,
        target = arguments[ 0 ] || {},
        i = 1,
        length = arguments.length,
        deep = false;
 
    // Handle a deep copy situation
    if ( typeof target === "boolean" ) {
        deep = target;
 
        // Skip the boolean and the target
        target = arguments[ i ] || {};
        i++;
    }
 
    // Handle case when target is a string or something (possible in deep copy)
    if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
        target = {};
    }
 
    // Extend jQuery itself if only one argument is passed
    if ( i === length ) {
        target = this;
        i--;
    }
 
    for ( ; i < length; i++ ) {
 
        // Only deal with non-null/undefined values
        if ( ( options = arguments[ i ] ) != null ) {
 
            // Extend the base object
            for ( name in options ) {
                src = target[ name ];
                copy = options[ name ];
 
                // Prevent never-ending loop
                if ( target === copy ) {
                    continue;
                }
 
                // Recurse if we're merging plain objects or arrays
                if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
                    ( copyIsArray = jQuery.isArray( copy ) ) ) ) {
 
                    if ( copyIsArray ) {
                        copyIsArray = false;
                        clone = src && jQuery.isArray( src ) ? src : [];
 
                    } else {
                        clone = src && jQuery.isPlainObject( src ) ? src : {};
                    }
 
                    // Never move original objects, clone them
                    target[ name ] = jQuery.extend( deep, clone, copy );
 
                // Don't bring in undefined values
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }
 
    // Return the modified object
    return target;
};

这篇文章主要是介绍js关于深拷贝的内容,其它的内容可以查看三水点靠木以前发表的文章

Javascript 相关文章推荐
JavaScript 格式字符串的应用
Mar 29 Javascript
jquery 图片截取工具jquery.imagecropper.js
Apr 09 Javascript
js 编码转换 gb2312 和 utf8 互转的2种方法
Aug 07 Javascript
JavaScript实现从数组中选出和等于固定值的n个数
Sep 03 Javascript
ie8模式下click无反应点击option无反应的解决方法
Oct 11 Javascript
JS实现图片产生波纹一样flash效果的方法
Feb 27 Javascript
jQuery实现的多级下拉菜单效果代码
Aug 24 Javascript
jquery把int类型转换成字符串类型的方法
Oct 07 Javascript
EasyUI创建人员树的实例代码
Sep 15 Javascript
在Swiper内如何制作CSS3动画效果示例代码
Dec 07 Javascript
vue配置nprogress实现页面顶部进度条
Sep 21 Javascript
JS前端canvas交互实现拖拽旋转及缩放示例
Aug 05 Javascript
详解JavaScript模块化开发
Dec 04 #Javascript
javascript 定时器工作原理分析
Dec 03 #Javascript
JavaScript 最佳实践:帮你提升代码质量
Dec 03 #Javascript
简单理解Vue条件渲染
Dec 03 #Javascript
学习vue.js条件渲染
Dec 03 #Javascript
浅谈jQuery中Ajax事件beforesend及各参数含义
Dec 03 #Javascript
jquery 判断div show的状态实例
Dec 03 #Javascript
You might like
彻底杜绝PHP的session cookie错误
2009/08/09 PHP
PHP基于ICU扩展intl快速实现汉字转拼音及按拼音首字母分组排序的方法
2017/05/03 PHP
关于document.cookie的使用javascript
2008/04/11 Javascript
from 表单提交返回值用post或者是get方法实现
2013/08/21 Javascript
JS实现模拟百度搜索“2012世界末日”网页地震撕裂效果代码
2015/10/31 Javascript
详解JavaScript中|单竖杠运算符的使用方法
2016/05/23 Javascript
javascript将中国数字格式转换成欧式数字格式的简单实例
2016/08/02 Javascript
node操作mysql数据库实例详解
2017/03/17 Javascript
javascript 的变量、作用域和内存问题
2017/04/19 Javascript
jQuery除指定区域外点击任何地方隐藏DIV功能
2017/11/13 jQuery
解析Angular 2+ 样式绑定方式
2018/01/15 Javascript
微信小程序实现聊天对话(文本、图片)功能
2018/07/06 Javascript
使用layer弹窗和layui表单实现新增功能
2018/08/09 Javascript
vue实现可视化可拖放的自定义表单的示例代码
2019/03/20 Javascript
微信小程序页面间跳转传参方式总结
2019/06/13 Javascript
使用 Vue 实现一个虚拟列表的方法
2019/08/20 Javascript
vue fetch中的.then()的正确使用方法
2020/04/17 Javascript
小程序富文本提取图片可放大缩小
2020/05/26 Javascript
Jquery cookie插件实现原理代码解析
2020/08/04 jQuery
浅析JavaScript预编译和暗示全局变量
2020/09/03 Javascript
js实现贪吃蛇游戏 canvas绘制地图
2020/09/09 Javascript
jQuery实现手风琴特效
2021/01/11 jQuery
Python实现SMTP发送邮件详细教程
2021/03/02 Python
python使用Pandas库提升项目的运行速度过程详解
2019/07/12 Python
详解Python对JSON中的特殊类型进行Encoder
2019/07/15 Python
关于python3.9安装wordcloud出错的问题及解决办法
2020/11/02 Python
使用before和:after伪类制作css3圆形按钮
2014/04/08 HTML / CSS
css3中背景尺寸background-size详解
2014/09/02 HTML / CSS
项目申报专员岗位职责
2014/07/09 职场文书
机关职员工作检讨书
2014/10/23 职场文书
导游词欢迎词
2015/02/02 职场文书
世界文化遗产导游词
2015/02/13 职场文书
大学生求职意向书
2015/05/11 职场文书
2016国培学习心得体会
2016/01/08 职场文书
总结一下关于在Java8中使用stream流踩过的一些坑
2021/06/24 Java/Android
html5调用摄像头实例代码
2021/06/28 HTML / CSS