面接経典の深浅コピーとクローン関数
15547 ワード
面接でよくクローン関数の実現を聞かれますが、この記事ではクローン関数の簡単な実現とJavaScriptの深浅コピーを紹介します.
まず、JavaScriptの中の深浅コピーを調べてみます.
アレイコピー
配列のコピーをより全面的に理解するために、次の例として使用する不思議な配列を突っ込まないでください.
コンセプト:行列の最初の基本的なデータタイプのデータのみをコピーすることができます.配列内部の参照タイプデータの参照関係は切断できません.
Aray.slice()とアラy.co ncat()
s 6拡張演算子
es 6 Object.assign()
コピー
コンセプト:行列内の引用タイプの引用関係を徹底的に遮断しました.
JSON.parse()とJSON.strigify()
オブジェクトコピー
対照配列のコピーを容易にするためには、対象コピーの例でも不思議な対象を使用します.
コンセプト:オブジェクト中の基本データタイプデータのみをコピーすることができ、オブジェクト内部の参照タイプデータの参照関係を切断することができません.
es 6 Object.assign()
コピー
コンセプト:対象内の引用タイプの引用関係を徹底的に遮断しました.
JSON.parse()とJSON.strigify()
クローン関数
これらのコピー技術の分析を通して、比較的に完全な深度コピーはないことを発見しました.行列やオブジェクトの深度コピーはどうやって実現しますか?次に簡単なクローン関数を書き,配列と物体の深いコピーを実現する.
まず、JavaScriptの中の深浅コピーを調べてみます.
アレイコピー
配列のコピーをより全面的に理解するために、次の例として使用する不思議な配列を突っ込まないでください.
var source = [1, null, undefined, {name: 'anjou', age: undefined, sex: null}, [2, 3, 4], function() {}]
浅いコピーコンセプト:行列の最初の基本的なデータタイプのデータのみをコピーすることができます.配列内部の参照タイプデータの参照関係は切断できません.
Aray.slice()とアラy.co ncat()
var target = source.slice() || source.concat()
target[0] = 5
target[3].name = 'anpoly'
target[4][0] = 6
console.log(source) // [1, null, undefined, {name: 'anpoly', age: undefined, sex: null}, [6, 3, 4], ƒ ()]
console.log(target) // [5, null, undefined, {name: 'anpoly', age: undefined, sex: null}, [6, 3, 4], ƒ ()]
二つの配列はarr[0]だけの値で異なることがわかった.結論:Aray.sliceとAray.co ncat方法は既存の配列を変更することなく、データを新しい配列に浅いコピーする.配列内の参照タイプデータは依然として参照関係を維持しています.s 6拡張演算子
var target = [...source]
target[0] = 5
target[3].name = 'anpoly'
target[4][0] = 6
console.log(source) // [1, null, undefined, {name: 'anpoly', age: undefined, sex: null}, [6, 3, 4], ƒ ()]
console.log(target) // [5, null, undefined, {name: 'anpoly', age: undefined, sex: null}, [6, 3, 4], ƒ ()]
結果は最初の方法と同じであるので、拡張演算子を用いて配列の浅いコピーを実現することもできる.es 6 Object.assign()
var proto = Object.getPrototypeOf(source)
var target = Object.assign({}, Object.create(proto), source)
target[0] = 5
target[3].name = 'anpoly'
target[4][0] = 6
console.log(source) // [1, null, undefined, {name: 'anpoly', age: undefined, sex: null}, [6, 3, 4], ƒ ()]
console.log(target) // [5, null, undefined, {name: 'anpoly', age: undefined, sex: null}, [6, 3, 4], ƒ ()]
結果は最初の方法と同じで、Object.assign法を使っても配列内部参照データタイプの参照関係を切断できない.コピー
コンセプト:行列内の引用タイプの引用関係を徹底的に遮断しました.
JSON.parse()とJSON.strigify()
var target = JSON.parse(JSON.stringify(source))
target['0'] = 5
target['3'].name = 'anpoly'
target['4'][0] = 8
console.log(source) // [1, null, undefined, {name: 'anjou', age: undefined, sex: null}, [2, 3, 4], ƒ ()]
console.log(target) // [5, null, null, {name: 'anpoly', sex: null}, [6, 3, 4], null]
上記の運転結果により、JSON.parseとJSON.strigifyを使って配列の深度コピーが可能であることを発見しました.コピーした配列は予想される結果とは大きく違っていることが分かりました.これはJSON.stringify(...)が対象の中でundefined、function、smbolに出会うと自動的に無視され、配列の中でnullに戻ります.オブジェクトコピー
対照配列のコピーを容易にするためには、対象コピーの例でも不思議な対象を使用します.
var source2 = {0:1, 1:null, 2:undefined, 3:{name: 'anjou', age: undefined, sex: null}, 4:[2, 3, 4], 5: function() {}}
浅いコピーコンセプト:オブジェクト中の基本データタイプデータのみをコピーすることができ、オブジェクト内部の参照タイプデータの参照関係を切断することができません.
es 6 Object.assign()
var obj = Object.getPrototypeOf(source2)
var target = Object.assign({}, Object.create(obj), source2)
target['0'] = 5
target['3'].name = 'anpoly'
target['4'][0] = 6
console.log(source2) // {0: 1, 1: null, 2: undefined, 3: {name: "anpoly", age: undefined, sex: null}, 4: [6, 3, 4], 5:
ƒ ()}
console.log(target) // {0: 5, 1: null, 2: undefined, 3: {name: "anpoly", age: undefined, sex: null}, 4: [6, 3, 4], 5:
ƒ ()}
結果は配列と似ていますが、オブジェクトの深度コピーも実現できません.コピー
コンセプト:対象内の引用タイプの引用関係を徹底的に遮断しました.
JSON.parse()とJSON.strigify()
var target = JSON.parse(JSON.stringify(source2))
target['0'] = 5
target['3'].name = 'anpoly'
target['4'][0] = 6
console.log(source2) // {0: 1, 1: null, 2: undefined, 3: {name: "anjou", age: undefined, sex: null}, 4: [2, 3, 4], 5:
ƒ ()}
console.log(target) // {0: 1, 1: null, 3: {name: "anpoly", sex: null}, 4: [6, 3, 4]}
オブジェクトの深さクローンは可能ですが、不安定なJSON値はコピーできません.原因は配列コピーにおいてすでに述べられており、ここではもはや説明しない.クローン関数
これらのコピー技術の分析を通して、比較的に完全な深度コピーはないことを発見しました.行列やオブジェクトの深度コピーはどうやって実現しますか?次に簡単なクローン関数を書き,配列と物体の深いコピーを実現する.
//
function deepCopy(data) {
if (typeof data !== 'object' || data == null) return data
var newData = data instanceof Array ? [] : {}
for (var key in data) {
newData[key] = typeof data[key] === 'object' ? (data[key] === null ? null : deepCopy(data[key])) : data[key]
}
return newData
}
var target = deepCopy(source)
target[0] = 5
target[3].name = 'anpoly'
target[4][0] = 6
console.log(source) // [1, null, undefined, {name: 'anjou', age: undefined, sex: null}, [2, 3, 4], ƒ ()]
console.log(target) // [5, null, undefined, {name: 'anpoly', age: undefined, sex: null}, [6, 3, 4], ƒ ()]
このようにすれば、私たちの予想に達することができますが、再帰的にクローン関数を呼び出すと、一定の性能問題が生じるので、実際の開発では実際の状況に応じて選択します.