What determines what super.prop refers to in a class? - javascript

I'm confused about why exactly super gets set the way it does inside of a class.
In static elements, super.x refers to Parent.x
In methods & field declaration assignments, super.x refers to Parent.prototype.x
MDN says super is either Parent or Parent.prototype, depending on whether the execution context is instance creation or class initialization. To me this makes sense for static elements, where super is seen during class initialization, and for field declarations, where super is seen during instance creation. However, aren't methods evaluated during class initialization, so that they can be put on the class's prototype, and not during instance creation?

aren't methods evaluated during class initialization
No
Methods are not cloned or evaluated, prototype chain is checked when method is not found in instance of the class
class Parent {
doSomething() {}
}
const parent = new Parent()
class Child extends Parent {}
const child = new Child()
console.log(parent.doSomething === child.doSomething)

Related

Can a child class overwrite a private field inherited from a superclass?

I'm playing around with ES6 classes in JavaScript and I'm wondering if it's possible for a child class to inherit private properties/methods from a superclass, while allowing the subclass to mutate this private property without using any "set" methods (to keep it read-only).
For example, say I want to create a static private property called #className in my superclass that is read-only, meaning you can only read from it using a method called getClassName() and you cannot access this property by doing class.className. Now, I make a new child class that extends this superclass. The child class will inherit #className and getClassName(), but, I would like #className to be initialized with a different value in the child class than in the superclass. So, in the superclass you could have: #className = 'Parent' but in the child class you would have #className = 'Child'.
My only problem is, it seems like you can't really do this. If I try declaring a new #className in the child class, the child class's getClassName() method still refers to the #className from the superclass. Here's the code I was playing with:
class Parent {
#className = 'Parent' // create private className
constructor() {}
getClassName() {
return this.#className; // return className from object
}
}
class Child extends Parent {
#className = 'Child' // re-define className for this child class
constructor() { super(); } // inherit from Parent class
}
new Child().getClassName() // --> this prints 'Parent' when I want it to print 'Child'
Does anyone have a solution to this? Or an alternative that achieves a similar affect?
JavaScript does not support directly accessing private properties inherited from another class, which is how private members are supposed to work. You seem to want the functionality of protected properties. As of 2022, JavaScript does not support protected properties or members of any kind. Why that is, I can't imagine, since other OOP languages have allowed said functionality since time immemorial.
If you have control over the code of the parent class, you can simulate protected properties by using symbols.
const className = Symbol();
class Parent {
[className] = 'Parent'; // create protected [className]
getClassName() {
return this[className]; // return [className] from object
}
}
class Child extends Parent {
[className] = 'Child'; // re-define [className] for this child class
}
console.log(new Child().getClassName()); // --> this prints 'Child'
I'm not sure why this snippet fails in the preview even with Babel. That exact code appears works in the console of every major browser I've tried.
The reason this works is that a Symbol in JavaScript is a primitive type that's guaranteed to be unique. Unlike other primitives, when used as a key in an object, it cannot be [easily] accessed or iterated over, effectively making it protected.
See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
Whether a property is truly "protected" is primarily determined by the scope within which you define the symbol (or whether you pass it around).
For instance, if the above code is module-scoped then that symbol will only be accessible to anything that's within that scope. As with any variable in JavaScript, you can scope a symbol within a function definition, or an if-statement, or any block. It's up to you.
There are a few cases where those symbols can be accessed. This includes the use of Object.getOwnPropertySymbols, Object.getOwnPropertyDescriptors, to name a few. Thus, it's not a perfect system, but it's a step above the old-school way of creating "private" members in JavaScript by prefixing names with an underscore.
In my own work, I use this technique to avoid using private class member syntax because of the gotcha you describe. But that's because I have never cared about preventing that capability in subclasses. The only drawback to using symbols is it requires a bit more code. Since a symbol is still a value that can be passed around, that makes it possible to create "loopholes" in your code for testing purposes or the occasional edge-case.
To truly eliminate leakage of conceptually protected properties, a WeakMap within the scope of the class definitions can be used.
const protectedClassNames = new WeakMap();
class Parent {
constructor() {
protectedClassNames.set(this, 'Parent');
}
getClassName() {
return protectedClassNames.get(this);
}
}
class Child extends Parent {
constructor() {
super();
protectedClassNames.set(this, 'Child'); // re-define className for this child class
}
}
console.log(new Child().getClassName()); // --> this prints 'Child'
A WeakMap is a key-value store that takes an object as a key and will dereference the object when said object has been garbage collected.
As long as the protectedClassNames WeakMap is only scoped to the class definitions that need it, then its values won't be possibly leaked elsewhere. However, the downside is that you run into a similar problem to the original issue if a class that's out of scope of the weak map tries to inherit from one of the classes that uses it.
WeakMaps aren't strictly necessary for this, but its function is important for managing memory.
Unfortunately, there appears to be no proposal in progress for adding protected members to the JavaScript standard. However, decorator syntax combined with either of the approaches described here may be a convenient way of implementing protected members for those willing to use a JavaScript transpiler.

function and static reserved word in JS class [duplicate]

Let's say I have two ES6 classes like this:
class Base {
static something() {
console.log(this);
}
}
class Derived extends Base {
}
And then I make a call like this:
Derived.something();
Note that I am making a call to a static method defined on the super class via sub class.
This does not give me errors. It prints
[Function: Derived]
So accessing this within a static method seems to work here.
I need a common static method for all sub-classes of a super class and I need to be able to know what sub-class is calling this method.
Now my question is whether using this within a static method is legal. I know these static methods become class methods, and hence this would naturally point to the class object they are called on. (The class object being the constructor.)
But I can't seem to find any definitive resource that states that this is allowed by the ES specification.
This looks like a good introduction to ES6 classes but does not talk about this with static.
Under typical circumstances, the this in any call to something.method() will refer to something as long as the function is not an arrow function, bound function, or something like that (and it is neither of those in this case).
Class inheritance, or even ES6, aren't really relevant here. All you need to know is that you are calling Derived.something(), so this will refer to Derived.
Yes, this is legal in static methods, that's the way this should be done.
this refers to class instance in prototype methods and refers to class constructor in static methods, unless a method was unbound from its original context.
Similarly, super refers to parent class prototype in instance methods and refers to parent class constructor in static methods.
As long as the static method is invoked as a member expression, e.g.
Derived.something();
as opposed to
const { something } = Derived;
something();
then this will refer to Derived. Derived.something() is identical to something.call(Derived) if Derived.something is stored to an intermediate variable, because that's how a member expression with a nested call expression is evaluated, essentially.

"super" keyword cannot access parent property in javascript

class Base{
constructor(val){
this.val = val;
}
basep = 'default1';
basef(){
return 2;
}
}
class Test1 extends Base{
constructor(val,name){
super(val);
this.name=name;
}
getp(){
console.log(this.name,this.val);
console.log(this.basep); //(in case of proerty) result: 'default1'. if "this" is changed to "super" ===> result: undefined
console.log(super.basef()); //(in case of methods) this and super both work.
}
}
test= new Test1(1,3);
test.getp();
In java 'this' and 'super' worked both fine to get parents property but it seems in javascript only this works.
why is the difference?
In Java, attribute names are put in a scope, and you can have two different attributes with the same name if they were created by different classes. This is not how it works in JavaScript with prototypical inheritance at all, though.
In JavaScript, data properties (that are created by assignment) are owned by the object itself. The parent constructor created the .basep property directly on the child instance. this.hasOwnProperty('basep') will be true.
The super keyword is used to access properties on the parent's .prototype object, regardless of what the current this value is, but still allows calling them on the this object. The basef method lives as a property on Base.prototype, basep does not.

Class Property in JavaScript

I have a question about class properties. I'll create an example for better understanding. Let's say we have a class.
class Person {}
And we want to add a property. How do we do that? From what I read there are 2 ways:
class Person {myProperty ='Something'} //As shown at https://javascript.info/class. If we use console.log(Person) the property will not show
Now let's create another class that extends Person, let's say Athlete and I want to change myProperty:
class Athlete extends Person{// If we use console.log(Athlete.myProperty ) it will show undefined
myProperty='Something else'// it changes the property
writeSomething() {
console.log(this.myProperty);
}
}
Now let's create a new object using Athlete as constructor
const athlete = new Athlete();
console.log(athlete)// It will have the property myProperty with value 'Something else'
My questions are:
Where is myProperty stored, I can't find it anywhere inside Person or Athlete?
Why can I access myProperty inside writeSomething()?
Why does myProperty appear inside athlete, when it didn't appear anywhere else?
Is this another way of writing properties as opposed to Person.myProperty or is it something else?
Thank you,
Elanvi
First of all, they are not "class properties". They are instance properties just declared upfront. This gives you the clear idea about what fields (properties) can be expected from the class instance.
Based on the documentation:
Public and private field declarations are an experimental feature (stage 3) proposed at TC39, the JavaScript standards committee.
Support in browsers is limited, but the feature can be used through a
build step with systems like Babel.
So, based on the proposal in the link, your code is for declaring public properties of the class instances:
class Person {
myProperty ='Something' // this means anyone can access the property
}
For private properties, they're suggesting this syntax:
class Person {
#myPrivateProperty ='Something' // this means only the instance methods have access
}
To answer your questions:
Where is myProperty stored, I can't find it anywhere inside Person or Athlete?
It's stored as an instance property of an instance of the Person class. Since Athlete extends it, an instance of Athlete also will be given the same property. Read Object Oriented Programming for more details.
Why can I access myProperty inside writeSomething()?
writeSomething() is the method of the instance, so it has access to any instance property. (public and private)
Why does myProperty appear inside athlete, when it didn't appear anywhere else?
It should appear in a Person instance as well. For example, you can write:
const person = new Person();
person.myProperty = 'new Something';
Is this another way of writing properties as opposed to Person.myProperty or is it something else?
It's just a declaration of instance properties made easier and more intuitive, and with implied access modifier (i.e. public).
Finally, if you want to declare a class property, you need to specify it as a static property like this:
class Person {
static myProperty = 'some value';
}
console.log(Person.myProperty);
I'll try to answer your questions as best I can:
Where is myProperty stored, I can't find it anywhere inside Person or Athlete?
Writing:
class Person { myProperty = 'Something' }
Is the same as writing:
class Person {
constructor() {
this.myProperty = 'Something';
}
}
My understanding is that writing it the first way actually adds the property definition to the constructor - you won't see myProperty anywhere on the prototype of Person, the property will not exist anywhere until an instance is created with new Person and the constructor is called.
Why can I access myProperty inside writeSomething()?
You can access myProperty inside writeSomething through the this context as it will refer to the instance of Person unless otherwise bound.
Why does myProperty appear inside athlete, when it didn't appear anywhere else?
The property isn't inside of Athlete per-se but without defining it's own constructor it will inherit the parent class constructor meaning Athlete instances will get the same property defined when they are instantiated. If you were to add a constructor to Athlete you would have to call the super constructor (i.e. Person constructor) at which time the property would be defined on the intstance.
Is this another way of writing properties as opposed to Person.myProperty or is it something else?
As described above, you can also define these instance properties in the constructor with this.myProp = 'my value'. You can read more here on how to use class fields.
Where is myProperty stored, I can't find it anywhere inside Person or
Athlete?
Public instance fields on classes are syntactic sugar for instantiating instance own properties from inside the class constructor.
class Person { constructor() { this.p1 = 'p1 value' } }
Given the following:
class Person { p1 = 'p1 value'; p2 = 'p2 value' }
class Athlete extends Person { p1 = 'p1 overridden value' }
The prototype property of the Person class is placed on to the prototype chain of the prototype property of the Athlete class.
console.log(Athlete.prototype.__proto__ === Person.prototype) // true
The prototype property of the Person class does not contain the p1 property, because it will be dynamically added by the default constructor of the Person class, when an instance is created.
console.log(Person.prototype.hasOwnProperty('p1')) // false
const p = new Person()
When newing-up a Person the constructor dynamically adds the property to the resulting instance.
console.log(p.hasOwnProperty('p1')) // true
const a = new Athlete()
When newing-up an Athlete the Person super constructor is implicitly called against the Athlete instance putting both p1 and p2 on the instance, finally the Athlete constructor is run and p1 is overwritten.
console.log(a.hasOwnProperty('p1')) // true
console.log(a.hasOwnProperty('p2')) // true
console.log(a.p1) // p1 value overridden
Why can I access myProperty inside writeSomething()?
This is just normal this behavior. When invoking a function as a method, the target of the method is the object it was invoked upon. In this case, the instance of Athlete.
1) Where is myProperty stored, I can't find it anywhere inside Person or Athlete?
Variables that can be accessed inside Person or Athlete are called static class variables. You can define them by either doing:
Person.staticProperty = 'Something';
or
class Person {
static staticProperty = 'Something';
}
This variable can be accessed inside your class:
class Person {
static staticProperty = 'Something';
logStaticProperty() {
console.log(Person.staticProperty);
}
}
2) Why can I access myProperty inside writeSomething()?
When you create an instance of a class using new, you can attach variables that are isolated to that single instance. The syntax being used in the constructor attaches the variable to the instance.
3) Why does myProperty appear inside athlete, when it didn't appear anywhere else?
See #2.
4) Is this another way of writing properties as opposed to Person.myProperty or is it something else?
Using the constructor method is functionally equivalent
class Person {
constructor() {
this.myProperty = 'Somethin';
}
}

What's the difference between static and instance members?

I want to know what the difference between these two functions at this sample class in javascript?
class Sample{
constructor(){}
sampleOne(){
console.log('this is test')
}
static sampleTwo(){
console.log('this is test too')
}
}
The first one is an instance method. This means, you have to create a new instance of the Sample class to call it:
let instance = new Sample();
instance.sampleOne();
The second one is a static method, so you don't have to have an instance of the class to call it:
Sample.sampleTwo();
Javascript doesn't have classes, therefore explanations in terms of "class" and "instance" are inaccurate. In javascript, there are only objects, which have constructors (functions) and prototypes (other objects). The ES6 "class" syntax is just a decorator, which simply translates java-like class declarations into prototypes, namely:
classes are turned into constructors (functions):
class Sample => function Sample()
ordinary methods are attached to the prototype property of the constructor, and copied to the object.[[Prototype]] later on when you do new Sample():
sampleOne => Sample.prototype.sampleOne
"static" methods are attached directly to the constructor:
sampleTwo => Sample.sampleTwo
Illustration:
static is a declaration to say this method sampleTwo can be called on the class level and not the instance level. sampleOne however, is an instance method and so you would need to instantiate an instance of your sample class.
const test = Sample();
test.sampleOne(); //works
test.sampleTwo(); //won't work cause it is defined on class level
Sample.sampleOne(); //won't work cause not static defined
Sample.sampleTwo(); //works because it is static.
Static methods are not bound to a class instance. But to the class itself. So while convenient you will not have access to this as there is no instance to point to.
Static methods are called on the class itself.
For example, the following would work perfectly
Sample.sampleOne()
// undefined
while
Sample.sampleTwo()
// this is a test two

Categories

Resources