代码之家  ›  专栏  ›  技术社区  ›  Stanislav Müller

克隆JavaScript对象。再次:(

  •  1
  • Stanislav Müller  · 技术社区  · 15 年前

    我知道再次读这个话题真的很烦人。在您开始深入研究代码之前,一个解决方案可能是我没有JavaScript中的原型和对象。但在这一点上,我想,我是的。


    如何克隆一个JavaScript类(使用原型创建),以便在以后扩展和执行时克隆的类保持不变?

    function clone(obj){
      if(obj == null || typeof(obj) != 'object')
          return obj;
    
      var temp = new obj.constructor();
      for(var key in obj)
          temp[key] = clone(obj[key]);
    
      return temp;
    }
    
    var FOO = function() {
      var myBAR = clone(BAR);
      myBAR.prototype = jQuery.extend(true, myBAR.prototype, this); // deep cloning twice and extending with this
      console.log("FOO:", this.name);
      new myBAR();
    };
    
    FOO.prototype = {
      name: "FOO"
    };
    
    var BAR = function() {
      console.log("BAR:", this.name);
    };
    
    BAR.prototype = {
      name: "BAR"
    };
    
    new FOO(); // returns FOO: FOO and BAR: FOO 
    new BAR(); // returns BAR: FOO should return BAR: BAR
    

    如果我没弄错的话 new BAR() (之后 new FOO() )应该回来 BAR: BAR BAR: FOO

    clone 功能如下:

    function clone(obj) {
      return eval("("+obj.toString()+")"); // the same as eval(uneval(obj));
    }
    

    但是这种方法有一个很大的缺点,即不能传递任何动态创建的对象。

    5 回复  |  直到 15 年前
        1
  •  1
  •   SolutionYogi Eric Lippert    15 年前

    问题在于如何克隆“原型”

    下一行

    myBAR.prototype = jQuery.extend(true, myBAR.prototype, this); // deep cloning 
    

    您不仅克隆了“原型”,还克隆了“名称”属性。

    myBAR.prototype = jQuery.extend(true, myBAR.prototype, this.prototype); // deep cloning 
    

    您的代码现在将返回

    new FOO(); // returns FOO:FOO and BAR:BAR
    new BAR(); // returns BAR:BAR
    
        2
  •  0
  •   Alex    15 年前

    我有以下的目标:

    var obj = { 
        prop : {
            n1prop : {
                hello : 'world';
            }
        }
    };
    

    这意味着我必须遍历所有类型为“object”的属性,如果您有一个深嵌套,那么这可能会非常昂贵。

    如果您有一个简单的1级对象,您可以使用一个简单的循环反射并创建一个新的对象文本。 注意:这不会复制原始对象的方法。

    function clone(obj) {
       var cloned;
    
       if (obj && typeof obj === 'object') {
           cloned = {};
           for (var p in obj) {
               if (obj.hasOwnProperty(p) && typeof obj[p] !== 'function') {
                   cloned[p] = obj[p]
               }
           }
           return cloned;
       }
       else {
           return null;
       }
    }
    
        3
  •  0
  •   Rojo    15 年前

    除了SolutionYogi提到的改变之外,还需要做另一个改变。在FOO中,您将传递要克隆的BAR,但BAR是构造函数(typeof BAR==“function”),因此它将无法通过克隆函数的第一次测试,并且您的返回值将是对未更改的BAR的引用。这意味着myBAR.prototype不是BAR.prototype的克隆,而是对它的引用。

    为了实际创建一个新的构造函数,而不仅仅是一个ref,我认为您必须使用eval——添加如下内容:

    if (typeof obj == "function) {
        eval("var temp = " + obj + ";");
        return temp;
    }
    

    还有其他考虑因素(正如Alex指出的),但是添加上面的内容应该会使您的测试用例成功。

        4
  •  0
  •   Christoph    15 年前
    function clone(obj) {
        if(typeof obj !== 'undefined') {
            clone.prototype = Object(obj);
            return new clone;
        }
    }
    
    function Foo() {} // base class
    
    function Bar() {} // derived class
    Bar.prototype = clone(Foo.prototype); // inherit from `Foo.prototype`
    
        5
  •  0
  •   Stanislav Müller    15 年前

    我只是想向任何人展示我对上述问题的解决方案。

    function cloneClass (func) {
    
      if(typeof func == 'function') {
    
        var prototype = {};
        for (var prop in func.prototype) {
          prototype[prop] = func.prototype[prop];
        };
    
        var Class = function() {
          var _this = this;
          var constructor = function() {
            func.apply(_this, arguments);
          };
          constructor.prototype = _this;
    
          for (var property in func) {
            if(property != "prototype") {
              constructor[property] = func[property];
            }
          };
    
          return constructor;
        };
    
        Class.prototype = prototype;
        return new Class();
      }
    
      return func;
    
    };
    

    试着深入了解它是如何工作的。是否有人可以看到此实现存在任何问题、内存泄漏等。?