JS borrows the model of classes, interfaces and objects from other languages, but has its own style of doing the things.
The Mozilla documentationis calling the HTMLElement an interface. In inherits from Element, which is a class in turn. This would not work in Java.
class ContentCardExample extends HTMLElement {
I find this example of javascript, that does extend the interface. In Java an interface would be implemented. This brings up a bunch of questions for me.
1.) Do I inherit the methods from Element when I extend HTMLElement or are they lost?
2.) If so, is HTMLElement a class and the wording of an interface is just an inaccuracy?
3.) Does HTMLElement add new methods, that can use or does it force me to implement methods in sense of a Java interface?
This question is related to this other question which expresses the problem in terms of prototypes, thought. At least in syntax it is not the same problem.
1.) Do I inherit the methods from Element when I extend HTMLElement or are they lost?
Your new subclass inherits them, via the prototype chain.
2.) If so, is HTMLElement a class and the wording of an interface is just an inaccuracy?
It's not inaccurate, it's a documentation/specification concept. What you have at runtime is a concrete implementation of that interface provided by the host environment (the browser's DOM implementation, in this case).
3.) Does HTMLElement add new methods, that can use or does it force me to implement methods in sense of a Java interface?
The methods are provided, again via the prototype chain. For example, click is a method added by HTMLElement (it doesn't exist on Element). You can see that a subclass inherits it:
// `click` isn't part of `Element`
console.log("click" in Element.prototype); // false
// It's provided by `HTMLElement`
console.log("click" in HTMLElement.prototype); // true
// If we define our own element type extending `HTMLElement`...
class MyElement extends HTMLElement {
}
customElements.define("my-element", MyElement);
// ...and create an instance of it...
const x = document.createElement("my-element");
// ...our custom element instance inherits from `HTMLElement`:
console.log(x instanceof HTMLElement); // true
// ...and via that from `Element`:
console.log(x instanceof MyElement); // true
// ...and so it has `click`:
console.log(typeof x.click); // "function"
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.
TLDR: Are there any pitfalls in using this.constructor(props) in a class method to duplicate that class?
I have a use case where defining class models for entities proves quite useful for management and processing throughout a complex system.
These entity model classes all require a set of common methods.
I do not wish to duplicate code across multiple entity model classes. I am exploring the use of a BaseEntityModel class which can be extended by all other model classes requiring the common functionality.
One of these methods is a clone() method, which provides a deep clone of the object as the ExtendedEntityModel type.
clone() will be declared on the BaseEntityModel class. I want to preserve the class name and properties when calling clone from a ExtendedEntityModel object which has extended BaseEntityModel class.
I need a way of creating a new instance of a ExtendedEntityModel through the BaseEntityModel without having to explicitly know what it is when calling clone(). In otherwords, I wanted to avoid using new ParentClassEntity() because the BaseEntityModel methods need to work for all extensions of it.
I did some searching but could not find much on this pattern. After some tinkering, I found that calling this.constructor(props) does exactly what I want. See below class, and clone() function
BaseEntityModel.js
class BaseEntityModel {
constructor({ entityType, dataKeys }) {
this.entityType = entityType;
this.dataKeys = dataKeys;
}
/**
* Provide deep clone of self, Preserving extended class properties
*/
clone() {
return new this.constructor(JSON.parse(JSON.stringify(this))); //<-- here, this.constructor
};
}
ExtendedModel.js
class ExtendedModel extends BaseEntityModel {
constructor({param1, param2} : ExtendedModel) {
super({ entityType, dataKeys });
this.param1 = param1;
this.param2 = param2;
}
}
and then usage
var extendedModel1 = new ExtendedModel({param1, param2};
var clonedExtendedModel = extendedModel1.clone(); //returns a cloned version and of type ExtendedModel
I cannot find much on calling this.constructor. Can anyone provide some insight into why this might be a bad choice, or confirm that this is not a javascript bomb waiting to go off?
See CodeSandbox of working implementation
How can I get name of object's class? I mean "Process" in this example
I see two ways to get it. First one is to write a getter in this class like
getClassName(){return "Process"}
But I suppose it will be an error if I try to call this method in object which doesn't belong to this class and hasn't got method like this.
And second one is using object instanceof Process. But maybe there is some way to make it better and more correctly?
You can get it from name on constructor:
console.log(object.constructor.name);
When you do ex = new Example, for instance, in the normal course of things that makes Example.prototype the prototype of the object that was created (ex), and the object inherits a constructor property from that object that refers back to the constructor (Example).
I say "in the normal course of things" because there are various ways those normal relationships can be changed. For instance, code could have overridden the constructor property with an own property on the object (ex.constructor = somethingElse;). To rule out that specific scenario, you could use:
console.log(Object.getPrototypeOf(object).constructor.name);
Live example:
class Example1 {
}
const e1 = new Example1();
console.log(e1.constructor.name); // "Example1"
class Example2 {
constructor() {
this.constructor = "I'm special";
}
}
const e2 = new Example2();
console.log(Object.getPrototypeOf(e2).constructor.name); // "Example2"
The TC39 committee members that specify JavaScript were happy enough to use the instance's constructor property in Promises when building the new promise that then and catch return (see StepĀ 3 here which goes here and reads constructor from the instance) (and in some other places), so you wouldn't be out on your own if you also used it. They don't even go to the prototype of the instance.
But yes, just for completeness, even if you go to the prototype for it, it's still possible for that to lead you astray, since the prototype's constructor property can also be mucked with:
class Example {
}
Example.prototype.constructor = Object; // Why would anyone do this? People are weird.
const e = new Example();
console.log(Object.getPrototypeOf(e).constructor.name); // "Object"
It's also possible to redefine the name on a function:
class Example {
}
// Why would someone do this? People are weird.
Object.defineProperty(Example, "name", {
value: "flibberdeegibbit"
});
const e = new Example();
console.log(Object.getPrototypeOf(e).constructor.name); // "flibberdeegibbit"
So...caveat user.
Note that the function name property is new as of ES2015 (as is class syntax). If you're using class syntax via a transpiler, it may or may not set name correctly.
Generally object instanceof Process is desirable if it's known for sure that object originates from this class/function. There may be situations where this won't be so. The appearance of several Process can be caused by iframes, multiple package versions, etc.
There is name property that already exists in regular functions class constructors. A known pitfall is that it will be mangled in minified code, so it is generally useless in browser JS, and its use can be considered an antipattern. name cannot be reassigned (in some browsers), so a separate property is needed to identify the class.
The proper way is to avoid this problem
But I suppose it will be an error if I try to call this method in object which doesn't belong to this class and hasn't got method like this.
is to use a getter:
class Process {
get className() { return 'Process'; }
...
}
Or a property:
class Process {
...
}
Process.prototype.className = 'Process';
As a result, there may be several Process classes that have Process className identifier. This may be desirable or not. While instanceof associates class instance with one particular class.
Use .constructor.name on the object. Each object's constructor by default refers to his creation function, which has a name property. It returns the name of the function.
class SomeClass {
}
const obj = new SomeClass();
console.log(obj.constructor.name);
Use the name property as follows:
class Process {}
console.log(Process.name);
const process = new Process;
console.log(process.constructor.name);
This is the same way it works for normal prototypal inheritance using functions.
JavaScript instanceof operator uses a prototype chain to return the type of object.
Let say, If i have below hierarchy.
Rabit->Animal->Object->Null
Now if i wanted to know any instance is actually a rabit or animal or Object, I can not do this.
if (rabbitObj instance of Animal) // true
if (rabbitObj instance of Rabit) // true
if (rabbitObj instance of Object) // true
How can get rabbitObj is actually a Rabbit.
How i can get actual type of instance, instead of following prototypical chain.
You are trying to use JavaScript's delegation behavior as class based languages inheritance but they are different things. In JavaScript you must think in terms of objects and their prototypes, and understand how the delegation can work for you to achieve what you are looking for instead of the inheritance.
Read more about the prototype and delegation systems in the You Don't Know JS: this & Object Prototypes book, specifically the Prototypes chapter.
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.