Welcome to the prototype chain!
Let's see what it looks like in your example.
The Problem
When you call new Thing(), you are creating a new object with a property relatedThings which refers to an array. So we can say we have this:
+--------------+
|Thing instance|
| |
| relatedThings|----> Array
+--------------+
You are then assigning this instance to ThingA.prototype:
+--------------+
| ThingA | +--------------+
| | |Thing instance|
| prototype |----> | |
+--------------+ | relatedThings|----> Array
+--------------+
So each instance of ThingA will inherit from the Thing instance. Now you are going to create ThingA1 and ThingA2 and assign a new ThingA instance to each of their prototypes, and later create instances of ThingA1 and ThingA2 (and ThingA and Thing, but not shown here).
The relationship is now this (__proto__ is an internal property, connecting an object with its prototype):
+-------------+
| ThingA |
| |
+-------------+ | prototype |----+
| ThingA1 | +-------------+ |
| | |
| prototype |---> +--------------+ |
+-------------+ | ThingA | |
| instance (1) | |
| | |
+-------------+ | __proto__ |--------------+
| ThingA1 | +--------------+ |
| instance | ^ |
| | | v
| __proto__ |-----------+ +--------------+
+-------------+ |Thing instance|
| |
| relatedThings|---> Array
+-------------+ +--------------+ +--------------+
| ThingA2 | | ThingA | ^
| | | instance (2) | |
| prototype |---> | | |
+-------------+ | __proto__ |--------------+
+--------------+
+-------------+ ^
| ThingA2 | |
| instance | |
| | |
| __proto__ |-----------+
+-------------+
And because of that, every instance of ThingA, ThingA1 or ThingA2 refers to one and the same array instance.
This is not what you want!
The Solution
To solve this problem, each instance of any "subclass" should have its own relatedThings property. You can achieve this by calling the parent constructor in each child constructor, similar to calling super() in other languages:
function ThingA() {
Thing.call(this);
}
function ThingA1() {
ThingA.call(this);
}
// ...
This calls Thing and ThingA and sets this inside those function to the first argument you pass to .call. Learn more about .call [MDN] and this [MDN].
This alone will change the above picture to:
+-------------+
| ThingA |
| |
+-------------+ | prototype |----+
| ThingA1 | +-------------+ |
| | |
| prototype |---> +--------------+ |
+-------------+ | ThingA | |
| instance (1) | |
| | |
| relatedThings|---> Array |
+-------------+ | __proto__ |--------------+
| ThingA1 | +--------------+ |
| instance | ^ |
| | | |
|relatedThings|---> Array | v
| __proto__ |-----------+ +--------------+
+-------------+ |Thing instance|
| |
| relatedThings|---> Array
+-------------+ +--------------+ +--------------+
| ThingA2 | | ThingA | ^
| | | instance (2) | |
| prototype |---> | | |
+-------------+ | relatedThings|---> Array |
| __proto__ |--------------+
+--------------+
+-------------+ ^
| ThingA2 | |
| instance | |
| | |
|relatedThings|---> Array |
| __proto__ |-----------+
+-------------+
As you can see, each instance has its own relatedThings property, which refers to a different array instance. There are still relatedThings properties in the prototype chain, but they are all shadowed by the instance property.
Better Inheritance
Also, don't set the prototype with:
ThingA.prototype = new Thing();
You actually don't want to create a new Thing instance here. What would happen if Thing expected arguments? Which one would you pass? What if calling the Thing constructor has side effects?
What you actually want is to hook up Thing.prototype into the prototype chain. You can do this with Object.create [MDN]:
ThingA.prototype = Object.create(Thing.prototype);
Anything that happens when the constructor (Thing) is executed will happen later, when we actually create a new ThingA instance (by calling Thing.call(this) as shown above).