【问题标题】:Recursive prototypal inheritance in javascript?javascript中的递归原型继承?
【发布时间】:2011-09-03 02:25:39
【问题描述】:
Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
};

来自Prototypal Inheritance in JavaScript

一段时间以来,我一直在使用这段代码来创建从以前的对象继承的新对象。但是我遇到了一个小惊喜。

a = {
    foo: [1,2,3]
}

b = Object.create(a);
// b.foo -> [1,2,3]
b.foo = "test";
// b.foo -> "test"
// a.foo -> [1,2,3]

c = Object.create(a);
// c.foo -> [1,2,3]
c.foo[0] = 'test';
// c.foo -> ["test",2,3]
// a.foo -> ["test",2,3]

在尝试更改 c.foo 时,我更改了 a.fooc.foo 显示更改,因为它继承自 a。我现在看到的唯一解决方案是只修改b的直接属性:

d = Object.create(a);
d.foo = Object.create(a.foo);
d.foo[0] = 'text';

我确定我缺少更好的解决方案! 如何从旧对象创建新对象而不冒修改原始对象的风险?

【问题讨论】:

    标签: javascript inheritance prototypal-inheritance


    【解决方案1】:

    +1 用于引用 Crockford =D

    我认为数组总是通过引用传递,并且永远不会复制数组。我认为 Array.slice 会复制它,但这是一个边缘案例......

    create 函数主要用于复制函数等。我从不使用该方法,因为我只是创建了一个构造函数:

    function a() {
        this.foo = [0, 1, 2];
    }
    

    这样,每次调用都会得到一个新数组。只需执行 b = new a();它对我有用...

    我尽量避免继承,因为它总是存在问题。我只会使用构造函数,然后为其分配新变量(而不是原型)。多重继承虽然变得棘手......

    【讨论】:

    • 说一个值是“通过值/引用传递”是不正确的。值总是在 js 中传递。参数 OTOH 始终是“按值调用”,这意味着您设置参数的参数将等于参数。还要区分参数和参数。参数是实际变量,参数是值。 function a(x) {} a(1) x 是参数,1 是参数(/值)。
    • 呃...我也没有提到...而且数组是特殊对象。从节点: var arr = [1, 2, 3]; > 功能酷(tArr) {tArr.push(4);} > 酷(arr); > arr; [1、2、3、4]
    【解决方案2】:

    当您从原型创建新对象时,不会进行复制。甚至没有复制属性:

    function F() {}
    F.prototype.a = 1
    new F().hasOwnProperty("a") // => false
    new F().a // => 1
    

    如果你这样做

    var f = new F();
    f.a = 2;
    

    那么您并没有更改F.protoype 中的属性,而是将新属性添加到f

    var f = new F();
    f.a = 2;
    f.a // => 2;
    delete f.a;
    f.a // => 1
    

    因此,这适用于您分配属性的每个值。如果你想克隆一个值,你必须明确地这样做,最简单的方法是在构造函数中设置一个新属性:

    function F() {
      this.a = [];
    }
    var f = new F();
    var g = new F();
    f.a === g.a // => false
    

    只有当原型包含可变值时才会出现此问题,不能修改其他值(属性可以更改值)。

    如果你想“子类化”F,记得从新的构造函数中调用它:

    function F() { this.a = []; }
    function G() { F.call(this); }
    G.prototype = new F();
    

    【讨论】:

      【解决方案3】:

      我见过的最好的继承模式是 Google Closure Library 的。它基于构造函数。示例如下:

      //with this method we will inherit one "class" (there are no real classes in JS) from another.
      var inherit = function (c, p) {
          function F() {}
          F.prototype = p.prototype;
          c.prototype = new F;
          c.prototype.constructor = c;
      };
      
      //create one "class"
      var A = function(){
          this.foo = [1,2,3];
      }
      //create its instance
      var a = new A();
      
      //create another "class"
      var C = function(){
          //call the parent constructor
          A.call(this);
      }
      //make inheritance
      inherit(C, A);
      //create example of inherited "class"
      var c = new C();
      

      结果如你所愿:

      console.log(c.foo);
      //-> [1,2,3]
      c.foo[0] = 'test';
      console.log(c.foo);
      //-> ['test',2,3]
      console.log(a.foo);
      //-> [1,2,3]
      

      【讨论】:

        【解决方案4】:

        是的:

        1)

        b.foo = "test";
        // b.foo -> "test"
        

        用“test”替换你的 F.prototype 引用,这样你就看不到错误,但实际上这比 2) 更糟

        2)

        c = Object.create(a);
        // c.foo -> [1,2,3]
        c.foo[0] = 'test';
        // c.foo -> ["test",2,3]
        // a.foo -> ["test",2,3]
        

        修改 a 对象,因为 c.foo[0] & a.foo 指向它(它们是指向 a 值的引用/指针)

        解决方案here

        【讨论】:

        • 出于这种原因,我尽量避免使用 JavaScript 进行继承。没有真正好的方法来复制这样的东西。例如,如果数组是一个数组数组,嵌套了好几层怎么办?这会导致对象构造缓慢。
        • 那么你需要一个更严肃的解决方案:stackoverflow.com/questions/122102/…
        • 通读一下extend方法。很干净,但是:github.com/documentcloud/underscore/issues/…
        • 欢迎编程,灵丹妙药尚未找到,悬停您是否访问了上一条评论中的链接?这是迄今为止最好的解决方案
        • 是的。这就是我所说的“通读扩展方法”的意思。我通读了 jQuery 代码。我只是说应该避免它。如果支持 IE 等旧版“浏览器”,则尤其如此。
        【解决方案5】:

        这是解决方案:

        
        Object.create = function (o) {
            function F() {
                for(var prop in this)
                    if(o.hasOwnProperty(prop))
                        this[prop] = Object.create(o[prop]);
            }
            F.prototype = o;
            return new F();
        };
        

        此解决方案的一般限制是原型不应具有递归引用。

        也可以在没有限制的情况下解决这个问题,但解决方案会更复杂。为了防止无限递归对象创建,您可以在初始化过程中使用某种临时地图存储。此存储将是初始化对象之间的缓存引用。

        【讨论】:

        • 这似乎不起作用,但我尝试了!首先有一个带有额外括号的错字并且 obj 是未定义的。 hasOwnProperty 也将始终为 false,因为 this 在创建时只有原型。如果你循环 o 的属性并将其传递给 F 它会产生非常奇怪的结果!
        猜你喜欢
        • 2010-09-28
        • 2010-09-28
        • 2018-02-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-07
        相关资源
        最近更新 更多