和深コピーの実現

4880 ワード

javascriptの変数タイプ
  • 基本タイプ5種類の基本データタイプUdefined、Null、Boolean、Number、Stringは、変数は値によって保存され、スタックメモリに格納された簡単なデータセグメントで直接アクセスできます.
  • 参照タイプがヒープメモリに格納されているオブジェクトで、変数はポインタで保存されています.参照タイプにアクセスする必要がある場合は、まずスタックからそのオブジェクトのアドレスポインタを取得し、メモリから必要なデータを取得する.
  • JavaScriptの記憶対象は全部住所を保存しているので、コピーが浅いと、obj 1とobj 2は同じメモリアドレスを指すことになります.その一方の内容を変えたのは、元のメモリに修正を加えることによってコピー対象とソースオブジェクトが変更されます.深度コピーは新しいメモリアドレスを開いて、元のオブジェクトの各属性を一つずつコピーします.コピー対象とソースオブジェクトのそれぞれの動作には影響がありません.
    浅いコピーを実現する方法
  • 簡単な引用コピー
  • 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);
    
  • Arayのsliceとconcat方法Arayの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オブジェクトのパーとストリングスフリー
  • // 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フォーマットで表現できるすべてのデータタイプを処理することができますが、正規表現タイプ、関数タイプなどに対しては、深くコピーすることができません.もう一つの悪いところは、相手を捨てるコンストラです.つまり、深くコピーした後、このオブジェクトの元の構造関数が何であろうと、深くコピーしたらObjectになります.また、オブジェクトに循環参照がある場合も正確に処理できません.
  • jQuery.exted()jQueryのextedメソッドは基本的な再帰的な考え方を使って浅いコピーの和深コピーを実現しましたが、この方法はソースオブジェクト内部循環参照を処理することができません.例えば、
  • 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;
        }         
    }