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 相关文章推荐
jQuery 使用个人心得
Feb 26 Javascript
php+ajax+jquery实现点击加载更多内容
May 03 Javascript
Javascript之Number对象介绍
Jun 07 Javascript
javascript 闭包详解及简单实例应用
Dec 31 Javascript
javascript深拷贝的原理与实现方法分析
Apr 10 Javascript
JS中常用的消息框总结
Feb 24 Javascript
vue解决使用webpack打包后keep-alive不生效的方法
Sep 01 Javascript
vue2 v-model/v-text 中使用过滤器的方法示例
May 09 Javascript
JS数组降维的实现Array.prototype.concat.apply([], arr)
Apr 28 Javascript
VSCode 添加自定义注释的方法(附带红色警戒经典注释风格)
Aug 27 Javascript
js实现滚动条自动滚动
Dec 13 Javascript
三种方式清除vue路由跳转router-link的历史记录
Apr 10 Vue.js
详解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
PHP4 与 MySQL 数据库操作函数详解
2006/12/06 PHP
gd库图片下载类实现下载网页所有图片的php代码
2012/08/20 PHP
Laravel框架数据库CURD操作、连贯操作总结
2014/09/03 PHP
Nginx实现反向代理
2017/09/20 Servers
php变量与字符串的增删改查操作示例
2020/05/07 PHP
各种常用浏览器getBoundingClientRect的解析
2009/05/21 Javascript
网页中的图片的处理方法与代码
2009/11/26 Javascript
JavaScript 数组运用实现代码
2010/04/13 Javascript
Tab切换组件(选项卡功能)实例代码
2013/11/21 Javascript
详解js中构造流程图的核心技术JsPlumb
2015/12/08 Javascript
javascript中Date format(js日期格式化)方法小结
2015/12/17 Javascript
微信小程序 开发工具快捷键整理
2016/10/31 Javascript
js 判断登录界面的账号密码是否为空
2017/02/08 Javascript
详解使用React进行组件库开发
2018/02/06 Javascript
p5.js入门教程之鼠标交互的示例
2018/03/16 Javascript
微信小程序支付PHP代码
2018/08/23 Javascript
angular 未登录状态拦截路由跳转的方法
2018/10/09 Javascript
Vue.extend实现挂载到实例上的方法
2019/05/01 Javascript
JS实现简易图片自动轮播
2020/10/16 Javascript
vue+Element-ui前端实现分页效果
2020/11/15 Javascript
python OpenCV学习笔记直方图反向投影的实现
2018/02/07 Python
Python实现读写INI配置文件的方法示例
2018/06/09 Python
Python IDLE清空窗口的实例
2018/06/25 Python
python3 下载网络图片代码实例
2019/08/27 Python
使用pycharm和pylint检查python代码规范操作
2020/06/09 Python
基于CSS3实现的黑色个性导航菜单效果
2015/09/14 HTML / CSS
浅析HTML5:'data-'属性的作用
2018/01/23 HTML / CSS
夏尔巴人登珠峰品牌:Sherpa Adventure Gear
2018/02/08 全球购物
购买英国原创艺术:Art Gallery
2018/08/25 全球购物
某IT外企面试题-二分法求方程!看看大家的C++功底
2015/07/04 面试题
本科生求职简历的自我评价
2013/10/21 职场文书
教师文明餐桌光盘行动倡议书
2015/04/28 职场文书
格林童话读书笔记
2015/06/30 职场文书
2016大学生党校学习心得体会
2016/01/06 职场文书
html5移动端禁止长按图片保存的实现
2021/04/20 HTML / CSS
python常见的占位符总结及用法
2021/07/02 Python