Lackneets
Lackneets

Reputation: 21

Static variable inheritance in Javascript (ES6)

I'm trying to figure out how does Javascript completely support OOP. Luckily I can find some clue through Babel and know how it do downward compatibility to ES5.

But I found the static variable behaviors strange in inheritance.

For example, I want to remember global properties in superclass. But it seems static variable accessed from subclass is not actually refers to superclass. Is this reasonable in classical OOP?

class Animal {
  constructor(){
    this.constructor.count += 1;
    console.log('An animal was born');
  }

  static count = 0;
  static sum = function(){
    console.log('There are', this.count, 'animals');
  }

}

class Cat extends Animal{
  constructor(){
    super(); // throws exception when not called
    console.log('  -- the animal is a cat');
  }
}

var cat1 = new Cat();
var cat2 = new Cat();

Cat.sum();    // should be 2
Animal.sum(); // should be 2, but result is 0

in Babel Experimental Mode


In the above was expermental syntax. Then I saw an article says static property is not supported in ES6 yet. So I follow his example rewriting into static method (getter/setter), style, but still got no idea.....

class Animal {
  constructor(){
    this.constructor.countOne();
    console.log('An animal was born');
  }

  static countOne(){
    this.count = (this.count||0)+1;
  }

  static sum(){
    console.log('There are', this.count, 'animals');
  }
}

Animal.count = 0; // Remove this, Animal.sum() will be undefined

class Cat extends Animal{
  constructor(){
    super();
    console.log('  -- the animal is a cat');
  }
}


var cat1 = new Cat();
var cat2 = new Cat();

Cat.sum();    // should be 2
Animal.sum(); // should be 2, but result is 0

ES6 Fiddle

"this" refers to subclass, not superclass, result is the same...


Furthermore, I try the same code in PHP, then I got expected result:

class Animal{
  static $count = 0;
  static function sum(){
    echo "There are " . self::$count . " animals <br>";
  }

  public function __construct(){
    self::$count++;
    echo "An animal was born <br>";
  }
}

class Cat extends Animal{
  public function __construct(){
    parent::__construct();
    echo " - the animal is a cat <br>";
  }
}

$cat = new Cat();
$cat = new Cat();
$cat = new Cat();

Cat::sum();     // is 3
Animal::sum();  // is 3

So far, should we say static variable inheritance is not supported by Javascript? even in ECMA6?

Is there any graceful solution?

Upvotes: 1

Views: 4557

Answers (3)

run_the_race
run_the_race

Reputation: 2318

In the Animal constructor, change this.constructor.count to Animal.count.

  • this.constructor.count refers to the Cat class counter
  • Animal.count refers to the Animals counter

I.e.:

class Animal {
  constructor(){
    Animal.count += 1;
    console.log('An animal was born');
  }

The main point is each class has its own static variable, i.e. its not shared. JavaScript doesnt support classes in the traditional sense, they say its syntatic sugar over the object prototype model it uses. You may find this link useful: https://github.com/getify/You-Dont-Know-JS/tree/2nd-ed/objects-classes

Upvotes: 0

Blindman67
Blindman67

Reputation: 54026

There is an alternative way to provide static properties and that is via closure. By wrapping your class definitions inside a function you can scope variables only to your class, effectively creating a private static variable.

For example

"use strict";
var log =function(d){console.log(d)}; // lazy zoo keeper

// need to define the intermediate container
// Ill call it zoo.
var zoo = (function() {
    // now create the private static property
    var count=0;  // function scoped
    class Animal {
        constructor(){
            count += 1; // count instances
            log('An animal was born');
        } 
        static sum(){  // create the static method of interagation
            log('There are'+count+'animals');
        }

     }

     class Cat extends Animal{
         whatAreYou(){log("I am a cat ")};
     }
     // now return the classes you want to expose 
     return {
         Animal:Animal,
         Cat:Cat,             
     };
})();  // call the function to create a  Zoo

// now you can make the the Animal and Cat public or you could 
// keep zoo and pass it to another scope and have them private 
// where you want.

var Animal = zoo.Animal;
var Cat = zoo.Cat;

// Use static function befor there are any instances of Animal or Cat
Animal.sum(); // displays 0    

var a = new Animal(); // or new zoo.Animal();
var c = new Cat();

// access static function sum to display content of private and static (closure) property count;
Cat.sum();    // 2
Animal.sum(); // 2

Upvotes: 2

madox2
madox2

Reputation: 51841

You can access static members like:

Animal.count;
Animal.countOne();
Animal.sum();

In your 2nd example when you create new cat, this refers to new cat object and this.constructor refers to Cat function (even if it is called from super constructor).

Upvotes: 2

Related Questions