1

Here are a couple of older question that discuss the Javascript prototypal inheritance & delegation, e.g.:

I am wondering what the current (2018) recommendation is to use prototypes / prototypal inheritance in Javascript.

As far as I understand, newer version of JavaScript (ES6) and TypeScript they both head more towards traditional class-based inheritance. (Myself I didn't use ES6 oder TS in practice yet.) Is this observation true?

In fact, this class-based code is really simple and easy to understand:

class A { a: "a" }
class B extends A { b: "b" }
let a = new A(), b = new B();

EDIT 2: In TypeScript it would be:

class A { a = "a" }
class B extends A { b = "b" }
let a = new A(), b = new B();

EDIT: In fact, the ES6 syntax is more complex:

class A { constructor() { this.a = "a"; } }
class B extends A { constructor() { super(); b = "b"; } }
let a = new A(), b = new B();

For using prototypes, there are more options, and actually I didn't find one yet that is equally simple and "nice".

EDIT: What I want to achieve is that I create b as instance of B with prototype A in a way that when I change A's property dynamically, b also is affected by the change:

A simple approach is:

var A = { a: "a" }
var B = Object.create(A, {b: {value: "b"}});
var a = Object.create(A), // direct instance of A
    b = Object.create(B); // indirect instance of A
console.log(b.a); // "a"
A.a = "a++"; // change the base prototype (will affect B, a, and b)
console.log(b.a); // "a++"

Which would be a lot nicer if the 2nd arg could also be a simple object with key-value pairs, not the property descriptors (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create)

Most of the time, the constructor function are used, e.g. in https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

function A() { this.a = "a"; }
function B() { this.b = "b"; }
B.prototype = new A();
var a = new A(), b = new B();
console.log(b.a); // "a"
A.a = "a++";
console.log(b.a); // return "a" instead of "a++" as b.a is overwritten in constructor

Also, not so nice, as here you cannot change A.a in a way that b.a is also changed which is IMO a key point in prototypal inheritance. So maybe this?

function A() {}
A.prototype.a = "a";
function B() {}
B.prototype = Object.create(A.prototype);
B.prototype.b = "b";
var a = new A(), b = new B();
function A() { this.a = "a"; }
function B() { this.b = "b"; }
B.prototype = new A();
var a = new A(), b = new B();
console.log(b.a); // "a"
A.a = "a++";
console.log(b.a); // still "a" instead of "a++"

Does not give the expected result. And, well, you don't want to write this, right?

Of course, you can put the creation in a constructor function as described by https://stackoverflow.com/a/16872315/1480587 but I think this is still not equally nice and simple to the class syntax. Actually, I'm looking for something like this (similar to Kotlin's object declaration):

object A { a: "a" }
object B extends A { b: "b" }
let a = new A(), b = new B();

So, what would you recommend? Is there anything that comes close?

Especially, if you want to use some encapsulation and have private object members not visible to cloned objects?

Does TypeScript provide a nice solution here?

Go for Kotlin?

Or should one generally move back to class-based inheritance as this is what everyone else is using and understands?

Peter T.
  • 2,927
  • 5
  • 33
  • 40
  • Actually, in ES6 `class` is just syntactic sugar for prototypes, try `class X{}` and `console.log(typeof X)`. – georg Mar 01 '18 at 12:11
  • *Does TypeScript provide a nice solution here?* Yes! ES6 classes. Welcome to modern JS. You will have hard time making TS types work for you without classes. – Estus Flask Mar 01 '18 at 12:29
  • I just edited my question to make clear what I am looking for. From your comments it seems that real prototype-based inheritance is not used. Which is actually sad as I think this is one of the really nice things in JS. Well, would be if we had a nice syntax for it. @georg: IMO the constructor functions in JS don't really help with proper prototypal inheritance, so I understand the ES6 class does not help in this case. – Peter T. Mar 01 '18 at 15:57
  • Of the first three snippets, the first one doesn’t seem to be relevant — it doesn’t appear to be valid syntax anywhere. The second snippet is supposedly about TypeScript, though I’m not sure if this is actual TypeScript syntax or has been in the past. The third snippet is missing the `this.` in `b = "b";`. What is this question focused on? ES6 or TypeScript? Or the interaction between the two? The first snippet should probably be removed and the other snippets updated. – Sebastian Simon Jan 09 '22 at 17:05

4 Answers4

3

A simple approach is Object.create

Then use that. It seems to be enough for what you want to do - two objects, the one inheriting from each the other. You don't need any constructors to do initialisation, and therefore no class syntax either.


Btw, to simplify I would not use the second argument to Object.create when you don't need custom property descriptors. Just do

var B = Object.create(A);
B.b = "b";

or, in one expression,

var B = Object.assign(Object.create(A), {
  b: "b",
});
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
0

Maybe I play around with a simple helper function like this:

/**
 * Create a new object with `prototype` as its prototype.
 * The (optional) properties will be copied to the newly created object
 * This is similar to Object.create(), however the properties passed is a plain
 * object, not a object with property descriptors,
 * see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
 *
 * @param prototype (object)
 * @param properties (object)
 * @return {prototype}
 */
function clonePrototype(prototype, properties) {
    var pDef = {};
    for(var key in (properties||{})) {
        if (properties.hasOwnProperty(key)) { pDef[key] = {value: properties[key]}; }
    }
    return Object.create(prototype, pDef);
}

This way, I can do what I wanted:

var a = { a: "a" };
var b = clonePrototype(a, { b: "b" });
console.log(b.a); // "a"
a.a = "a++";
console.log(b.a); // "a++"

.... comments and suggestions are welcome.

Peter T.
  • 2,927
  • 5
  • 33
  • 40
0

Alternatively, underscore and lodash provide _.create(), which help with creating a new object with initialized prototype:

var a = { a: "a" };
var b = _.create(a, { b: "b" });
console.log(b.a); // "a"
a.a = "a++";
console.log(b.a); // "a++"
Peter T.
  • 2,927
  • 5
  • 33
  • 40
-1

I just found what I was looking for: In ES6 "object literals are extended to support setting the prototype at construction." This is really simple & convenient!!

var obj = {
    // Sets the prototype. "__proto__" or '__proto__' would also work.
    __proto__: theProtoObj,
    // Computed property name does not set prototype or trigger early error for
    // duplicate __proto__ properties.
    ['__proto__']: somethingElse,
    // ...
};
Peter T.
  • 2,927
  • 5
  • 33
  • 40