Is there a way to use instanceof with strings in an array?

Hi,

Just FYI, working in the Build a Library challenge (https://www.codecademy.com/paths/front-end-engineer-career-path/tracks/fecp-javascript-syntax-part-iii/modules/fecp-learn-javascript-syntax-classes/projects/build-a-library).

However, this will be a simplified example for the question, obviously.

Is there a way you can access an array with strings with something like a foreach loop and check with instanceof if this is an instance of a certain class?

Something like this (hard-coded), which worked:

      if (this instanceof One || this instanceof Two || this instanceof Three){
        console.log('It is!\n');
      } else {
        console.log('It isn\'t!!!\n');
        throw new Error('Wrong number');
      }

but instead dynamically, so that if more elements (in this example, numbers) are added to the list, they’re checked, too?

With an array like ['One', 'Two', ...], I tried this:

numbersArray.forEach(number => {
        if (this instanceof number){
          console.log('It is!\n')
      } else {
        console.log('It isn\'t!!!\n');
        throw new Error('Wrong number');
      }

But I get a TypeError telling me 'Right-hand side of instanceof is not an object'. Yes, you’re right. It’s because it actually is a string, not the object I am checking. Same variable name, but different type.

Also tried wrapping number in [ and ] but it’s also a TypeError, telling me something similar ('Right-hand ... is not callable').

Is my only solution here to hard-code each class name (and use a thousand || :slight_smile: )? Am I missing something? I would appreciate any pointers here.

Thanks in advance!

You are comparing the instance (?) with itself: Either you apply forEach on the array of instances – then the instance can’t be onstanceof at the same time. Or you apply forEach on an array of class names – then the instance you want to check isn’t available in the iteration.

Not sure what you want to do with this, but maybe you could achieve it with this:

instancesArray.forEach(instance => {
  let currentClass = instance.constructor.name;
  switch(currentClass) {
  	case 'One' : 
    console.log('This is an instance of One')
    break;
  }
})

First of all, thank you so much for taking the time to analyze this and answer me.

Secondly, you are a saviour! Please let me take a minute of silence to thank you!

OK, now I will explain you what I’m trying to do, because you told why I would want to do that (and maybe you even know of a better way?) I know my example didn’t make justice. It’s just that trying to simplify code for asking a question proves a very difficult task sometimes. And this is one of those times. So please bear with me if the explanation is now a little long. It’s the best way I can summarize or simplify this. And thank you once again for your help and suggestions/feedback about the way I am doing it, since I am learning.

Now, what I have is like a parent class with a few children classes, right? Now the parent’s constructor takes one value as arg and others that are added within it.
So, that would be like:

class Parent {
    constructor(arg1){
        this._arg1 = arg1;
        this._prop = value;
        this_anotherProp = anotherValue;
        this.doThis();
    }
...
}

Now, the children will (obviously) receive that first arg, too, but also other args that differ on each child. And then also their own stuff created within. And here is the issue: one of the properties is added with a value that differs on each of them, so it depends on each constructor.

So:

class Child1 extends Parent {
    constructor(arg1, child1arg2, child1arg3){
        super(arg1);
        this._child1arg2 = child1arg2;
        this._child1arg3 = child1arg3;
        this._type = specificValueForChild1;
        this.doSomethingElse();
    }
...
}

// and then...

class Child2 extends Parent {
    constructor(arg1, child2arg2, child2arg3){
        super(arg1);
        this._child2arg2 = child2arg2;
        this._child2arg3 = child2arg3;
        this._type = specificValueForChild2;
        this.doSomethingElse();
    }
...
}

Most importantly, though, the parent does not assign this property (the type), only the children do.

And the issue I am having is I want to avoid a silent ‘error’ (although technically not an error per se) where

const nonsense = new Parent(arg1Here);

will go through but without assigning the type field. Literally, I am artificially trying to avoid that the Parent class can create actual instances, because I want the children to assign that important property (in my actual scenario, it’s a ‘media type’, so I have an external array with valid media types with which I want to contrast, that was my original problem). But now that I’ve seen your explanation, I did typeof this.constructor.name and it gave me a string so I can check, like you said or similarly, against it like:

      if (this.constructor.name === 'Parent'){
        throw new Error('Wrong type');
      } else {
        console.log('Cool type, go on then...');
       // or actually go ahead with the code here, like a validation.
      }
    

Sorry for the long text, and yet this is a simplification of the actual code! :upside_down_face: Just may I ask, do you think I am tackling this in an inefficient or inadequate way?

Thank you once again!

1 Like

From what I understand now, you want to create instances dynamically rather than getting the name of the constructor of each instance after creation?
Then neither of the above suggested solutions will work.

I don’t know how you store the arguments for the instances to be created. For the following example I assume that you have two separate arrays. One that stores the constructor names as strings and one that stores the arguments for the new instances:

const instancesToBeCreated = ['Child1', 'Parent', 'Child2'];
const instancesArgs = [['arg1', 'arg2', 'arg3'],
['arg1', 'child2arg2', 'child2arg3'],
['arg1', 'child3arg2', 'child3arg3']];

In order to be able to create a new instance by passing a string as a class name, you could create an object which stores the classes like a map:

// Shorthand property names (ES2015)
const classes = {
    Parent,
    Child1,
    Child2
}

Then write a new class which accepts the class names as strings as a first argument and the arguments for the instances passed in with the ES6 spread syntax:

class DynamicClass {
    constructor (className, ...args) {
        return new classes[className](...args);
    }
}

Then dynamically create your instances with a forEach loop. In the loop you can filter the class names and apply different behaviour:

const newInstances = [];

instancesToBeCreated.forEach((className, index) => {
	if(className === 'Parent') {
  	console.log('This is the Parent class, don\'t do anything');
    return;
  } else{
  	newInstances.push( new DynamicClass(className, ...instancesArgs[index]))
  }
})

newInstances.forEach(instance => console.log('constructor: ', instance.constructor.name));
// "constructor: ", "Child1"
// "constructor: ", "Child2"
1 Like

Wow, well! Huge thank you once again.

This is totally another league. You’re so right. Instead of allowing instances to be created, make like a what’s called, a superclass(?) :smiley: that does the heavy lifting with the validation before creating anything, which should actually contain the instantiation bit.

I will confess I’ve had to go through this like a number of times in order to wrap my head around it, and now I’ve only started trying to implement something like it on my specific codecase, but I am sure it will take me a few days to get this to even work haha. But it seems a magnificent idea and approach, and I will definitely update you here once it’s done.

In the meantime, huge thanks for your time and patience here. This has been a tremendous learning experience for me. Can’t thank you enough!

1 Like