JavaScriptは深くコピーした穴があります.
4998 ワード
前言
ある会社に面接に行った時、面接官が私に質問しました.当時私は心の中で密かに喜んでいましたが、こんな簡単な問題を考える必要がありますか?「普段よく使われているのは2つの方法があります.最初はJSON.parse(JSON.stingify(obj)を使って、2番目はfor…inを使って再帰的に完成します.」面接官はそれを聞いてうなずいて満足しています.その時は私もあまりこの問題を気にしていませんでした.この間までまたこの問題を思い出しました.上記の二つの方法は全部Bugがあります.
質問を発する
上記のBugは何ですか?特殊対象コピー まずこのような相手がいると考えてみましょう.普通のタイプを考えないで、次のようなメンバーがいます.
JSON法
また、
再帰する
結論
上記のテストにより、これらの2つの方法はいずれも関数をコピーできないことが分かりました.リング 環とは何ですか
環は対象の循環参照であり、自分が閉ループになることをもたらします.例えば、次のようなオブジェクトです.
ソリューションリング 1つの特殊対象のコピー この問題の解決は面倒くさいです.特別に扱うべき対象の種類が多すぎるので、MDN上の構造コピーを参考にして、解決ループの方案を結び付けました.
完全版はlodashの深コピーを見ることができます.関数のコピー MDN上の構造化コピーはまだ解決関数のコピーがありません.
今までは、関数を
コピー関数の増加関数の種類
結果をコピー
エラーの種類
後記
JavaScriptの深度コピーは上記のピットだけではなく、プロトタイプチェーン上の属性をどうやってコピーしますか?どのようにコピーしたら列挙できない属性ですか?
しかし、日常的にはJSON法を使うことを提案しています.この方法はほとんどの業務需要をカバーしていますので、簡単なことを複雑にする必要はありません.しかし、面接で面接官に会ったら、この問題に対する答えは絶対に彼の顔を見せられます.
ある会社に面接に行った時、面接官が私に質問しました.当時私は心の中で密かに喜んでいましたが、こんな簡単な問題を考える必要がありますか?「普段よく使われているのは2つの方法があります.最初はJSON.parse(JSON.stingify(obj)を使って、2番目はfor…inを使って再帰的に完成します.」面接官はそれを聞いてうなずいて満足しています.その時は私もあまりこの問題を気にしていませんでした.この間までまたこの問題を思い出しました.上記の二つの方法は全部Bugがあります.
質問を発する
上記のBugは何ですか?
const obj = {
arr: [111, 222],
obj: {key: ' '},
a: () => {console.log(' ')},
date: new Date(),
reg: / /ig
}
私たちは上の二つの方法でそれぞれ一回コピーします.JSON法
JSON.parse(JSON.stringify(obj))
出力結果:obj
の中の一般的なオブジェクトと配列はコピーできますが、date
のオブジェクトは文字列となり、関数は直接なくなり、正則は空のオブジェクトとなります.また、
for...in
の再帰方法を参照してください.再帰する
function isObj(obj) {
return (typeof obj === 'object' || typeof obj === 'function') && obj !== null
}
function deepCopy(obj) {
let tempObj = Array.isArray(obj) ? [] : {}
for(let key in obj) {
tempObj[key] = isObj(obj[key]) ? deepCopy(obj[key]) : obj[key]
}
return tempObj
}
結果:結論
上記のテストにより、これらの2つの方法はいずれも関数をコピーできないことが分かりました.
date
、reg
タイプのオブジェクトです.環は対象の循環参照であり、自分が閉ループになることをもたらします.例えば、次のようなオブジェクトです.
var a = {}
a.a = a
上の二つの方法を使ってコピーしたら、直接間違えます.ソリューション
WeakMap
を使用して、コピーされたオブジェクトを構造的に記憶してもよく、コピーするたびに、そのオブジェクトがコピーされているかどうかはWeakMap
に調べておき、コピーされているなら、そのオブジェクトを取り出して返し、deepCopy
関数を以下のように変換しても良い.
function deepCopy(obj, hash = new WeakMap()) {
if(hash.has(obj)) return hash.get(obj)
let cloneObj = Array.isArray(obj) ? [] : {}
hash.set(obj, cloneObj)
for (let key in obj) {
cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
}
return cloneObj
}
コピーリングの結果:
// date,reg ,
function deepCopy(obj, hash = new WeakMap()) {
let cloneObj
let Constructor = obj.constructor
switch(Constructor){
case RegExp:
cloneObj = new Constructor(obj)
break
case Date:
cloneObj = new Constructor(obj.getTime())
break
default:
if(hash.has(obj)) return hash.get(obj)
cloneObj = new Constructor()
hash.set(obj, cloneObj)
}
for (let key in obj) {
cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
}
return cloneObj
}
コピー結果:完全版はlodashの深コピーを見ることができます.
今までは、関数を
eval
の方法でコピーしたいだけですが、この方法は矢印関数のみ有効です.fun(){}
のような形であれば、エラーが発生します.コピー関数の増加関数の種類
結果をコピー
エラーの種類
後記
JavaScriptの深度コピーは上記のピットだけではなく、プロトタイプチェーン上の属性をどうやってコピーしますか?どのようにコピーしたら列挙できない属性ですか?
Error
オブジェクトなどのピットをコピーする方法は、ここではいちいち説明しない.しかし、日常的にはJSON法を使うことを提案しています.この方法はほとんどの業務需要をカバーしていますので、簡単なことを複雑にする必要はありません.しかし、面接で面接官に会ったら、この問題に対する答えは絶対に彼の顔を見せられます.