How does property lookups after calling super() in subclass actually work - javascript

I've a simple example from MDN.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
constructor(name) {
super(name); // call the super class constructor and pass in the name parameter
}
speak() {
console.log(this.name + ' barks.');
}
}
let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.
Now, in subclass Dog how does this.name works under the hood. Since this refers to Dog class instance and name is not something which exists on Dog instance. So to access that we use super call which invokes parent's constructor. I get that it looks up.
But can someone please explain via the prototype mechanism (I'm comfortable in understanding the prototype lookup and chaining mechanism).
I'm sure deep down it will boil down to that but not clear about intermediate steps in between. Thanks!

this refers to Dog class
No, this refers to the instantiated object. The instantiated object has an internal prototype of Dog.prototype, and Dog.prototype has an internal prototype of Animal.prototype.
Since this refers directly to the instantiated object (in both constructors, and in all of the methods),
this.name = name;
puts the name property directly on that object, so it's completely fine to reference d.name, or, inside one of the methods, this.name:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a noise.');
}
}
class Dog extends Animal {
constructor(name) {
super(name); // call the super class constructor and pass in the name parameter
}
speak() {
console.log(this.name + ' barks.');
}
}
const d = new Dog('Mitzie');
const dProto = Object.getPrototypeOf(d);
const secondProto = Object.getPrototypeOf(dProto);
console.log(dProto === Dog.prototype);
console.log(secondProto === Animal.prototype);
console.log(d.hasOwnProperty('name'));

Actually, what I wanted to ask was under the hood. So, here's the answer based on #Jai's pointer in comments that I was looking for.
I ran the class based code through es5compiler or any compiler and got this conversion
var Dog = /** #class */ (function (_super) {
__extends(Dog, _super);
function Dog(name) {
return _super.call(this, name) || this;
}
Dog.prototype.speak = function () {
console.log(this.name + ' barks.');
};
return Dog;
}(Animal));
So basically
return _super.call(this, name) inside Dog function explains the confusion of this reference inside speak method of class Dog. It changes the context through call()

Related

Using call() method vs setting prototype property for inheritance

I'm a somewhat beginner, I used to know the very basics but stopped programming after that.
In JavaScript's Prototypal Inheritance, I've found/learned two ways of doing this.
Using the prototype property of a constructor:
function Mammal(name) {
this.name = name,
this.order = "Mammal",
this.sayHi = function() {
console.log("My name is " + this.name);
}
}
function Cat(name) {
this.name = name;
this.family = "Felidae";
}
Cat.prototype = new Mammal();
let lion = new Cat("Lion");
Or by calling the Mammal.call(this, name) method.
What are the differences between the two results? Which one is to be preferred if they're different?
Usually, we use class keyword to define a class in many program languages. In Javascript you can define your classes using simple and clear syntax using class keyword.
Either you can use the two ways you mentioned, you can use class keyword:
class Mammal {
constructor(name) {
this.name = name;
this.order = 'Mammal';
}
sayHi() {
console.log('My name is ' + this.name);
}
}
class Cat extends Mammal {
constructor(props) {
super(props);
this.family = 'Felidae';
}
}
let lion = new Cat('Lion');
lion.sayHi();
Read more about classes on MDN or W3S

Unable to use property from constructor

I'm creating two ES js Classes (Person, Teacher)
class Person {
constructor (name = "no name") {
this.name = name
}
Teacher inherit from Person
class Teacher extends Person {
constructor(name, degree){
super(name)
this.degree = degree;
}
}
In the teacher constructor, I have a new property called degree.
when I create a new property called Full name that takes name and degree. Degree shows as undefined. Although when I log object teacher property is there. It looks like a delay issue. But, shouldn't I be able to access property right away?
class Person {
constructor(name = 'no name') {
this.name = name;
}
}
class Teacher extends Person {
constructor(name, degree) {
super(name);
this.degree = degree;
}
fullname = this.degree + this.name;
printFullName() {
console.log(this.fullname);
}
}
let person = new Teacher('Adam', 'MS.');
console.log(person);
person.printFullName(); // undefined Adam
https://repl.it/#adbarani/TrimBrilliantPaintprogram#index.js
This is the behaviour specified in the MDN documentation:
Public instance fields are added with Object.defineProperty() either at construction time in the base class (before the constructor body runs), or just after super() returns in a subclass.
Your code example is in the second case. The order of execution is thus:
Execute super() in case your class is a subclass
Define the public instance fields on this
Execute the rest of the constructor
If you need a work around, then don't define fullname with the field syntax, but assign to this.fullname as part of the constructor function.
Update fullname to a getter to compute the property value when it is accessed:
class Teacher extends Person {
constructor(name, degree) {
super(name);
this.degree = degree;
}
get fullname() {
return this.degree + this.name;
};
printFullName() {
console.log(this.fullname);
}
}
It sounds like you want fullname to be a property, why not set it in the constructor then?
class Teacher extends Person {
constructor(name, degree) {
super(name);
this.degree = degree;
this.fullname = this.degree + this.name;
}
printFullName() {
console.log(this.fullname);
}
}
Otherwise just change printFullName to do the work for you:
printFullName() {
console.log(this.degree + this.nam);
}
Hopefully that helps!

Javascript: Using super to call parent prototype function inside child prototype function

I have a question about prototypes and the usage of super in JS.
I have a parent class Person. I have hooked a new toString() function to Person.prototype .
Then, I have a child class Teacher (extends Person). I am trying to override the toString() method on its prototype too, however I would like to reuse the result of the toString() method of its parent.
I attempted to call the toString() method of the super object in the Teacher function, but I get an error saying that super is undefined.
Why is that?
Is it possible to reuse a method of the parent through super in a prototype function of the super's child?
Now, I'm aware that the issue can be solved in another way, but I'm still curious if it's doable in the manner above.
Here's some reference code:
class Person {
name;
email;
constructor(name, email){
this.name = name;
}
}
Person.prototype.toString = function(){
return `name: ${this.name}`
}
class Teacher extends Person{
constructor(name, subject){
super(name);
this.subject = subject;
}
}
Teacher.prototype.toString = function(){
return this.super.toString() + ` subject: ${this.subject}`
}
let teacher = new Teacher("testname", "testSubject");
console.log(teacher.toString());
I don't believe super is available when you define a function on the prototype like that. Instead, you should define your methods within the class and use super.toString. Like this:
class Person {
constructor(name, email) {
this.name = name;
}
toString() {
// you were missing `this` here
return `name: ${this.name}`;
}
}
class Teacher extends Person {
constructor(name, subject) {
super(name);
this.subject = subject;
}
toString() {
return super.toString() + ` subject: ${this.subject}`;
}
}
let teacher = new Teacher("testname", "testSubject");
console.log(teacher.toString());

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

Inline binding of external function to class method

Let's say I have a function that returns a function like this:
function createGreeter(logger) {
return function greet(greeting) {
logger.log(greeting + ', ' + this.name);
}
}
And a class
class Person {
constructor(name) {this.name = name;}
}
If I want to assign a greet method to the Person class that uses the console as the logger, I can think of several ways:
1.
class Person {
constructor(name) {this.name = name;}
greet(greeting) {
return createGreeter(console).call(this, greeting);
}
}
2.
class Person {
constructor(name) {this.name = name;}
}
Person.prototype.greet = createGreeter(console);
However, I think both of these are somewhat ugly; 1) creates an, essentially, unnecessary wrapper method that simply binds this and calls the function, and 2) modifies the prototype outside of the class body which in my opinion makes the class API less clear.
Is there no clearer/shorter syntax for inline assignment and binding of an external function to a class method. I'm thinking something like:
class Person {
constructor(name) {this.name = name;}
greet: createGreeter(console)
}
...which resembles how you would be able to assign a function in an object literal. But this doesn't work, obviously. Is there something similar (now, or upcoming)?
Also, I'm wondering about the memory consumption and/or performance aspects of returning a closure as in 1) if the returned function is large. Every time the greet method is called on a Person object, a new function object will be created even though we always want to pass the same parameters (console) to it. So yet another approach could be to declare const consoleGreeter = createGreeter(console) before the class definition and implement greet as return consoleGreeter.call(this, greeting), but would it be worth it?
class Person {
constructor(name) {this.name = name;}
}
Person.prototype.greet = createGreeter(console);
is the proper way to do this, unless there are other concerns (it will have problems in TypeScript).
This can be also done with class fields, which are stage 3 proposal and will likely land in ES2018:
class Person {
greet = createGreeter(console);
}
Which is a shortcut for
class Person {
constructor() {
this.greet = createGreeter(console);
}
}
First snippet evaluates createGreeter(console) once and assigns the method to class prototype. Second snippet evaluates it every time the class is instantiated and assigns the method to class instance, which is less effective.
Ok, so here is an alternative to bind external method to class
class Person {
constructor(name) {this.name = name;}
get greet() { return createGreeter(console) }
}
Just checked, this is also working fine.
This would achieve the same effect #1, since the wrapper is invoked each time the method is accessed. If that's okay with you to improve readability...
function createGreeter(logger) {
return function greet(greeting) {
logger.log(`${greeting}, ${this.name}`);
};
}
class Person {
constructor(name) {
this.name = name;
}
get greet() {
return createGreeter(console);
}
}
let person = new Person('Patrick');
person.greet('Hello');

Categories

Resources