Search code examples
javascriptinheritancestatic

why static methods of Object class are not inherited by other classes in JavaScript


In JavaScript If I create a class with a static method and then create a subclass of that class, I can call the static method using subclass name too (static methods are inherited).

Object class which is superclass of all classes has many static methods. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object But why none of those methods can be called using any of subclass names ?

update: I noticed some suggested to explicitly 'extend' Object. But Object class is already superclass of every class in JavaScript. the proof is the .toString() method that every object from every class has it and we know it is inherited from Object class even if you don't explicitly write 'extends Object'. So every class is implicitly extending Object (directly or through inheritance chain) but why non-static methods are being inherited automatically and not the static methods?

as for sample code:

class Test {
  constructor() {}
}

const test = new Test();
console.log(test.toString()); // works, inherited from Object class
console.log(Test.<any static method of Object>)); // doesn't work

Solution

  • This was a deliberate decision back in 2012 when ES6 was worked on - you can find it in the meeting notes on Maxmin class semantics:

    Mark S. Miller: so Foo will inherit Object.create, Object.getOwnPropertyDescriptor, etc?

    Dave Herman: that does mean we'll be more and more hampered from adding methods to Object

    […]

    Mark S. Miller: polluting of statics with everything in Object is fatal; those are just not relevant to most of the class abstractions people write; when I write class Point { } I don't want Point.getOwnPropertyDescriptor

    Allen Wirfs-Brock: you only opt into that with class Point extends Object; with class Point { } you don't get any of that stuff

    […]

    Yehuda Katz: also, there are override hazards of pollution: if someone freezes Object, then you wouldn't be able to override sweet class method names like keys(), so the ability to avoid that pollution is important

    I'm glad they've chosen this behaviour. Notice that not doing any inheritance of class (constructor function) objects - common in ES5 - was also on the table, it would have meant that calling static methods with dynamic this on subclasses would have been quite awkward. (Notice that at the time, there was no static modifier for method definitions, static methods were created by simple Foo.method = function() { … }; assignments).

    The "static methods" of Object were never (and still are not) meant to be dynamic, they neither do use the receiver (this) nor are they expected to be called on "subclasses". Object really is just a namespace for the most basic tools (intrinsic functions) for working with objects. When calling Test.create(), one would expect that to create a new test, not require a prototype object as an argument.

    Another reason why this would be weird is that when a class inherits from another class, including Object, its constructor must call super(). Simple classes that don't mean to use any inheritance shouldn't have to deal with that. So the case where a class has no extends clause is already handled differently, with a focus on usability since it's the most common use. Not making those class objects inherit from Object is just another feature.

    Finally, it follows the precedent of other built-in "classes". Function.prototype, Array.prototype, Error.prototype etc all inherit from Object.prototype, but their constructors do not inherit from Object.

    This is also corroborated by early tutorial blog posts such as this one:

    [When you] don’t extend: class Foo {}

    • The prototype of Foo is Function.prototype (as for all functions).
    • The prototype of Foo.prototype is Object.prototype.

    That is the same as for functions. Note that the above is not equivalent to class Foo extends Object, for the only reason that you normally want to avoid Foo inheriting methods such as Object.create().