和深コピーの実現
4880 ワード
javascriptの変数タイプ基本タイプ5種類の基本データタイプUdefined、Null、Boolean、Number、Stringは、変数は値によって保存され、スタックメモリに格納された簡単なデータセグメントで直接アクセスできます. 参照タイプがヒープメモリに格納されているオブジェクトで、変数はポインタで保存されています.参照タイプにアクセスする必要がある場合は、まずスタックからそのオブジェクトのアドレスポインタを取得し、メモリから必要なデータを取得する. JavaScriptの記憶対象は全部住所を保存しているので、コピーが浅いと、obj 1とobj 2は同じメモリアドレスを指すことになります.その一方の内容を変えたのは、元のメモリに修正を加えることによってコピー対象とソースオブジェクトが変更されます.深度コピーは新しいメモリアドレスを開いて、元のオブジェクトの各属性を一つずつコピーします.コピー対象とソースオブジェクトのそれぞれの動作には影響がありません.
浅いコピーを実現する方法簡単な引用コピー Obejct.assign()Object.assign()方法は、任意の複数のソースオブジェクト自身のエニュメレーション属性を対象オブジェクトにコピーし、対象オブジェクトに戻ることができる. Arayのsliceとconcat方法Arayのsliceとconcat方法は、元の配列を修正せずに、元の配列の要素を浅いコピーした新しい配列を返すだけです.深くコピーしたように見えます.実際には、浅いコピーです. JSONオブジェクトのパーとストリングスフリー jQuery.exted()jQueryのextedメソッドは基本的な再帰的な考え方を使って浅いコピーの和深コピーを実現しましたが、この方法はソースオブジェクト内部循環参照を処理することができません.例えば、 自分で手を下して を実現します.
浅いコピーを実現する方法
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
var obj1 = {a:1};
var obj2 = Object.assign({},obj1);
console.log(obj1 === obj2);
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
深度コピーの実現// 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になります.また、オブジェクトに循環参照がある場合も正確に処理できません.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;
}
}