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 相关文章推荐
重定向实现代码
Nov 20 Javascript
jquery ajax请求方式与提示用户正在处理请稍等
Sep 01 Javascript
Javascript的闭包详解
Dec 26 Javascript
AngularJS中的$watch(),$digest()和$apply()区分
Apr 04 Javascript
对Angular.js Controller如何进行单元测试
Oct 25 Javascript
jquery实现焦点轮播效果
Feb 23 Javascript
微信小程序实现图片上传、删除和预览功能的方法
Dec 18 Javascript
详解Vue Elementui中的Tag与页面其它元素相互交互的两三事
Sep 25 Javascript
小程序数据通信方法大全(推荐)
Apr 15 Javascript
JS前端知识点offset,scroll,client,冒泡,事件对象的应用整理总结
Jun 27 Javascript
JavaScript中跨域问题的深入理解
Mar 04 Javascript
如何用JavaScipt测网速
May 09 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
Zerg兵种介绍
2020/03/14 星际争霸
什么是短波收听SWL
2021/03/01 无线电
PHP curl模拟浏览器采集阿里巴巴的实现代码
2011/04/20 PHP
php开发工具有哪五款
2015/11/09 PHP
详解PHP中mb_strpos的使用
2018/02/04 PHP
php创建类并调用的实例方法
2019/09/25 PHP
改进:论坛UBB代码自动插入方式
2006/12/22 Javascript
从JavaScript 到 JQuery (1)学习小结
2009/02/12 Javascript
js中设置元素class的三种方法小结
2011/08/28 Javascript
javascript设计模式之解释器模式详解
2014/06/05 Javascript
JavaScript中的函数模式详解
2015/02/11 Javascript
DOM操作一些常用的属性汇总
2015/03/13 Javascript
jQuery原理系列-css选择器的简单实现
2016/06/07 Javascript
angularjs实现首页轮播图效果
2017/04/14 Javascript
JS函数节流和函数防抖问题分析
2017/12/18 Javascript
Three.JS实现三维场景
2018/12/30 Javascript
jQuery实现移动端笔触canvas电子签名
2020/05/21 jQuery
JavaScript用document.write()输出换行的示例代码
2020/11/26 Javascript
[08:29]DOTA2每周TOP10 精彩击杀集锦vol.7
2014/06/25 DOTA
在Python中使用dict和set方法的教程
2015/04/27 Python
python 简单搭建阻塞式单进程,多进程,多线程服务的实例
2017/11/01 Python
使用python 和 lint 删除项目无用资源的方法
2017/12/20 Python
python获取文件真实链接的方法,针对于302返回码
2018/05/14 Python
Python适配器模式代码实现解析
2019/08/02 Python
python将图片转base64,实现前端显示
2020/01/09 Python
2021年值得向Python开发者推荐的VS Code扩展插件
2021/01/25 Python
使用 CSS3 中@media 实现网页自适应的示例代码
2020/03/24 HTML / CSS
浅谈基于Canvas的手绘风格图形库Rough.js
2018/03/19 HTML / CSS
Roots加拿大官网:加拿大休闲服饰品牌
2016/10/24 全球购物
一级方程式赛车官方网上商店:F1 Store(支持中文)
2018/01/12 全球购物
JBL加拿大官方商店:扬声器、耳机等
2020/10/23 全球购物
Python的两道面试题
2013/06/29 面试题
大学毕业生自荐书怎么写?
2014/01/06 职场文书
给老师的道歉信
2014/01/11 职场文书
庆祝儿童节标语
2014/10/09 职场文书
MySQL悲观锁与乐观锁的实现方案
2021/11/02 MySQL