In given two classes:
class Person {
constructor(name) {
this.name = name;
}
feedCats() {
console.log(this.name + ' fed the cats');
}
}
and:
class Cat {
constructor(name) {
this.name = name;
}
meow() {
console.log(this.name + ' says \'meow\'');
}
}
I want method meow() in every Cat's instance to be called whenever any instance of Person is calling feedCats() method. How can I do this?
I assume maybe the events handling will be needed, however in JavaScript event type is needed when using addEventListener() method and I can't find the proper one. I need the fastest JavaScript or jQuery solution.
I believe that there's a very easy path to solve this problem.
Move feedCats to a new class called CatFeeder, and add which cats should be fed before feed gets called:
class CatFeeder extends Person {
constructor(...args) {
super(...args);
this.cats = [];
}
addCat(name) {
this.cats.push(new Cat(name));
}
feed() {
this.cats.forEach(cat => cat.meow());
console.log(`${this.name} fed the cats and they said meow!`);
}
}
var catFeeder = new CatFeeder("Matías");
catFeeder.addCat("Inno");
catFeeder.addCat("Darky");
catFeeder.feed();
At the end of the day, a Person isn't a cat feeder per se, but is a CatFeeder (who's a person, but not any person). For me, this feels more natural than considerating that any person has the properties and behaviors to feed a cat.
Related
I want to test if my function inside my class returns the given sentence. But if I try to test using console.log, it returns ReferenceError: rev is not defined. What am I doing wrong?
class store {
constructor(revenue, workers) {
this.revenue = revenue;
this.workers = workers;
}
}
class storeManager extends store {
constructor(name) {
super(revenue, workers);
this.name = name;
}
rev() {
return "The stores revenue is" + this.revenue;
}
hiredWorkers() {
return "The store has" + this.revenue + "workers";
}
};
console.log(rev())
I'm a fairly new programmer, so bear with me if this is a stupid question.
The entire point of a class is to provide a template to bundle up functionality into a self-contained object and not have globals for everything.
rev is a method that appears on instances of the class. It isn't a global.
You have to create an instance of the class:
const myInstance = new StoreManager("some value for name");
(Note that this is going to fail because your constructor calls super() and tried to pass the values of variables which don't exist in the constructor method).
… and then call the method on that:
const result = myInstance.rev();
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!
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());
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()
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');