【问题标题】:Javascript Clone throw Type ErrorJavascript克隆抛出类型错误
【发布时间】:2013-05-30 05:56:22
【问题描述】:

我正在用 javascript 编写我的克隆函数。

该函数递归地克隆对象(避免循环引用)并且看起来运行良好,但如果对象(在某个级别)具有内置对象引用,当我尝试访问克隆对象的某些属性时(看起来正确)我有一个类型错误。

这是我使用日期时间的简单示例(我知道克隆日期时间对象有更有效的方法,但这不是我关心的问题,我需要做的是克隆一个通用的构建对象,日期时间只是一个示例)我真正的克隆方法是只有浅拷贝函数和数组但深拷贝对象(内部数组和函数除外)而不避免循环引用的方法。 这只是错误的一个展示(我使用完整的克隆函数获得的错误相同)。

代码:


        var date = new Date () ;
        var dateProto = date.__proto__ ;

        var cloned = {} ;
        var clonedProto = {} ;

        function clone ( obj )
        {
            if ( obj instanceof Array )
                return [] ;

            if ( obj instanceof Function )
                return obj ;

            if ( obj instanceof Object )
            {
                var result = {} ;
                var elems = Object.getOwnPropertyNames(obj) ;
                var len = elems.length ;
                for ( var i = 0 ; i < len ; i++  )
                {
                    var prop = elems[i] ;
                    var elem = obj[prop] ;

                    result [ prop ] = clone ( elem ) ;
                }

                return result ;
            }

            return obj ;
        }

        cloned = clone ( date ) ;
        clonedProto = clone ( dateProto ) ;

        cloned.__proto__ = clonedProto ;

        alert ( cloned.getDay() );

但这会在尝试访问 getDay 方法时导致此类型错误: 未捕获的 TypeError:这不是 Date 对象。

但我仍然不明白为什么,克隆看起来像日期对象,我知道方法是共享的,我期待调用时出现奇怪的行为(方法引用名为“日期”的对象,所以对象内部状态是“日期”不是“克隆”)但不是错误。

那么为什么会出现这个错误呢?

感谢您的帮助,抱歉我的英语不好。


编辑

根据向我建议的新想法(来自 RobG 和 jfriend00 在评论中发布的表格文章),我以这种方式重写了克隆函数。

                    function clone ( obj )
        {
            if ( obj instanceof Array )
                return [] ;

            if ( obj instanceof Function )
                return obj ;

            if ( obj instanceof Object )
            {
                var result = new obj.constructor() ;

                result.__proto__ = clone ( obj.__proto__ ) ;

                var elems = Object.getOwnPropertyNames(obj) ;
                var len = elems.length ;
                for ( var i = 0 ; i < len ; i++  )
                {
                    var prop = elems[i] ;
                    var elem = obj[prop] ;

                    result [ prop ] = clone ( elem ) ;
                }

                return result ;
            }

            return obj ;
        }

现在似乎按我的预期工作,但我不明白为什么这段代码: var result = new obj.constructor() ; 有所作为。

非常感谢您的帮助。

【问题讨论】:

  • 要克隆一个 Date 对象,您需要使用 Date 构造函数(注意所有对象都是 Object 的实例,包括函数和数组)。这可以使用var cloneDate = new Date(+originalDate); 来完成,除非您直接向 originalDate 对象添加了一些其他属性。可以使用您的 for..in 策略在自己的属性上复制它们。
  • @RobG 你给了我一个想法,动态地检索我要克隆的每个对象的构造函数并递归地克隆原型。如果我这样做,它会起作用,我想原型似乎完全克隆了。但我不明白为什么以及它是否正确。能给我解释一下吗?
  • 构造函数属性可以修改(重新赋值),所以如果你使用它,你只希望它指向正确的对象。如果您创建一个新实例,那么它已经拥有“正确的”[[Prototype]]。剩下的就是复制原件自己的属性。但这不会克隆可能已使用闭包添加的“私有”成员。因此,不可能创建适用于每种情况的通用“克隆”函数,但是您的方法将在受限上下文中工作(构造函数是正确的,没有私有属性,没有用户定义的不可枚举的自己的属性等)。跨度>
  • 非常感谢 RobG 和 jfriend00 的帮助。我想我现在已经解决(并理解)我的问题了^_^

标签: javascript clone typeerror


【解决方案1】:

当您的对象是除了普通对象(如 Date 对象)之外的其他类型的对象时,您不是在创建克隆的 Date 对象,而是在创建普通对象。

此外,当您将属性复制到克隆对象时,您只是直接在对象上复制属性,而不是原型链中的属性,因为这是 Object.getOwnpPropertyNames() 返回的内容。因此,您克隆的 Date 对象没有任何 Date 方法。

根据 RobG 的建议,在 return 之前添加第一行:

result.__proto__ = obj.__proto__;
return result ;

【讨论】:

  • 是的,OP 正在使用 clone 函数来复制原型的属性(使用非标准的 __proto__),这些属性是不可枚举的。我以为他会做clone.__proto__ = obj.__proto__
  • 我知道我的结果不是一个正确的 Date 对象,而是像其中的一个(对我来说已经足够了)。
  • 我知道我的结果不是正确的 Date 对象,而是像其中的一个(对我来说已经足够了)。我已经在这个例子和实际案例中调试了对象(其中一个更复杂的函数也在对象树和原型中导航)并且克隆的对象显示了与原始 Date 对象相同的模式。如果您在示例中看到(为了不使递归过于复杂),我已经明确克隆了 Date 对象的原型并将其分配给 Date 的克隆。因此,在调试器中,我的克隆看起来完全(模式)等于原始,但不起作用。 (抱歉重复发帖)
  • @RobG 我知道克隆 Date 对象有更好的方法,但这只是一个例子。在实际情况下,我要克隆的对象可能具有 RegExp、Date、Error 或任何其他内置对象的某些属性;而且我需要能够复制所有这些(所以我需要一个通用方法来克隆内置对象)。
  • @Raji - 您是否尝试过 Rob 分配原型的建议?有数百篇关于克隆 JS 对象的体面方法的文章。这是one
猜你喜欢
  • 2018-12-06
  • 2022-01-11
  • 2015-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-13
  • 2015-05-03
  • 2011-06-29
相关资源
最近更新 更多