回答

收藏

如何正确克隆 JavaScript 对象?

技术问答 技术问答 316 人阅读 | 0 人回复 | 2023-09-11

我有一个对象x。我想把它复制成 object y,以便更改为y不修改x。我意识到复制从内置 JavaScript 对象衍生的对象会导致不必要的属性。
, L' O4 f6 J! \8 y3 V如何正确克隆 JavaScript 对象?
$ K; O$ W- k: f' l" k) p$ ]$ m$ O* Z                                                                1 t3 L# M$ O4 V' o
    解决方案:                                                               
8 Z7 C3 _  y  [8 p5 `                                                                对 JavaScript 中的任何对象都不会简单或直接执行此操作。您将遇到从对象原型中错误获取属性的问题。这些属性应该留在原型中,而不是复制到新的例子中。例如,如果你想在 中添加一个clone方法Object.prototype,正如一些答案所描述的那样,你需要显式跳过属性。Object.prototype但是,如果你不知道添加其他附加方法或其他中间原型呢?在这种情况下,您将复制不应该复制的属性,因此您需要使用该方法来检测不可预见的非本地属性hasOwnProperty。& _: u) P  c& l! ^- F1 z0 F; O
除了无数的属性,当你试图复制具有隐藏属性的对象时,你会遇到更困难的问题。prototype是函数的隐藏属性。此外,对象的原型由属性引用,该属性__proto__ 也是隐藏的,不会被迭代源对象属性的 for/in 循环复制__proto__可能是 Firefox 的 JavaScript 解释器在其他浏览器中在其他浏览器中可能有所不同,但你知道。并非所有的东西都可以列举。如果你知道它的名字,你可以复制一个隐藏的属性,但我不知道如何自动找到它。
; x5 v8 O4 Q9 T3 C1 f! x7 R) ^另一个寻求优雅解决方案的障碍是正确设置原型继承。如果您的源对象的原型是Object,然后只需创建一个新的通用对象{},但如果源的原型是 的后代Object,然后你就会失使用hasOwnProperty过滤器跳过的原型中的其他成员,或者在原型中,但一开始就无法枚举。一种解决方案可能是调用源对象的constructor获得初始复制对象,然后复制属性,但您仍然不会获得无数属性。例如,一个Date对象将其数据存储为隐藏成员:
( E7 o/ ^% p% v! O# G7 v! L
    / C+ x  q. T3 t6 B1 c7 O
  • function clone(obj)    if (null == obj || "object" != typeof obj) return obj;    var copy = obj.constructor();     for (var attr in obj)        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];   }    return copy;}var d1 = new Date();/* Executes function after 5 seconds. */setTimeout(function(){     var d2 = clone(d一、    alert("d1 = "   d1.toString()   "\nd2 = "   d2.toString(),5000)code]日期字符串d1将比 日期字符串晚 5 秒d2.一一个Date同样的方法是调用它setTime但这是特定的Date类别。我认为没有万无一失的通用解决方案,尽管我愿意犯错误!
    4 U: P: N$ g" W4 z
  • 当我不得不实现一般的深度复制时,我终于妥协了,假设我只需要复制一个普通的Object,Array,Date,String,Number,或Boolean. 最后 3 的类型是不可改变的,所以我可以执行浅拷贝而不用担心它会改变。我进一步假设它包含在内Object或Array也将是列表中 6 简单类型之一的任何元素。这可以通过以下代码来完成:
    ; c: c3 Z5 |% b0 T6 L0 p" U& O
  • [code]function clone(obj)    var copy;    // Handle the 3 simple types,and null or undefined    if (null == obj || "object" != typeof obj) return obj;    // Handle Date    if (obj instanceof Date)        copy = new Date();;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;copy.setTime(obj.getTime();;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;return copy;    }    // Handle Array    if (obj instanceof Array)        copy = for (var i = 0,len = obj.length; i 只要对象和数组中的数据形成树结构,上述函数就可以完全适用于我提到的 6 简单类型。换句话说,对象中不超过一个引用相同数据。[code]// This would be cloneable:var tree =    "left"  : "left" : null,"right" : null,"data" "right" : null,   "data"  : 8}This would kind-of work,but you would get 2 copies of the // inner node instead of 2 references to the same copyvar directedAcylicGraph =    "left"  : "left" : null,"right" : null,"data" "data" : 8};directedAcyclicGraph["right"] = directedAcyclicGraph["left"];// Cloning this would cause a stack overflow due to infinite recursion:var cyclicGraph =    "left"  : "left" : null,"right" : null,"data" "data" : 8};cyclicGraph["right"] = cyclicGraph;
    1 A2 X0 z, V  e9 v5 q) I
它不能处理任何 JavaScript 对象,但它可能足以满足很多目的,只要你不认为它只适用于你扔给它的任何东西。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则