Differences between class and prototype Array.isArray

Throughout my coding journey, I've often heard the saying "Classes are just syntactic sugar for prototypes."

However, an interesting example challenges this notion.

function SubArray() {}
SubArray.prototype = new Array( );
console.log(Array.isArray(new SubArray()))  // false

In contrast, consider the same example using classes.

SubArray = class extends Array{}
console.log(Array.isArray(new SubArray()))  // true

Surprisingly, instanceof works correctly with both new SubArray instanceof Array. But why does Array.isArray fail to return true with prototypes in this case?

Answer №1

If you're wondering why the code for the class is essentially sugar-coated, here's an example of how it can be achieved in ES5:

function SubArray () {
  if (!(new.target)) {
    throw new TypeError("Class constructor SubArray cannot be invoked without 'new'")
  }

  return Reflect.construct(Array, arguments, new.target)
}

Object.setPrototypeOf(SubArray.prototype, Array.prototype)
Object.setPrototypeOf(SubArray, Array)

console.log(Array.isArray(new SubArray())) // true

To mimic the behavior of the provided class syntax example in ES5, some workarounds are needed:

function SubArray () {
  if (!(this instanceof SubArray)) {
    throw new TypeError("Class constructor SubArray cannot be invoked without 'new'")
  }

  return Array.apply(this, arguments)
}

SubArray.prototype = Object.create(Array.prototype)
SubArray.__proto__ = Array

console.log(Array.isArray(new SubArray())) // true

The essence is to delegate object construction to the Array constructor for proper initialization as an Array exotic object. The minimalistic version would look like this:

function SubArray () {
  return Array.call(this)
}

console.log(Array.isArray(new SubArray())) // true

However, access to Array.prototype methods will be limited in this case. Therefore, sticking to class syntax or the improved ES5 version is recommended.

Edit

An experimental approach to closely emulate class in ES5 involves opting out of strict mode to utilize arguments.caller:

// WARNING: This is NOT recommended
// Demonstrating close emulation of new.target using ES5
// For educational purposes only

function SubArray () {
  if (!(this instanceof SubArray) && !(arguments.caller && this instanceof arguments.caller)) {
    throw new TypeError("Class constructor SubArray cannot be invoked without 'new'")
  }

  return Array.apply(this, arguments)
}

SubArray.prototype.__proto__ = Array.prototype
SubArray.__proto__ = Array

// Sloppy extension of FooBar from SubArray
function FooBar () {
  if (!(this instanceof SubArray) && !(arguments.caller && this instanceof arguments.caller)) {
    throw new TypeError("Class constructor FooBar cannot be invoked without 'new'")
  }

  return SubArray.apply(this, arguments)
}

FooBar.prototype.__proto__ = SubArray.prototype
FooBar.__proto__ = SubArray

try {
  SubArray()
} catch (e) {
  console.log(e.toString())
}

console.log(new SubArray(1, 2, 3))

try {
  FooBar()
} catch (e) {
  console.log(e.toString())
}

console.log(new FooBar(1, 2, 3))

Answer №2

Exploring the process of executing Array.isArray():

IsArray ( argument )

The IsArray abstract operation involves a single argument, and follows these steps:

If Type(argument) is not Object, return false.
If argument is an Array exotic object, return true.
If argument is a Proxy exotic object, then
    If the value of the [[ProxyHandler]] internal slot of argument is null, throw a TypeError exception.
    Let target be the value of the [[ProxyTarget]] internal slot of argument.
    Return IsArray(target).
Return false.

An instance that does not adhere to "If argument is an Array exotic object, return true." would lack its unique length property.

function SubArray() {}
SubArray.prototype = new Array( );
console.log(Array.isArray(new SubArray())) // false


const sa = new SubArray();
console.log(typeof sa); // object
console.log(Object.getOwnPropertyDescriptor(sa, "length")); // undefined

Answer №3

Kudos to rlemon for sharing a solution in the chat that allows you to achieve similar results to what Patrick did without using Reflect, and potentially enabling IE11 support.

function Bar() {
    const list = new Array(...arguments);
    list.__proto__ = Bar.prototype;
    return list;
}

Bar.prototype.constructor = Bar;

Bar.prototype.__proto__ = Array.prototype;

Array.isArray( new Bar(456) ) 

Answer №4

This unique approach utilizes iframes to offer the flexibility of extending a class through the use of class extend functionality.

let iframe = document.createElement("iframe");
iframe.style.display = "none";
document.body.appendChild(iframe);

frames[frames.length - 1].document.write(
  "<script>parent.SubArray = Array;<\/script>"
);

SubArray.prototype.__proto__ = Array.prototype;
SubArray.__proto__ = Array;
console.log(Array.isArray(new SubArray()));
console.log(new SubArray() instanceof Array);
console.log(new SubArray() instanceof SubArray);

SubArray2 = class extends SubArray {} 

console.log(new SubArray2() instanceof SubArray)

Answer №5

Traditional ES5 methods do not support proper inheritance from the Array object, but ES6 has rectified this issue.

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

How to loop through an array in JavaScript using a for loop?

Currently, I am engaged in a CSRF lab exercise with the challenge of iterating through over 20 tokens. <script> var token = ["f23e7b8c79d33d39ea67f0062b2cdb23", "90b157ac841c5aa7854285ea225c18e3", "9a189a1ef6a01aae6a298a0594831b66"]; var arr ...

Initiate the React application with the given external parameters

I have created a React app that is embedded within a webpage and needs to start with specific parameters obtained from the page. Currently, I am passing these parameters in the index.HTML file within a div element. The issue arises when these parameters ar ...

Is it possible to utilize the output of a function nested within a method in a different method?

I am currently facing a challenge with my constructor function. It is supposed to return several methods, but I'm having trouble using the value from this section of code: var info = JSON.parse(xhr.responseText); Specifically, I can't figure ou ...

A guide to resizing images for uploading in Node.js using Jimp without the need for refreshing the page

My goal is to resize a file server-side using Jimp before uploading it to Cloudinary in a node.js environment. Here's the controller I'm using: exports.uploadImage = async (req, res) => { if (!req.files) { return res.status(400).json({ m ...

Invoking a JavaScript function using jQuery's AJAX

I'm currently working with jQuery and Ajax. Within my MainFile, I have the following code: <html> <head> <script src="Myscript.js"> </script> <script type="text/javascript"> $(doc ...

Incapable of merging various arrays together

I'm having difficulties merging the contents of arrays x and y to create array A. Unfortunately, I'm encountering multiple build errors in the process. The first error states "Consecutive declarations on a line must be separated by ';'" ...

Use angular, javascript, or FormIo to set the cookie value to a dropdown list within a form as it loads

Currently, I am utilizing the Formio form within my Angular application. The registration form in my app consists of various fields, including State and city dropdowns that load values from State and city Formio resources. Whenever a user clicks on the Reg ...

Automatically scroll the page upon loading if the user is at the top of the page

Is there a way to trigger an action only when the page is detected to be at the very top, without executing it otherwise? I think maybe using an if statement could work, but I'm not entirely sure how to go about it. For instance, I'd like the pa ...

Resizing an iframe dynamically based on the content of the URL without displaying a scroll bar using JavaScript

Within my application, there is a select drop-down menu that contains URLs. When a user selects a URL from the drop-down menu, I want to load that URL in an iframe with the appropriate content size. I am looking for a way to increase the height of the if ...

Angular JS - showcasing and modifying model within textarea while also ensuring updates occur when model is altered

I recently visited this helpful post: Angular JS display and edit model in textarea While the information was useful for my current project, I now need to enhance it by making the textarea update when model values change. I am currently working with the ...

Node.js async.series not functioning properly within Express application - response being triggered prematurely

In my Node.js Express application, I am setting cookies from various sources such as the server, Twitter, and a database. These tasks need to be executed in series to prevent callback complications. I am attempting to utilize Async.js to achieve this, but ...

Tips for accessing user input in JavaScript functions

In my ASP.NET code, I have created a dynamic image button and panel. Here is the code: Panel panBlocks = new Panel(); panBlocks.ID = "PanBlockQuestionID" + recordcount.ToString(); panBlocks.Width = 1300; panBlocks.Height = 50; panBlocks.BackColor = Color. ...

A guide to correctly importing a Json File into Three.js

I've been working on some cool projects in Blender and wanted to showcase one using threejs. However, I'm facing an issue where the object isn't displaying properly. Can someone guide me on how to correctly load a JSON file with keyframe ani ...

Error encountered when attempting to dynamically alter property values of an object using Typescript

Can anyone help me figure out how to dynamically change object property values based on the property type in Typescript? I want to replace all string properties with "***", but I keep running into an error that says Type '"***"' is not assignable ...

Creating a circular shape around a specific location on a map is a common task in Openlayers. Here's a

I have been attempting to create a circle based on the point/coordinate where a user clicks. While I know how to generate a point and found a function that can create a circle based on said point (similar to a buffer or range ring), it appears to only func ...

Unleashing the Power of RxJS with OR Conditions

I am working with two Observables. For instance, I am waiting for either an HTTP POST call or a WebSocket call to return so that I can proceed. Once either call returns, I need to verify the information until a certain condition is met. In the following e ...

Utilizing ngModel on input elements inside a custom directive, ensuring compatibility with other ng-* attributes

In my current project, I am working on developing a custom directive that acts as a wrapper around an input field. The main purpose of this directive is to simplify formatting, encapsulate animations, and enhance overall functionality. One of my goals for ...

The dynamic styles set by CSS/JavaScript are not being successfully implemented once the Ajax data is retrieved

I am currently coding in Zend Framework and facing an issue with the code provided below. Any suggestions for a solution would be appreciated. The following is my controller function that is being triggered via AJAX: public function fnshowinfoAction() { ...

Generating unique ObjectIDs for each object is crucial before saving documents in Mongoose

I need to generate a unique ObjectID for every object within my array. The challenge is that I am fetching products from another server using a .forEach statement and adding them to my array without a Schema that automatically creates an ObjectID.... Prod ...

Retrieve the radio button value without using a key when submitting a form in JSON

Looking to extract the value upon form submission in Angular, here is the code: In my Typescript: constructor(public navCtrl: NavController, public navParams: NavParams, public modalCtrl: ModalController, public formBuilder: FormBuilder, public alertCtrl ...