首页 > web前端 > js教程 > JavaScript 中对象的深拷贝

JavaScript 中对象的深拷贝

黄舟
发布: 2017-02-21 12:00:33
原创
1198 人浏览过



在JavaScript中,对对象进行拷贝的场景比较常见。但是简单的复制语句只能对对象进行浅拷贝,即复制的是一份引用,而不是它所引用的对象。而更多的时候,我们希望对对象进行深拷贝,避免原始对象被无意修改。

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

  • 浅拷贝:仅仅复制对象的引用,而不是对象本身;

  • 深拷贝:把复制的对象所引用的全部对象都复制一遍。

一. 浅拷贝的实现

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

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

1

2

3

4

5

6

7

8

/* ================ 浅拷贝 ================ */

function simpleClone(initalObj) {

    var obj = {};

    for ( var i in initalObj) {

        obj[i] = initalObj[i];

    }

    return obj;

}

登录后复制

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

/* ================ 客户端调用 ================ */

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() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

1

2

3

4

5

6

7

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():

1

2

3

4

5

6

7

8

/* ================ 深拷贝 ================ */

function deepClone(initalObj) {

    var obj = {};

    try {

        obj = JSON.parse(JSON.stringify(initalObj));

    }

    return obj;

}

登录后复制

1

2

3

4

5

6

7

8

9

10

11

/* ================ 客户端调用 ================ */

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 方法二:递归拷贝

代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

/* ================ 深拷贝 ================ */

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;

}

登录后复制

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

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

改进版代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

/* ================ 深拷贝 ================ */

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),可以达到深拷贝的效果。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

/* ================ 深拷贝 ================ */

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()也实现了对象的深拷贝。下面将官方代码贴出来,以供参考。

官方链接地址:http://www.php.cn/。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

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&#39;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&#39;t bring in undefined values

                } else if ( copy !== undefined ) {

                    target[ name ] = copy;

                }

            }

        }

    }

 

    // Return the modified object

    return target;

};

登录后复制


以上就是JavaScript 中对象的深拷贝的内容,更多相关内容请关注PHP中文网(www.php.cn)!






相关标签:
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
怎么实现 JavaScript点与圆的位置关系
来自于 1970-01-01 08:00:00
0
0
0
JavaScript钩子函数是什么?
来自于 1970-01-01 08:00:00
0
0
0
c++ 调用javascript
来自于 1970-01-01 08:00:00
0
0
0
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板