What are the recommended techniques for utilizing prototypal and prototype-based inheritance in modern JavaScript (ES6) and TypeScript?

Some older discussions on Javascript prototypal inheritance & delegation can be found below:

  • Benefits of prototypal inheritance over classical?
  • classical inheritance vs prototypal inheritance in javascript

I am curious about the current (2018) recommendations for using prototypes / prototypal inheritance in JavaScript.

It seems that newer versions of JavaScript (ES6) and TypeScript lean more towards traditional class-based inheritance. (I have yet to use ES6 or TS in practice.) Is this observation correct?

The class-based code example provided is straightforward and easy to grasp:

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

EDIT 2: In TypeScript, it would look like this:

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

EDIT: The ES6 syntax is slightly 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 available but finding one as simple and clean as class-based inheritance proves challenging.

EDIT: My goal is to create an instance of B with prototype A in a way that updates to A dynamically reflect in B:

A basic approach is shown here:

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++";
console.log(b.a); // "a++"

An improvement could be simplifying the object creation process by allowing key-value pairs instead of property descriptors.

Typically, constructor functions are used for prototypal inheritance as outlined in this article.

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

Unfortunately, this method does not allow changes to A.a to automatically update B.a, which is crucial in prototypal inheritance. Perhaps this alternative could work:

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();

This also falls short of expectations. It's evident that such manual manipulation is not ideal.

One option is to encapsulate these behaviors in constructor functions, as discussed in this post. However, it still lacks the simplicity and elegance of the class syntax. I'm searching for a solution akin to Kotlin's object declaration:

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

What approach do you suggest? Is there a better alternative out there?

Especially for those seeking encapsulation and private object members inaccessible to cloned objects?

Does TypeScript offer a viable solution?

Should we consider Kotlin as a potential alternative?

Or is reverting back to class-based inheritance due to its wider acceptance and understanding the best course of action?

Answer №1

A straightforward method involves utilizing the Object.create function.

Afterwards, implement this approach. It appears to be sufficient for achieving your desired outcome - creating two objects where one inherits from the other. There is no requirement for constructors for initialization, hence eliminating the need for using the class syntax as well.


By the way, for simplification purposes, consider omitting the second argument in Object.create unless you require custom property descriptors. Simply use:

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

Alternatively, accomplish it in a single expression:

var B = Object.assign(Object.create(A), {
  b: "b",
});

Answer №2

Exploring a potential solution using a basic helper function:

/**
 * Implementing a function to create an object with a specific prototype and optional properties.
 * Allows for copying properties to the new object without the need for property descriptors like in 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 approach enables achieving the desired functionality:

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

Any feedback or recommendations are appreciated.

Answer №3

Another option is to use underscore and lodash's _.create() function, which can assist in generating a fresh object with an established prototype:

let first = { first: "first" };
let second = _.create(first, { second: "second" });
console.log(second.first); // "first"
first.first = "updated";
console.log(second.first); // "updated"

Answer №4

I stumbled upon exactly what I needed: The latest features in ES6 allow you to define the prototype of an object right at its creation. This new functionality is incredibly straightforward and user-friendly!

let newObj = {
    // Setting the prototype using '__proto__'
    __proto__: myPrototypeObject,
    // Using a computed property name without affecting the prototype
    ['__proto__']: anotherThing,
    // Additional properties can be added here...
};

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Revolutionize Your App with React Native's Interactive Bottom Sheet Option

Hello there! I am trying to create a FlatList with multiple items, each of which should trigger a bottom-sheet when clicked. I want to make this dynamic but I'm encountering an error. TypeError: undefined is not an object (evaluating `_this[_reactNat ...

Tips for determining if a key is present in local storage:

I need to set a key value, but only if it doesn't already exist. In my component1.ts file, I am assigning the key and value in the constructor. However, I want to include a condition that this action should only be taken if the key is not already pre ...

If the user clicks outside of the navigation menu, the menu is intended to close automatically, but unfortunately it

I have a nav file and a contextnav file. I've added code to the nav file to close the navigation when clicking outside of it, but it's not working. How can I ensure that the open navigation closes when clicking outside of it? Both files are in ts ...

Sending javascript data to php within a modal popup

I am currently working on passing a javascript variable to php within a modal window, all while remaining on the same page (edit.php). Although I plan to tackle handling this across separate pages later on, for now, I have successfully implemented the foll ...

The setTimeout function fails to behave as intended when used in conjunction with synchronous ajax

Within my code, I have a function that is being invoked multiple times due to iterating through an array of length n and creating 'Plants' with synchronous ajax calls. These calls involve querying the Google Maps API, so it is crucial to throttle ...

Ways to prevent styles from being overridden by those specified in JavaScript

Some elements on my page have their style dynamically changed by JavaScript. However, I want one of them to maintain its static style without being overridden. Is there a way to specify that the style of this particular element should not be altered? Than ...

Employing a for loop to verify the existence of a JSON key

Currently, I am attempting to loop through an EJS JSON object and verify the existence of values. If a value does exist, I want to add the corresponding URL to an array further down. Upon loading the page, I am encountering an issue where I am informed th ...

Adding parameters to a URL is a common practice

"Adding additional information to a URL that was previously included?" I apologize for the confusing title, but I can't find a better way to phrase it. Perhaps an example will make things clearer. Let's say I have URL 1: http://example.com/?v ...

An unforeseen issue occurred while trying to access an indexed element ID

I've recently started learning javascript and jquery, and encountered a problem while working on a script. The script is created by php code that reads lines from a file, processes them, and displays them using arrays. In addition, javascript validat ...

Converting a specific string format to a Date object in TypeScript

I am in need of a solution to convert strings with the given format into Date objects using TypeScript: var dateTimeString:string = "20231002-123343" I have created my own method as shown below: var dateTime:string[] = dateTimeString.split(" ...

Creating a User Authentication System using Node.js and Express

I am currently working on developing an application with node.js and expressJs, even though I come from a PHP background. In PHP, we typically use the session to handle logins, so naturally, I thought that in the case of node.js it would be similar. After ...

What types of modifications do ViewChildren and ContentChildren QueryLists keep an eye out for?

Imagine you come across the following lines of code: https://i.stack.imgur.com/7IFx1.png And then, in a different section, you stumble upon this code block: https://i.stack.imgur.com/qac0F.png Under what circumstances would () => {} be executed? Wha ...

Setting a maximum value for an input type date can be achieved by using the attribute max="variable value"

Having trouble setting the current date as the "max" attribute value of an input field, even though I can retrieve the value in the console. Can anyone provide guidance on how to populate the input field with the current date (i.e max="2018-08-21")? var ...

Navigating to JSON input in Express correctly

I have successfully created a basic Express-based API to serve JSON data, but now I want to enhance its functionality. The JSON file follows this structure: (some sections are excluded) [{ "ID": "1", "NAME": "George Washington", "PAGE": "http://en.w ...

express: how to differentiate routing path based on request parameters

Currently, I am working on an API that is designed to receive a specific value from an Android client and then provide back a list of posts. One interesting aspect of this API is the request value known as req.body.sortType, which influences how the list o ...

The JavaScript object is unable to reach the PHP controller

When I attempt to send a JavaScript Object to my PHP controller, it is arriving empty. Here is the object: https://i.sstatic.net/19gFV.png Upon arrival, the object appears empty. Here is my AJAX code: https://i.sstatic.net/ekHsC.png var SendMovs = { ...

Creating generic output types in TypeScript based on the input types

In my React-Native project, I'm focusing on implementing autocomplete and type validation. One of the challenges I'm facing is configuring the types for the stylesheet library I am using. A customized stylesheet is structured like this: const s ...

Display various messages when submitting a form based on Ajax technology

I have been working on a script that utilizes AJAX to process form submissions. While I can successfully submit the form using AJAX and display a success message, I am looking to customize the messages based on the background data processing of the form. ...

Tips for adjusting the color of multiple classes that have a common class using a toggle feature

I am working on a simple website using bootstrap 4 and SCSS. I want to include a toggler that switches between dark and light modes on the webpage. However, I'm facing an issue with changing the background color of 6 Bootstrap cards, footer, and the t ...

Could someone provide clarification on this particular line of Apex code for me?

I'm completely new to Oracle Apex and I could use some guidance in understanding the code snippet I came across in a tutorial about app creation. After digging around, I suspect it might be JavaScript, but I'm not entirely certain. The scenario ...