代码之家  ›  专栏  ›  技术社区  ›  madhamster

为什么PrototypeJS类实例共享相同的属性?[副本]

  •  1
  • madhamster  · 技术社区  · 8 年前

    我很难理解PrototypeJS的多个类实例。

    这是我的代码示例:

    var Test = Class.create();
    
    Test.prototype = {
      settings: {
        a: {},
        b: {},
      },
      initialize: function(options) {
        this.settings.c = options.c;
      }
    };
    
    var a = new Test({c: 1});
    console.log(a.settings);
    
    var b = new Test({c: 2});
    console.log(a.settings);
    
    var c = new Test({c: 3});
    console.log(a.settings);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.2/prototype.min.js"></script>

    预期结果: a、 设置应包含值为“1”的“c”项。

    实际结果: a、 设置“c”等于“3”。

    Screenshot from dev. tools

    (请注意,您需要在开发工具中展开对象才能看到它。)

    为什么?我是遗漏了什么,还是某种PrototypeJS错误?创建孤立类实例的正确方法是什么?

    1 回复  |  直到 8 年前
        1
  •  1
  •   Felix Kling    8 年前

    每个实例共享相同的 this.settings 对象可变数据结构必须在 构造函数 ,不在原型上:

    Test.prototype = {
      initialize: function(options) {
        this.settings =  {
          a: {},
          b: {},
          c: options.c;
        };
      }
    };
    

    请注意,这似乎是“传统方式”(根据文档),较新的方式是:

    var Test = Class.create({
      initialize: function(options) {
        this.settings =  {
          a: {},
          b: {},
          c: options.c;
        };
      }
    });
    

    这个问题实际上在 PrototypeJS documentation :

    编程语言中的继承类型

    通常,我们区分基于类的继承和原型继承,后者特定于JavaScript。

    当然,原型继承是该语言的一个非常有用的特性,但当您实际创建对象时,它通常是冗长的。这就是为什么我们通过内部原型继承来模拟基于类的继承(如在Ruby语言中)。这有一定的含义。

    [...]

    我们可以尝试在Prototype中执行相同的操作:

    var Logger = Class.create({
      initialize: function() { },
      log: [],
      write: function(message) {
        this.log.push(message);
      }
    });
    
    var logger = new Logger;
    logger.log; // -> []
    logger.write('foo');
    logger.write('bar');
    logger.log; // -> ['foo', 'bar']
    

    它有效。但如果我们再创建一个Logger实例呢?

    var logger2 = new Logger;
    logger2.log; // -> ['foo', 'bar']
    
    // ... hey, the log should have been empty!
    

    您可以看到,尽管您可能期望新实例中有一个空数组,但它与以前的记录器实例具有相同的数组。事实上,所有记录器对象都将共享同一个数组对象,因为它是通过引用而不是通过值复制的。

    相反,使用默认值初始化实例:

    var Logger = Class.create({
      initialize: function() {
        // this is the right way to do it:
        this.log = [];
      },
      write: function(message) {
        this.log.push(message);
      }
    });