美文网首页我爱编程
浅拷贝和深拷贝的实现

浅拷贝和深拷贝的实现

作者: 九又四分之三o | 来源:发表于2018-03-25 17:19 被阅读0次

    javascript的变量类型

    • 基本类型
      5种基本数据类型Undefined、Null、Boolean、Number 和 String,变量是按值存放的,存放在栈内存中的简单数据段,可以直接访问。
    • 引用类型
      存放在堆内存中的对象,变量保存的是一个指针。当需要访问引用类型时,首先从栈中获取该对象的地址指针,然后从堆内存中获取所需的数据。

    JavaScript存储对象都是存地址的,所以浅拷贝会导致 obj1 和obj2 指向同一块内存地址。改变了其中一方的内容,都是在原来的内存上做修改会导致拷贝对象和源对象都发生改变,而深拷贝是开辟一块新的内存地址,将原对象的各个属性逐个复制进去。对拷贝对象和源对象各自的操作互不影响。

    实现浅拷贝的方法

    • 简单的引用复制
    function shallowClone(copyObj) {
      var obj = {};
      for ( var i in copyObj) {
        obj[i] = copyObj[i];
      }
      return obj;
    }
    var x = {
      a: 1,
      b: { f: { g: 1 } },
      c: [ 1, 2, 3 ]
    };
    var y = shallowClone(x);
    console.log(y.b.f === x.b.f);     // true
    
    • Obejct.assign()
      Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
        var obj1 = {a:1};
        var obj2 = Object.assign({},obj1);
        console.log(obj1 === obj2);
    
    • Array的slice和concat方法
      Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。看起来像是深拷贝。而实际上它是浅拷贝
    var array = [1,2,3]; 
    var array_shallow = array; 
    var array_concat = array.concat(); 
    var array_slice = array.slice(0); 
    console.log(array === array_shallow); //true 
    console.log(array === array_slice); //false,“看起来”像深拷贝
    console.log(array === array_concat); //false,“看起来”像深拷贝
    

    可以看出,slice和concat返回的是不同的数组实例,这与直接的引用复制是不同的。但是从下面的例子可以看出,slice和concat并没有实现深复制,数组中的对象元素只是复制了引用。

    var array = [1, [1,2,3], {name:"array"}]; 
    var array_concat = array.concat();
    var array_slice = array.slice(0);
    array_concat[1][0] = 5;  //改变array_concat中数组元素的值 
    console.log(array[1]); //[5,2,3] 
    console.log(array_slice[1]); //[5,2,3] 
    array_slice[2].name = "array_slice"; //改变array_slice中对象元素的值 
    console.log(array[2].name); //array_slice
    console.log(array_concat[2].name); //array_slice
    

    深拷贝的实现

    • JSON对象的parse和stringify
    //例1
    var source = { name:"source", child:{ name:"child" } } 
    var target = JSON.parse(JSON.stringify(source));
    target.name = "target";  //改变target的name属性
    console.log(source.name); //source 
    console.log(target.name); //target
    target.child.name = "target child"; //改变target的child 
    console.log(source.child.name); //child 
    console.log(target.child.name); //target child
    //例2
    var source = { name:function(){console.log(1);}, child:{ name:"child" } } 
    var target = JSON.parse(JSON.stringify(source));
    console.log(target.name); //undefined
    //例3
    var source = { name:function(){console.log(1);}, child:new RegExp("e") }
    var target = JSON.parse(JSON.stringify(source));
    console.log(target.name); //undefined
    console.log(target.child); //Object {}
    

    这种方法使用较为简单,可以满足基本的深拷贝需求,而且能够处理JSON格式能表示的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)。还有一点不好的地方是它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。同时如果对象中存在循环引用的情况也无法正确处理。

    • jQuery.extend()
      jQuery的extend方法使用基本的递归思路实现了浅拷贝和深拷贝,但是这个方法也无法处理源对象内部循环引用,例如:
    var a = {"name":"aaa"};
    var b = {"name":"bbb"};
    a.child = b;
    b.parent = a;
    $.extend(true,{},a);//直接报了栈溢出。Uncaught RangeError: Maximum call stack size exceeded
    
    • 自己动手实现
    (function ($) {
        'use strict';
    
        var types = 'Array Object String Date RegExp Function Boolean Number Null Undefined'.split(' ');
    
        function type () {
           return Object.prototype.toString.call(this).slice(8, -1);
        }
    
        for (var i = types.length; i--;) {
            $['is' + types[i]] = (function (self) {
                return function (elem) {
                   return type.call(elem) === self;
                };
            })(types[i]);
        }
    
        return $;
    })(window.$ || (window.$ = {}));//类型判断
    
    function copy (obj,deep) { 
        if (obj === null || (typeof obj !== "object" && !$.isFunction(obj))) { 
            return obj; 
        } 
    
        if ($.isFunction(obj)) {
            return new Function("return " + obj.toString())();
        }
        else {
            var name, target = $.isArray(obj) ? [] : {}, value; 
    
            for (name in obj) { 
                value = obj[name]; 
    
                if (value === obj) {
                    continue;
                }
    
                if (deep) {
                    if ($.isArray(value) || $.isObject(value)) {
                        target[name] = copy(value,deep);
                    } else if ($.isFunction(value)) {
                        target[name] = new Function("return " + value.toString())();
                    } else {
                        target[name] = value;
                    } 
                } else {
                    target[name] = value;
                } 
            } 
            return target;
        }         
    }
    

    相关文章

      网友评论

        本文标题:浅拷贝和深拷贝的实现

        本文链接:https://www.haomeiwen.com/subject/bcvhcftx.html