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 相关文章推荐
extjs fckeditor集成代码
May 10 Javascript
JavaScript字符串对象charAt方法入门实例(用于取得指定位置的字符)
Oct 17 Javascript
jQuery数据缓存用法分析
Feb 20 Javascript
js实现的鼠标滚轮滚动切换页面效果(类似360默认页面滚动切换效果)
Jan 27 Javascript
基于jQuery下拉选择框插件支持单选多选功能代码
Jun 07 Javascript
Angular2 (RC4) 路由与导航详解
Sep 21 Javascript
微信小程序 教程之小程序配置
Oct 17 Javascript
bootstrap是什么_动力节点Java学院整理
Jul 14 Javascript
在vue中读取本地Json文件的方法
Sep 06 Javascript
使用uni-app开发微信小程序的实现
Dec 13 Javascript
解决vue中的无限循环问题
Jul 27 Javascript
JavaScript Blob对象原理及用法详解
Oct 14 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
DIY实用性框形天线
2021/03/02 无线电
Php Mssql操作简单封装支持存储过程
2009/12/11 PHP
PHP Socket 编程
2010/04/09 PHP
php异常处理使用示例
2014/02/25 PHP
PHP连接MySQL的2种方法小结以及防止乱码
2014/03/11 PHP
JavaScript 通过模式匹配实现重载
2010/08/12 Javascript
jQuery实现的在线答题功能
2015/04/12 Javascript
ionic 上拉菜单(ActionSheet)实例代码
2016/06/06 Javascript
js无提示关闭浏览器窗口的两种方法分析
2016/11/06 Javascript
jQuery图片切换动画效果
2017/02/28 Javascript
Vue开发中整合axios的文件整理
2017/04/29 Javascript
JS判断非空至少输入两个字符的简单实现方法
2017/06/23 Javascript
vue项目中使用axios上传图片等文件操作
2017/11/02 Javascript
vue按需加载组件webpack require.ensure的方法
2017/12/13 Javascript
jQuery实现图片简单轮播功能示例
2018/08/13 jQuery
浅谈Vuex注入Vue生命周期的过程
2019/05/20 Javascript
解决ant Design Search无法输入内容的问题
2020/10/29 Javascript
使用jquery实现轮播图效果
2021/01/02 jQuery
Windows下用py2exe将Python程序打包成exe程序的教程
2015/04/08 Python
python3使用PyMysql连接mysql数据库实例
2017/02/07 Python
简单谈谈Python中的元祖(Tuple)和字典(Dict)
2017/04/21 Python
Python实现将数据框数据写入mongodb及mysql数据库的方法
2018/04/02 Python
python读取word文档,插入mysql数据库的示例代码
2018/11/07 Python
python浪漫表白源码
2019/04/05 Python
python中类的输出或类的实例输出为这种形式的原因
2019/08/12 Python
使用python模拟命令行终端的示例
2019/08/13 Python
详解PyTorch中Tensor的高阶操作
2019/08/18 Python
Python搭建代理IP池实现存储IP的方法
2019/10/27 Python
Python pip使用超时问题解决方案
2020/08/03 Python
Python3.8安装Pygame教程步骤详解
2020/08/14 Python
使用PDF.JS插件在HTML中预览PDF文件的方法
2018/08/29 HTML / CSS
为数据库创建索引都需要注意些什么
2012/07/17 面试题
应用心理学个人求职信范文
2013/12/11 职场文书
表彰先进的通报
2014/01/31 职场文书
优秀大学生事迹材料
2014/12/24 职场文书
企业法人任命书
2015/09/21 职场文书