Way to test which "sibling class" an object is in Javascript? - javascript

Say I have a base class "Pet" and child classes "Dog" and "Cat". Is there a generic way I can take an arbitrary object I know is a "Pet" and test to see if it is "Dog" or "Cat"?

You can either ask the object for its constructor.name property, or if you have a reference to the class, you can use the instanceof operator:
class Pet {
constructor(name) {
this.name = name;
}
}
class Cat extends Pet {
sayHello() { console.log(`${this.name} miaows`) }
}
class Dog extends Pet {
sayHello() { console.log(`${this.name} barks`) }
}
const goofy = new Dog('Pluto');
const garfield = new Cat('Garfield');
console.log(goofy.constructor.name);
console.log(garfield.constructor.name);
console.log(goofy instanceof Dog);
console.log(garfield instanceof Cat);
Please note that Pet shouldn't know about the existence of Dog or Cat (and also neither of the latter two should know anything about the existence of the other).

Related

Sharing a function between different methods of class

Is there a way to declare a function inside class?
If yes, I'm trying to declare function age() inside the class, but i think it is not possible on JavaScript or maybe I'm wrong.
I do not want to put it outside the class because the code looks more organized like this. What are the options available?
My code is below.
class Animal {
constructor(name) {
this.name = name;
}
cat() {
console.log(`Meow! It is cat and his name is ${this.name}`);
console.log(age('cat'));
}
dog() {
console.log(`Au! It is dog and his name is ${this.name}`);
console.log(age('dog'));
}
function age(animal){
if(animal=='cat') return 7;
if(animal=='dog') return 5;
}
}
const fluffy = new Animal('Fluffy');
fluffy.cat();
const billy = new Animal('billy');
billy.dog();
You've already declared functions inside of the class, just do the same for age. To access it, you need to use this.age since JavaScript doesn't look for class instance variables/functions by default.
class Animal {
constructor(name) {
this.name = name;
}
cat() {
console.log(`Meow! It is cat and his name is ${this.name}`);
console.log(this.age('cat'));
}
dog() {
console.log(`Au! It is dog and his name is ${this.name}`);
console.log(this.age('dog'));
}
age(animal) {
if (animal == 'cat') return 7;
if (animal == 'dog') return 5;
}
}
const fluffy = new Animal('Fluffy');
fluffy.cat();
const billy = new Animal('billy');
billy.dog();
As others already said cat and dog are already functions of Animal (member functions).
If you don't want that the age function is a member function (belongs to an instance of Animal) you can make it static:
class Animal {
constructor(name) {
this.name = name;
}
cat() {
console.log(`Meow! It is cat and his name is ${this.name}`);
console.log(Animal.age('cat'));
}
dog() {
console.log(`Au! It is dog and his name is ${this.name}`);
console.log(Animal.age('dog'));
}
static age(animal){
if(animal=='cat') return 7;
if(animal=='dog') return 5;
}
}
const fluffy = new Animal('Fluffy');
fluffy.cat();
const billy = new Animal('billy');
billy.dog();
If you don't want to make it accessible outside of Animal than make it private:
class Animal {
constructor(name) {
this.name = name;
}
cat() {
console.log(`Meow! It is cat and his name is ${this.name}`);
console.log(Animal.#age('cat'));
}
dog() {
console.log(`Au! It is dog and his name is ${this.name}`);
console.log(Animal.#age('dog'));
}
static #age(animal){
if(animal=='cat') return 7;
if(animal=='dog') return 5;
}
}
const fluffy = new Animal('Fluffy');
fluffy.cat();
const billy = new Animal('billy');
billy.dog();
However having a private static function is rarely useful. The intent of a static method is that it logically belongs to the class but performs a task that does not require an instance of that class.
Hi Jessica You can put it as a method if you want.
But I think anyway in this case you should have a class for cat and another one for dog.
Or pass in the constructor the age but it will mean that the age will not be related to what is the animal.
Long story short you can decide or to create a class for each or to create a method inside the class.
You can create a function in a class, but the correct syntax requires you to omit the 'function' keyword. Also use this keyword to refer to the current object.
class Animal {
constructor(name) {
this.name = name;
}
cat() {
console.log(`Meow! It is cat and his name is ${this.name}`);
console.log(this.age('cat'));
}
dog() {
console.log(`Au! It is dog and his name is ${this.name}`);
console.log(this.age('dog'));
}
age(animal){
if(animal=='cat') return 7;
if(animal=='dog') return 5;
}
}
const fluffy = new Animal('Fluffy');
fluffy.cat();
const billy = new Animal('billy');
billy.dog();
well I can come up with 3 way to define some functionality in a class
number one: method ,it is the common one that you are likely to see in class based codes
class Animal{
//rest of class
age(){
//your function goes here
}
}
you can latter access it with an instance of class
const cat = new Animal() cat.age()
number two: static ,it is somewhat different because it belong to class itself
class Animal{
//rest of class
static age(){
//your function goes here
}
}
in this case you don't need to create an instance of class
Animal.age()
and lastly: treating your function as property it is not different from using method but I would recommend method if you want to use this functionality often there are minor differences in** memory usage**
syntax goes like this
class Animal {
constructor(name) {
this.age= ()=>{//your function}
}}
use age like this
const cat = Animal()
, {age} = cat
age()

Create Child Class From Parent (ES6 Classes in Javascript)

What I am trying to do
I am trying to create a child (sub) class by initiating a parent class with the child type as a parameter, and I am wondering how to do this.
For example, say I have the following starter code:
class Animal{
constructor(settings){
//parent value
this.name = settings.name;
}
//parent function
sayName(){
console.log(`My name is ${this.name}.`);
}
}
class Frog extends Animal{
constructor(settings){
super(settings);
//child-specific value
this.isTreeFrog = settings.isTreeFrog;
}
//child function
livesInTheForest(){
return this.isTreeFrog;
}
}
class Rabbit extends Animal{ [...] }
class Whale extends Animal{ [...] }
I want to be able to write:
let barry = new Animal({
animalType: "frog",
name: "Barry",
isTreeFrog: false
})
(rather than let barry = new Frog({name: "Barry", isTreeFrog: false}))
and have barry be a frog, meaning I can write things like this:
barry.sayName() //should print 'My name is Barry'
console.log(barry.livesInTheForest()) //should print 'false'
What I have Tried
I have tried two different ways to achieve this, but both are a bit hacky and don't achieve exactly what I want.
The first involves having a value in the Animal class which stores the child in. For example, in the constructor for Animal I might have something like this:
if(settings.animalType === "frog"){
this.animal = new Frog(settings);
}else [...] //other animal types
There are two main problems with this:
I have to call child functions like this: barry.animal.livesInTheForest(), which creates inconsistency as the parent functions can be called without the .animal.
The child classes (e.g. Frog) can no longer be child classes, as otherwise I will get too much recursion as it keeps trying to call itself with super().
I thought of a second method as well, which works like this:
In the parent (Animal) constructor:
//make sure this isn't being called from the child class
if(settings.animalType !== null){
if(settings.animalType === "frog"){
//set settings.animal null to avoid too much recursion
//this means that I can't set this.animalType though, so I can't access barry.animalType
settings.animalType = null;
//Is this something I can do?!
this = new Frog(settings);
} else [...] //other animal types
}
This works (I think), but I now can't set this.animalType to settings.animalType, meaning I can't write barry.animalType and get frog.
Also, this seems really hacky to me and I can't help thinking that there must be a better way to do this.
class Animal {
static create (settings) {
return new this.subClasses[settings.type](settings)
}
}
class Rabbit extends Animal {}
class Frog extends Animal {}
class Whale extends Animal {}
Animal.subClasses = { frog: Frog, rabbit: Rabbit, whale: Whale }
const animals = ['frog', 'rabbit', 'whale'].map((type) => Animal.create({ type }))
console.log({ animals })

what is an elegant mixin/composition syntax for ES6 classes?

I'm looking for a clean and simple way to mixin methods to different classes. Most of the examples I've been able to find use the JS prototype, like this
Example code:
const _ = require("underscore")
let actions = {
speak() {
console.log(this.name + " animal speak")
},
look() {
console.log(this.name + " animal looks")
}
}
class Dog {
constructor(name) {
console.log("new Dog", name)
this.name = name
// modify instance and return
let that = _.extend(this, actions)
return that
}
speak() {
console.log(this.name + " dog speaks")
}
bark() {
console.log(this.name + " dog barks")
}
}
function test() {
let rover = new Dog("rover")
// speak in "actions" overrides Dog.speak method
rover.speak() // => rover animal speak
// runtime works but ts-lint doesn't like it
// look property doesn't exist on type 'dog'
rover.look() // => rover animal looks
// from dog since the method doesn't exist on actions
rover.bark() // => rover dog barks
}
test()
So to use the prototype I could modify the above as:
Object.assign(Dog.prototype, actions)
and then just use a vanilla constructor that returns the this
class Dog {
constructor(name) {
this.name = name
}
...
}
Object.assign(Dog.prototype, actions)
In both cases the mixin speak method will replace the Dog Class speak method, ok.
So my question is: if there is any other simpler/cleaner method to mixin methods across Classes?
And is there anything wrong with the above in terms of adding to a prototype? Is that creating copies of the actual methods? If it's only in the prototype and not every instance, I guess it's OK, but not entirely clear of any memory implications there.
As an alternative to using mixins you can use inheritance using extends to override methods on a class.
The disadvantage of inheritance compared to mixins is you can only extend one class at a time, but you can use multiple mixins. So it depends on what your use case is for which one you use.
Here is an example:
class Animal {
constructor(name) {
console.log("new", this.constructor.name, name)
this.name = name
}
speak() {
console.log(this.name + " animal speak")
}
look() {
console.log(this.name + " animal looks")
}
}
class Dog extends Animal {
constructor(name) {
super(name)
}
speak() {
console.log(this.name + " dog speaks")
}
bark() {
console.log(this.name + " dog barks")
}
}
const dog = new Dog('Fred')
// Will call the overridden speak method on Dog
dog.speak() // Fred dog speaks
// Will call look on Animal as it hasn't been overriden in Dog
dog.look() // Fred animal looks

JavaScript How to get subclasses? [duplicate]

I have code like this
class Animal{}
class Dog extends Animal {}
class Cat extends Animal {}
class Donkey extends Animal {}
I want to look at all of the classes in my application's universe, and when I find one that descends from Animal, I want to create a new object of that type and add it to the list. This allows me to add functionality without having to update a list of things. So I can avoid the following:
var animals = [];
animals.push( new Dog() );
animals.push( new Cat() );
animals.push( new Donkey() );
PS: I don't want to add extra functionality to my classes or call them explicitly.
I think you could make use of decorators. For instance, you could create #Extends() one and provide base class as an argument, e.g. #Extends(Animal). Inside the decorator function, you could take the name of the class decorated with #Extends and and put it into an array or an object. Don't know if it is applicable in browsers, but it should be. In Node with TypeScript I would do something like:
import { MyClassMetadata } from './';
export function Extends(parent): (...args: any[]) => void {
return (target: object): void => {
MyClassMetadata.someVariableToStoreChildren[parent.constructor.name].push(
target,
);
}
}
Then you can access the MyClassMetadata variable that stores array of children of a given class and use it the way you want. You can play with it and get the desired result.
What about this:
class Animal {
static derived = new Set();
}
class Dog extends Animal {
static dummy = Animal.derived.add(this.name);
}
class Cat extends Animal {
static dummy = Animal.derived.add(this.name);
}
class Donkey extends Animal {
static dummy = Animal.derived.add(this.name);
}
console.log(Animal.derived);
I tried this in a TypeScript environment. The result:
Set(3) {"Dog", "Cat", "Donkey"}
without instantiating a class.
Here what I discovered so far
http://jsbin.com/xiroyurinu/1/edit?js,console,output
class Animal{}
class Dog extends Animal {}
class Cat extends Animal {}
class Donkey extends Animal {}
var animals = getAllSubclasses(Animal);
console.log(animals.map(function(c){ return new window[c] })) // creates objects
document.body.innerText = animals; // Dog, Cat, Donkey
and the magic
function getAllSubclasses(baseClass) {
var globalObject = Function('return this')();
var allVars = Object.keys(globalObject);
var classes = allVars.filter(function (key) {
try {
var obj = globalObject[key];
return obj.prototype instanceof baseClass;
} catch (e) {
return false;
}
});
return classes;
}
The main disadvantage of this method is that I can not use ES6 module import and have to do old fashioned and simple contatenation of files, but this is still better that nothing.
PS: still wait for better answer
UPD: and ye, i know that to use this all classes must be defined globally, that's why i search for better way to do this..
It is not possible. You can never know e.g. about local classes defined inside some function, or privately in another module. And that's by design. It would be unmodular and break encapsulation.
Also, the set of classes is not static in JavaScript. You can create new classes dynamically open-endedly.
If you think you want such functionality then I strongly suggest you're holding it wrong. What are you trying to achieve?

js get chain of constructor names

I generate a chain of constructor names based on inheritance:
class Person {
}
class User extends Person {
}
$userClassIdentifier = generateIdentifier(User.constructor)
// userClassIdentifier = 'Person.User'
function generateIdentifier(constructor) {
if (constructor.prototype && constructor.prototype.__proto__
&& constructor.prototype.__proto__.constructor && constructor.prototype.__proto__.constructor.name) {
return `${constructor.prototype.__proto__.constructor.name}.${constructor.name}`
}
return constructor.name
}
this works for one level of inheritance. But it seems very hacky/ugly. Is there a nicer way to do it and to make it work for an unknown number of levels deep?
If I have SUV extending Car extending Vehicle I expect 'Vehicle.Car.SUV'
I'd use getPrototypeOf and a loop, starting with the constructor you want to start with, and ending when that constructor's prototype is Function.prototype or null:
function getLineage(ctor) {
let name = ctor.name;
while ((ctor = Object.getPrototypeOf(ctor)) != Function.prototype && ctor) {
name = ctor.name + "." + name;
}
return name;
}
class Vehicle { }
class Car extends Vehicle { }
class SUV extends Car { }
console.log(getLineage(SUV));
One suggestion I'd make, though, would be to use a different separator rather than ., as . seems to suggest that (say) SUV is a property of Car, which of course it isn't. :-)

Categories

Resources