First of all, I do not know of any other language which uses prototypal inheritance. My opinion is that all types of inheritance systems have pros and cons, so it really depends on personal taste and what's available.
Here is how it works (taken from MDN):
// Let's assume we have object o, with its own properties a and b:
// {a: 1, b: 2}
// o.[[Prototype]] has properties b and c:
// {b: 3, c: 4}
// Finally, o.[[Prototype]].[[Prototype]] is null.
// This is the end of the prototype chain as null,
// by definition, null has no [[Prototype]].
// Thus, the full prototype chain looks like:
// {a:1, b:2} ---> {b:3, c:4} ---> null
console.log(o.a); // 1
// Is there an 'a' own property on o? Yes, and its value is 1.
console.log(o.b); // 2
// Is there a 'b' own property on o? Yes, and its value is 2.
// The prototype also has a 'b' property, but it's not visited.
// This is called "property shadowing"
console.log(o.c); // 4
// Is there a 'c' own property on o? No, check its prototype.
// Is there a 'c' own property on o.[[Prototype]]? Yes, its value is 4.
console.log(o.d); // undefined
// Is there a 'd' own property on o? No, check its prototype.
// Is there a 'd' own property on o.[[Prototype]]? No, check its prototype.
// o.[[Prototype]].[[Prototype]] is null, stop searching,
// no property found, return undefined
In short, when you inherit from an object, that object is stored in prototype. JS interpreters first try to find a value on the object, then in its prototype, until the prototype is null.
Classical inheritance (for example C++) works different in that it copies all inherited methods and properties on a class level (non-instanciated). Upon instantiating the class, you will have to set the default property values of any inherited properties yourself (usually done by calling the super constructor). Example (C++):
class Base
{
public:
int a;
// Constructor with initializer list, initializes `a` with the value `42`
Base() :
a{42}
{}
};
class Child : public Base
{
public:
int b;
Child() :
b{17},
// The following line calls the super constructor
Base()
{}
};
int main()
{
// The following line instantiates the object from the class
Child* child = new Child();
std::cout << "child.a: " << child->a << " and child.b: " << child->b << std::endl;
return 0;
}