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');
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 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'm trying to figure out if there's any different when defining functions inside or outside of a class in JavaScript. Why would I choose to do it one way over the other? (Notice my getName [inside class] and getName2 [outside of class]).
class TestClass {
constructor(myName) {
this.name = myName;
}
getName() {
return this.name;
}
}
TestClass.getName2 = function() {
//won't actually print the name variable set since not associated with an instance of the class?
console.log(this.name);
};
var test = new TestClass("Joe");
console.log(test.getName());
///////////////
TestClass.getName2();
Output:
Joe
TestClass
The only difference I can really see so far through my testing here is that I cannot access this.name within my getName2 since I believe it's not associated with any instance of the TestClass. So my getName2 is almost like a static class function where it's not associated with an instance of the class?? Please help me clarify this and why I would choose to implement a function one way over the other.
From the MDN doc:
JavaScript classes, introduced in ECMAScript 2015, are primarily syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax does not introduce a new object-oriented inheritance model to JavaScript.
So this...
class TestClass {
constructor(myName) {
this.name = myName;
}
getName() {
return this.name;
}
static getName2() {
return 'getName2 result';
}
}
...is exactly equivalent to this:
const TestClass = function(myName) {
this.name = myName;
}
TestClass.prototype.getName = function() {
return this.name;
}
TestClass.getName2 = function() {
return 'getName2 result';
}
So whether you use the older prototype syntax or the newer ES6 class syntax is just a matter of personal preference, and as you suspected, defining methods directly on a class is exactly equivalent to creating a static class method.
I don't think there's any reason you would ever do
TestClass.getName2 = function() {
If you want a standalone function, make it a standalone function.
e.g.
export function getName2() {...};
export TestClass;
If you want to extend an existing class, you'd do
TestClass.prototype.getName2 = function() {
which will allow you to access this.
One thing that I noticed is you are using a reserved keyword for objects, properties, and methods. TestClass.getName2() should return undefined, but since you used the "name" reserve keyword. It returned the name of the class, which is considered an object like a function. In your case, you are using this.name which refers to the class name and it returns "TestClass".
Example:
class TestClass {
constructor(userName) {
this._userName = userName;
}
getName() {
return this._userName;
}
}
TestClass.getName2 = function() {
// returns undefined now
console.log(this._userName);
};
var test = new TestClass("Joe");
console.log(test.getName()); // returns Joe
///////////////
TestClass.getName2(); // returns undefined
let testClassName = TestClass.name; // Just a little proof of what is returned below
console.log(testClassName); // returns TestClass
My only suggestion is that you should stay abreast of the current reserved keywords and where you can and cannot use them. You could also focus on your naming convention in your code.
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()
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.