Creating new classes that inherit from an existing array class using the `

After researching various blog posts, SO threads, and lectures on subclassing Array in JavaScript, it seems like the consensus is that creating a subclass comes with its own set of challenges.

However, through experimentation, I developed my own solution:

// Here's the constructor for the new CustomArray class.
function CustomArray() {

    // Enforcing strict mode to ensure cleaner code execution.
    "use strict";

    // Initializing a new array to maintain the special behavior of numeric properties.
    var arr = [],
        i;

    // Setting CustomArray.prototype in the prototype chain so that the object inherits methods without breaking array functionalities.
    Object.setPrototypeOf(arr, CustomArray.prototype);

    // Pushing all arguments into the newly created array.
    for (i = 0; i < arguments.length; i++) {
        arr.push(arguments[i]);
    }

    // Returning the modified array with an updated prototype chain instead of returning "this".
    return arr;
}

// Establishing inheritance from Array for CustomArray.
CustomArray.prototype = Object.create(Array.prototype);

// Defining a method for the CustomArray class.
CustomArray.prototype.last = function () {
    return this[this.length - 1];
};

var myArray = new CustomArray("A", "B", 3);
// [ "A", "B", 3 ]

myArray.length;
// 3

myArray.push("C");
// [ "A", "B", 3, "C" ]

myArray.length;
// 4

myArray.last();
// "C"

Now my question remains: Is there any flaw or issue within this implementation? It's hard to believe that I've stumbled upon the perfect solution after others have extensively explored this realm before me.

Answer №1

The content delves into the process of creating a specialized array "subclass". This involves crafting an object that aligns with Array.prototype in its prototype chain, but with an immediate prototype parent distinct from Array.prototype to incorporate additional methods beyond typical array functions.

Exploring the concept further, it highlights a key challenge when forming an array "subclass" - arrays derive their functionality from both:

  1. their prototype, and
  2. simply being instances of arrays.

If arrays garnered all their traits solely from Array.prototype, the task would be straightforward. An object encompassing Array.prototype in its prototype chain would suffice as an ideal prototype for our array-subclass instances.

Nonetheless, arrays exhibit distinctive automatic behaviors exclusive to array instances which are not inherited from the prototype. These behaviors, particularly concerning the automatic adjustment of the length property upon array modifications, cannot be replicated faithfully in ECMAScript 5. Consequently, the instance of your array subclass must originate from the Array constructor to preserve the essential length behaviors.

This prerequisite clashes with the need for the instance to possess a prototype divergent from Array.prototype. In ECMAScript 5, objects generated through the Array constructor necessitate a prototype parent of Array.prototype. Altering an object's prototype after creation is not feasible according to the ECMAScript 5 specification.

In contrast, ECMAScript 6 offers a mechanism allowing such alterations. Your technique resembles the __proto__-based approach outlined in the article section titled "Wrappers. Prototype chain injection.," albeit utilizing ECMAScript 6's Object.setPrototypeOf instead of __proto__.

Your strategy effectively meets these specified criteria:

  1. Every instance genuinely represents an array (i.e., constructed via the Array constructor) ensuring correct internal properties like [[Class]] and accurate functioning of length.
  2. Each instance possesses an immediate prototype deviating from Array.prototype, while still incorporating Array.prototype within its prototype chain.

Previously unattainable under ES5, these requirements find fulfillment in ES6, offering a streamlined path forward. In ES5, one could encounter an array instance failing to meet requirement #2 or a regular object not aligning with requirement #1.

Answer №2

Subclassing arrays can be achieved without using Object.setPrototypeOf() or __proto__. Instead, the mysterious Array.of() method can be leveraged to switch the constructor function used to construct an array. By default, Array.of() is tied to the Array object, creating normal arrays. However, when bound to another object that can act as a constructor function, it constructs arrays based on that object. Let's explore how to subclass an array using Array.of():

function SubArr(){}
SubArr.prototype = Object.create(Array.prototype);
SubArr.prototype.last = function(){return this[this.length-1]};

var customArray = Array.of.call(SubArr, 1, 2, 3, 4, "this is last");
console.log(JSON.stringify(customArray,null,2));
console.log(customArray.last());
console.log(customArray.map(e => e));
console.log(customArray instanceof Array);
console.log(Array.isArray(customArray));
customArray.unshift("this is first");
console.log(JSON.stringify(customArray,null,2));

As demonstrated, array subclassing becomes straightforward with Array.of(). More details about its specifications can be found here. An interesting aspect to note is:

NOTE 2 The of function is an intentionally generic factory method; it does not require that its this value be the Array constructor. Therefore it can be transferred to or inherited by other constructors that may be called with a single numeric argument.

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

I'm struggling to understand how to interpret this. The v-tab function seems to be generating a button with various properties, but I'm unsure which specific property is related to

The V-tab below generates the button known as the right one on the v-app-bar: https://i.stack.imgur.com/DzNmq.png <v-tab :to="'/demo'" active-class="text--primary" class=&quo ...

The Vue.JS application encountered an error while making an API request, resulting in an uncaught TypeError: Cannot read properties of undefined related to the 'quote'

<template> <article class="message is-warning"> <div class="message-header"> <span>Code Examples</span> </div> <div class="message-body"> ...

"Using jQuery to ensure multiple sections are ready for manipulation on

After some experimentation, I made an interesting discovery: I can actually include $(document).ready(function(){}); multiple times. For example: $(document).ready(function(){ var abc = "1122"; //do something.. }); $(document).ready(function() ...

Assigning the value of one variable to be equal to the most recent state value of another variable

As a beginner in reactjs, I am facing a challenge in my code where I need to set the starting balance in a form as the closing balance of the previous record. The initial values in the form are fetched from an API call stored in the get_schedule array. The ...

"Narrow down the object's properties based on an array of specified property

So here's the situation: I have an object that looks like this var obj = { name1: somevalue1, name2: somevalue2, name3: somevalue3} Followed by an array var arr = [name2, name3] Both of these are dynamically generated. What I need to do is filte ...

What is the best way to make a Dojo TitlePane overlap using CSS?

My dilemma involves a jsfiddle featuring two TitlePane widgets in the central pane's top right corner. Currently, clicking on the right TitlePane ("Switch Basemap") moves the left TitlePane ("Map Overlays") to the left. What I want is for the right Ti ...

Issue encountered while attempting to deactivate certain foundation CSS and JavaScript files

I am attempting to deactivate certain CSS files in the foundation framework, as they are unnecessary for my project. After installing foundation via a gem, I followed Ryan Bates' recommendation to modify the foundation_and_overrides.scss file by repl ...

Updating objects in Angular 8 while excluding the current index: a guide

this.DynamicData = { "items": [ { "item": "example", "description": "example" }, { "item": "aa", "description": "bb" }, ...

Create a dynamic JavaScript animation with frames

Is there a simpler method to animate a sprite with just 4 frames in HTML Canvas? Having a background in AS3 and C#, I found the code below to be overly complicated. It seems like I'll be spending hours trying to decipher it. Is there a more modern or ...

Why is the useHistory hook in React failing to function properly?

When I try to launch my web application using npm start, an error occurs stating that the useHistory hook has not been downloaded, despite having installed the latest version of react-router-dom. Can someone explain why this is happening? Here is a screens ...

Angular dynamic multi-select dropdown box

Exploring Choices In my quest to enhance user experience, I am working on creating a dynamic system involving two selection (drop-down) boxes. The idea is for the options in the second box to change based on the selection made in the first box. For insta ...

Retrieve data depending on the conditions set in the if statement

I am currently working on a demo app that retrieves data from the Chuck Norris jokes API. I have included a variable in the endpoint to specify the joke category. fetchJoke: function() { fetch( `https://api.chucknorris.io/jokes/random?category=${th ...

Tips for preventing multiple button clicks until the completion of the initial click event

I created a clock that tells the time every quarter as per the professor's request. However, there is an issue - when I click multiple times, the clock keeps telling the time repeatedly until the number of tellings matches the number of clicks. I thou ...

Try implementing transform translate with absolute coordinates when positioning elements on a webpage

If I wanted to create a box that moves along with the mouse cursor, I could achieve that using the code below. document.addEventListener('mousemove', (e) => { document.documentElement.style.setProperty('--mouse-x', e.clientX ...

The VoiceOver feature on iOS fails to respond correctly to anchor elements and changes in focus

While I have sought answers to similar questions in the past, such as this one, my mind remains unsettled until I discover a solution: Among all the screen readers I've encountered, VoiceOver on iOS seems to be the only one that struggles with handli ...

Sending a post request from JavaScript to Django Rest Framework

I am working with a DFR api endpoint: url = http://example.com/api/data/ The URL of the page where I am running JavaScript code is: http://example.com/page/1/ I have logged in as User1 in my browser. POST request - from DRF browser API - successful. G ...

Exploring the data transfer process through the Vue-Laravel API Resource link

Utilizing the powerful combination of Laravel and Vue connected through an API has been smooth sailing so far. Recently, I made a request to fetch an offer using a method from Vue: getOffer(id) { this.$http.get('http://127.0.0.1:8 ...

Unable to invoke setState (or forceUpdate) on a component that has been unmounted

After updating the component, I encountered an issue with fetching data from the server. It seems that componentWillUnmount is not helpful in my case since I don't need to destroy the component. Does anyone have a solution for this? And when should I ...

Learn the process of utilizing Javascript to substitute additional texts with (...) in your content

I am facing a challenge with a text field that allows users to input text. I want to use JavaScript to retrieve the text from the textbox and display it in a paragraph. However, my issue is that I have set a character limit of 50. If a user exceeds this li ...

What is the best way to merge several filters into one filter function in order to retrieve data for a specific month?

A smarter approach to improve efficiency and reduce time complexity (n^3) while retrieving all variables. getrewarddata represents an object d1, d2, d3... are individual variables //Object contains various rewards for different months const getrewardda ...