【问题标题】:How to prevent classes from inheriting static variables?如何防止类继承静态变量?
【发布时间】:2019-08-02 21:55:17
【问题描述】:

我正在制作盗版 agar.io。每次创建 Blob(游戏中所有循环实体的父类,包括玩家和食物)时,都会在 Blob 类的静态数组中存储一个引用。我想为扩展 Blob 类的 Food 类做同样的事情(创建一个类的所有实例的静态数组)。

我已经尝试过:在所有三个类的构造函数中,对象被推送到.all 静态数组,因此所有实例都可以通过Blob.all 或Food.all 访问。要在推送this 之前初始化静态变量,我意识到如果等于null,我需要将.all 设置为一个空数组。

const Blob = class extends Point { 
    constructor(x = 0, y = 0, radius = 5, color = `rgb(0, 0, 0)`) {
        super(x, y)
        this.radius = radius
        this.color = color
        this.deleted = false
        Blob.all = Blob.all || []
        Blob.all.push(this)
    }

    static draw(ctx) {
        Blob.all.forEach(blob => blob.draw(ctx))
    }

    // ...

    delete() {
        this.deleted = true
        Blob.all.filter(blob => blob !== this)
    }
}


const Food = class extends Blob { 
    constructor(x = 0, y = 0) {
        super(x, y, 5, `rgb(255, 150, 120)`)
        Food.all = Food.all || []
        Food.all.push(this)
    }

    static draw(ctx) {
        Food.all.forEach(food => food.draw(ctx))
    }

    // ...

    delete() {
        super.delete()
        Food.all.filter(food => food !== this)
    }
}


const Player = class extends Blob { 
    constructor(x = 0, y = 0, color = `rgb(100, 100, 100)`) {
        super(x, y, 20, color)
        Player.all = Player.all || []
        Player.all.push(this)
    }

    static draw(ctx) {
        Player.all.forEach(player => player.draw(ctx))
    }

    // ...

    delete() {
        super.delete()
        Player.all.filter(player => player !== this)
    }
}


// ...


// oversimplification of how I'm initializing and looping my game
gameInit = _ => {
     // creates 100 new instances of Food
     for (let i = 0; i < 100; i++) new Food(randX, randY)

     // creates 1 new instance of a player
     let player = new Player()
}

drawLoop = _ => {
     Food.draw(ctx)
     Player.draw(ctx)
}


在gameInit() 之后,我希望Player.all 是一个包含 1 个实例(1 个 Player 对象)的数组。 我还希望Food.all 是一个包含 100 个实例(100 个 Food 对象)的数组。 我还希望Blob.all 是一个包含 101 个实例(100 个 Food 对象,1 个 Player 对象)的数组。

原来,Player.all、Food.all、Blob.all 面积都相等!它们都共享相同的静态变量。不仅我不希望它们都共享同一个变量,而且该数组中有 202 个对象,因为在创建 Food 时,它会被推送到静态变量两次。

如何获得预期的结果?

【问题讨论】:

    标签: javascript es6-class


    【解决方案1】:

    问题是Food.all 会查找存储在Blob.all 下的继承数组,所以这样:

      Food.all = Food.all || [];
    

    基本上可以

     Food.all = Blob.all /*|| []*/;
    

    要在每个类下存储新数组,您应该在类声明之外手动实例化它们:

     class Food extends Blob { /*...*/ }
     Food.all = [];
    

    创建 Food 时,它会被推送到静态变量两次。

    当然,因为每个 Food 也是一个 Blob,因此它会被推送到 Blob.all 和到 Food.all。


    注意:通过创建这些数组,您可以防止所有 Blob 对象在您保留对它们的引用时被垃圾回收。您必须手动将它们从数组中删除,以使它们可用于垃圾收集(就像使用 .delete 方法一样)。

    【讨论】:

      【解决方案2】:

      你可以为每个类重载静态属性

      const Player = class extends Blob { 
          static all = [];
      
          // rest of the implementation
      }
      

      这样每个类的属性实际上是不同的。

      【讨论】:

      • 不是在 ES6 版本的 JS 中实现的吗?如果 Food.all 还不是静态属性,他还能如何访问它?
      • class Player { /*...*/ } 或多或少等于function Player() { /*...*/ }(幕后发生了一些特殊的事情,我在这里简化了)并且函数只是对象,您可以将属性添加到对象。这就是OP所做的。 static all = [] 也创建了该属性,但它使用了一种新语法,目前是提案的第 3 阶段,因此它可能是 es2019
      • 我讨厌成为那个人,但除非我误解了某些东西,否则它似乎已经可用jsfiddle.net/kgwt0d5h
      • 是的,它们附带 chrome 72 chromestatus.com/feature/6001727933251584
      【解决方案3】:

      你无法阻止它,但你可以解决它。

      首先,您可以为每个类显式初始化 Array,如 Jonas Wilms 所示:

      const Blob = class extends Point { 
      ...
      }
      Blob.all = [];
      
      const Food = class extends Blob { 
      ...
      }
      // at this point, Food has inherited the Array `all`, but you can overwrite it with an own one
      Food.all = [];
      

      等等。你可以摆脱这些检查Blob.all = Blob.all || [],因为你已经明确地初始化了每个数组。


      V2:可以查看数组all是否属于当前类

      if(!hasOwnProperty.call(Blob, "all")) Blob.all = [];
      

      其他类也一样。也许把这个逻辑放在一个函数中。


      V3:为什么不对数组使用单独的变量?

      const allBlobs = [];
      const Blob = class extends Point { 
          constructor(x = 0, y = 0, radius = 5, color = `rgb(0, 0, 0)`) {
              ...
              allBlobs.push(this)
          }
      
          static draw(ctx) {
              allBlobs.forEach(blob => blob.draw(ctx))
          }
      
          delete() {
              ...
              allBlobs.filter(blob => blob !== this)
          }
      };
      
      const allFoods = [];
      const Food = class extends Blob { 
          constructor(x = 0, y = 0) {
              super(x, y, 5, `rgb(255, 150, 120)`)
              allFoods.push(this)
          }
          ...
      }
      

      如果您使用任何类型的模块,这些变量也会封装在这些模块中,而不是公开的。

      【讨论】:

      • 还有其他一些方法可以解决这个问题,比如 IIFE 和符号,但这些“解决方案”正在得到 imo。太花哨了,无法将它们包含在答案中。
      猜你喜欢
      • 1970-01-01
      • 2020-10-18
      • 2014-10-08
      • 2011-08-05
      • 1970-01-01
      • 2017-02-07
      • 2012-02-18
      • 2014-04-23
      • 1970-01-01
      相关资源
      最近更新 更多