I have two classes defined, say Animal and Rabbit. Rabbit extends Animal and has some additional methods apart from those defined in Animal.
When the user runs my app, an instance of Animal is created (say myAnimal). Then depending on something that the user does, I may need to "enhance"/cast myAnimal into the inherited class Rabbit. Is this possible in Modern JavaScript so the additional methods defined in Rabbit may now be available to myAnimal?
#Etheryte 's suggestion worked. I defined a static method fromAnimal in Rabbit which creates a new Rabbit, sets properties of given Animal (myAnimal) and returns that instance. It includes all processing that myAnimal has undergone so far.
Thank you.
no you can not.
other way, you can create an instance of Rabbit - myRabbit with properties of myAnimal. (comment of #Etheryte above)
or use decorator of Typescript
Related
Looking at this simple code :
class Animal {
someField = 42;
animalFunc() {
console.log('animal')
}
}
class Lion extends Animal {
lionFunc() {
console.loge('lion')
}
}
let lion = new Lion();
console.log(lion)
Result in Chrome is :
As we can see, the instance methods are on the prototype (for each constructor function)
Question:
Why are fields, as opposed to methods, not on the prototype?
I mean , someField is in Animal not in Lion.
This is the intended behavior as seen in ECMAScript - 15.7.14 Runtime Semantics: ClassDefinitionEvaluation 25.f:
f. Else if element is a ClassFieldDefinition Record, then
i. If IsStatic of e is false, append element to instanceFields.
ii. Else, append element to staticElements.
and in 29.:
Set F.[[Fields]] to instanceFields.
So after we see that this is not a bug, let's see why:
In simple words:
As we know if we have a function on the prototype and we changed that function value, it would be changed to everyone.
If we put the property on the prototype and some instance change the property value it would change the property value in other instances as well and naturally this behavior should not happen so we put the properties on the instance itself.
For static properties this is the intended behavior as we only have one instance of them, this is why they are on the prototype
Extra:
this comment by Ranando D Washington-King from class fields proposal
The quote that I mention is about the problem of add an x data property to the prototype [...]:
Every instance has a copy of the object. The expectation may or may not be exactly that. Consider in Java, the ability to define nested classes. In that case, it is definitely the desire that every instance be able to see the exact same class definition. On the other hand, consider assigning an empty array. The likely intention is for each instance to have its own copy of that array for storing instance-specific values.
I want to access all of the properties of a class that will be defined when the constructor is called, so that I can implement a sort of interface for the class.
Say I had a class that defines the property hello, I would like to access it to check it has been implemented and the type assigned to it is correct. Problem is, since all non-static class properties are tied to an instance, I can't get at them without instantiating the class, which I can't do.
In this situation, is it possible to access hello?
class MyClass {
constructor () {
this.hello = 'greetings';
}
}
In this situation, is it possible to access hello?
Not without using a JavaScript parser (like IDEs do to try to infer instance mbmers). hello, as you say, doesn't exist as a property until/unless an instance is created. With a parser, you can (usually) determine what the property names will be, perhaps sometimes their initial values, but that's all.
I asked the following question on why classes are being used in a typically classless prototypal inheritance language like js: class use in a prototypal inheritance-based language
It seems they're "syntatic sugar" because the masses like classes... and that they're just regular js objects in the background.
Now id like to know what the differences are between js fake "classes" and classical classes?
If I'd compare JS "classes" with classes in Java, the first thing I'd say is that JS "class" is just an object whereas Java class is not.
An object has its own properties/fields/methods. This is also what a JS "class" can have. But a class in Java can not, only instances can.
Some people would probably say "Wait, but how about static fields and methods? I can do a static MyClass.myField field an MyClass.doSomething() method."
Yes, you can. But from my point of view they will just be "placed" in the MyClass namespace, they aren't really properties of the class:
You don't have this in static methods - but you can use this in class-level methods in JS "classes"
You can't make a class with a static method to implement a certain interface - assuming duck-typing as interface this is no problem in JS
So a Java class with static fields or methods is just a namespace for these fields and methods, nothing more. What makes it even more obvious is the fact that in a static method you can't find out which class you're in (without tricks):
Getting the class name from a static method in Java
So in JS pseudoclasses are first-class objects whereas in Java they are not.
I can think of this, in class inheritance, the parent objects are locked up. For instance:
class A
{
};
class B extends A
{
};
Here, A's definition is locked up, you can't add new properties once it is inherited.
In Javascript 'classes', you can:
var o = {x:2};
var b = Object.create(o);
o.y = 5;
b.y
>5
Do you see how we added a property after 'inheriting'? This is one difference, objects aren't really locked up(though you can do the locking up of objects by using Object.preventExtensions, Object.seal, Object.freeze), but by themselves the objects can be modified at any point of time and this link to the prototype object is maintained in the child object.
I just finished reading this very good article on prototypal inheritance in JavaScript, but was surprised to see how vehemently the author was against having properties defined in prototypes.
A common mistake when creating objects for the prototype chain, from programmers that come from classical OOP anyway, is to define common properties high up in the chain because they exist for all instances. We feel the need to define the property as if the abstract object described an interface. Yet there is no point in defining in a prototype a property that will be present in objects that descend from it. Javascript is not like Java : you don't declare in the base objects variables that will be different to all instances of the descendants. You declare a variable only on the level where it will be defined.
Take the name property of our animals. Since every animal has a name, it's natural to consider this property as common to all, and define it in the common denominator which is the Animal prototype. The thing is, Animal has no name. A Dog instance has a name.
In Javascript, you cannot say an Animal has a name. Animal is an object, not a definition, even if we use it like so. And that object has no name property. Why then is name referred to in Animal's methods if Animal has no name? Because Animal is abstract : it is not intended to be used by itself. this, in Animal, will never refer to Animal. It will refer to whatever object descends from Animal, dino for example. And dino has a name.
If I have a very complex set of classes that have, dozens of properties in common. I don't see how it's better to duplicate those properties and the work that goes into setting them up on each instantiable derived class when the work can be done once in the base class, even if that base class was meant to be 'abstract'.
For instance:
function Analysis(args){
args = args || {};
// Extract supported init properties from args
this.description = args.description;
this.host = args.host;
this.source = args.source;
this.identifier = args.identifier;
this.vendor = args.vendor;
this.agent = args.agent;
//etc...
}
function PortfolioAnalysis(args){
Analysis.call(this, args);
args = args || {};
this.portfolio = args.portfolio;
this.author = args.author;
//etc...
}
PortfolioAnalysis.prototype = Object.create(Analysis.prototype);
PortfolioAnalysis.prototype.constructor = PortfolioAnalysis;
function TreatyAnalysis(args){
Analysis.call(this, args);
args = args || {};
this.treaty = args.treaty;
this.terms = args.terms;
//etc...
}
TreatyAnalysis.prototype = Object.create(Analysis.prototype);
TreatyAnalysis.prototype.constructor = TreatyAnalysis;
//etc...
So the article is saying I should paste the initialization code for the properties description, host, source, etc. in each of the derived classes, and remove it from the base class.
I don't see why that's better, especially if there's a bunch of complex common logic around constructing these objects using those shared properties, what's so bad about defining them in the base class, and if it's so bad, is there a way around it that doesn't involve code duplication or having to define a separate '.init()' method?
So the article is saying I should paste the initialization code for the properties description, host, source, etc. in each of the derived classes, and remove it from the base class.
No. Your code is perfectly fine, exactly how it should be done.
And that article is saying that the properties like description, host etc should be placed on instances (like a new ThreatAnalysis(…), or even a new Analysis(…)), but not on Analysis.prototype - just what you are doing. There are some people who would "default", e.g. empty, identifiers etc on Analysis.prototype because they want to "declare" that every Analysis instance should have an identifier. That is rubbish, as the article explains.
To share your initialisation behaviour in the Analysis constructor is fine (as the article mentions, shared functions may be placed hight in the prototype chain). There's no need to inline it and make Analysis and empty object, even if it is abstract and will never be instantiated directly.
I don't think you get prototypal inheritance yet. The author is not saying “don't put the initialization code in the base constructor”. What the author is saying is “don't put properties on the base prototype”. It's totally different.
So you are allowed to do what you are currently doing. It's totally fine. What you shouldn't do however is put default values of properties on the prototype as it might cause problems. For example consider:
function Foo() {}
Foo.prototype.data = []; // you shouldn't do this
var a = new Foo;
var b = new Foo;
a.data.push(0);
alert(JSON.stringify(b.data)); // [0]
This is why you shouldn't share properties on the prototype. We modified the value of a.data but since data is shared amongst all instances of Foo we also modified b.data. Hence an invariant was violated.
Think about it like this:
The properties defined on the current object are its public properties. They are not shared.
The properties defined on the prototype of the current object are static properties. They are shared amongst the instances of the prototype.
Hence it's alright to define static properties, like the count of all the instances, on the prototype. However it's not alright to define public properties on the prototype because it may cause problems like the one above.
Dr. Alex Rauschmayer explains this better: http://www.2ality.com/2013/09/data-in-prototypes.html
Your code is fine because the this in your constructors always point to the current object, not the prototype. Hence you are not defining any properties on the prototype.
I think you got confused because of constructors vs prototypes. Perhaps this blog post would elucidate your doubts: http://aaditmshah.github.io/why-prototypal-inheritance-matters/
So the code below reflects the pseudo-classical version of inheritance in JavaScript.
function SynthOne() { // constructor 1
this.sound1 = "piano";
};
function SynthTwo() { // constructor 2
this.sound2 = "bass";
}
SynthOne.prototype = new SynthTwo; // overwrite constructor 1's prototype with constructor 2's
var synthsCombined = new SynthOne; // assign constructor 1 to variable
// this variable now has access to both constructors properties & methods
document.write(synthsCombined.sound1 + " ")
document.write(synthsCombined.sound2)
But let's change this to make sound1 and sound2 to simply sound.
Let's also assume that I really wanted to create an inheritance chain to access both of these "sounds" even if they were named the same thing. Is there a pattern in the JavaScript community or a coding convention that exist to deal with this kind of situation? Or am I just stuck?
Thank you
Child properties hide properties of the same name further up the prototype chain. Technically, you can still get access to the parent property like this:
Object.getPrototypeOf(synthsCombined).sound
Or the non-standard:
synthsCombined.__proto__.sound
But this probably isn't something you want to do. If you need both values, then name them different things.
it was simply something that entered my mind and I was curious about. I can see a situation where at the very least you combine constructors not realizing they have similar property/method names.
You hardly inherit from classes whose set of properties1 you do not know. If you subclass something, you often want to explicitly overwrite properties with more specific values - that's just what the shadowing is for.
In case you want to extend the set, you'd have to choose an unallocated name. In case of interface clashes (e.g. when extending the implementation), that's just a bug and either the base class or the child classes would need to change their identifier. Using descriptive names will lower the risk.
How to deal with this kind of situation?
If it's unwanted, fix the bug (this is not JavaScript-specific). If you deliberately have chosen the same property name, you can access the inherited value by manually ascending the prototype chain with Object.getPrototypeOf().
[1]: Speaking of both attributes and methods, as they're just properties in javascript
You could give one of your constructors a base property, which will get the properties from the inherited constructor:
function SynthOne() { // constructor 1
this.base = {};
this.sound = "piano";
SynthTwo.call(this.base);
};
function SynthTwo() { // constructor 2
this.sound = "bass";
}
SynthOne.prototype = Object.create(SynthTwo.prototype);
var synthsCombined = new SynthOne;
console.log(synthsCombined.sound); //piano
console.log(synthsCombined.base.sound); //bass
But from what it looks like you are trying to accomplish, maybe inheritance is not the right way for you. It might make more sense to create a generic Synth class and maybe a SynthCollection class, to combine different Synths.