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
Related
Is there a way to declare a function inside class?
If yes, I'm trying to declare function age() inside the class, but i think it is not possible on JavaScript or maybe I'm wrong.
I do not want to put it outside the class because the code looks more organized like this. What are the options available?
My code is below.
class Animal {
constructor(name) {
this.name = name;
}
cat() {
console.log(`Meow! It is cat and his name is ${this.name}`);
console.log(age('cat'));
}
dog() {
console.log(`Au! It is dog and his name is ${this.name}`);
console.log(age('dog'));
}
function age(animal){
if(animal=='cat') return 7;
if(animal=='dog') return 5;
}
}
const fluffy = new Animal('Fluffy');
fluffy.cat();
const billy = new Animal('billy');
billy.dog();
You've already declared functions inside of the class, just do the same for age. To access it, you need to use this.age since JavaScript doesn't look for class instance variables/functions by default.
class Animal {
constructor(name) {
this.name = name;
}
cat() {
console.log(`Meow! It is cat and his name is ${this.name}`);
console.log(this.age('cat'));
}
dog() {
console.log(`Au! It is dog and his name is ${this.name}`);
console.log(this.age('dog'));
}
age(animal) {
if (animal == 'cat') return 7;
if (animal == 'dog') return 5;
}
}
const fluffy = new Animal('Fluffy');
fluffy.cat();
const billy = new Animal('billy');
billy.dog();
As others already said cat and dog are already functions of Animal (member functions).
If you don't want that the age function is a member function (belongs to an instance of Animal) you can make it static:
class Animal {
constructor(name) {
this.name = name;
}
cat() {
console.log(`Meow! It is cat and his name is ${this.name}`);
console.log(Animal.age('cat'));
}
dog() {
console.log(`Au! It is dog and his name is ${this.name}`);
console.log(Animal.age('dog'));
}
static age(animal){
if(animal=='cat') return 7;
if(animal=='dog') return 5;
}
}
const fluffy = new Animal('Fluffy');
fluffy.cat();
const billy = new Animal('billy');
billy.dog();
If you don't want to make it accessible outside of Animal than make it private:
class Animal {
constructor(name) {
this.name = name;
}
cat() {
console.log(`Meow! It is cat and his name is ${this.name}`);
console.log(Animal.#age('cat'));
}
dog() {
console.log(`Au! It is dog and his name is ${this.name}`);
console.log(Animal.#age('dog'));
}
static #age(animal){
if(animal=='cat') return 7;
if(animal=='dog') return 5;
}
}
const fluffy = new Animal('Fluffy');
fluffy.cat();
const billy = new Animal('billy');
billy.dog();
However having a private static function is rarely useful. The intent of a static method is that it logically belongs to the class but performs a task that does not require an instance of that class.
Hi Jessica You can put it as a method if you want.
But I think anyway in this case you should have a class for cat and another one for dog.
Or pass in the constructor the age but it will mean that the age will not be related to what is the animal.
Long story short you can decide or to create a class for each or to create a method inside the class.
You can create a function in a class, but the correct syntax requires you to omit the 'function' keyword. Also use this keyword to refer to the current object.
class Animal {
constructor(name) {
this.name = name;
}
cat() {
console.log(`Meow! It is cat and his name is ${this.name}`);
console.log(this.age('cat'));
}
dog() {
console.log(`Au! It is dog and his name is ${this.name}`);
console.log(this.age('dog'));
}
age(animal){
if(animal=='cat') return 7;
if(animal=='dog') return 5;
}
}
const fluffy = new Animal('Fluffy');
fluffy.cat();
const billy = new Animal('billy');
billy.dog();
well I can come up with 3 way to define some functionality in a class
number one: method ,it is the common one that you are likely to see in class based codes
class Animal{
//rest of class
age(){
//your function goes here
}
}
you can latter access it with an instance of class
const cat = new Animal() cat.age()
number two: static ,it is somewhat different because it belong to class itself
class Animal{
//rest of class
static age(){
//your function goes here
}
}
in this case you don't need to create an instance of class
Animal.age()
and lastly: treating your function as property it is not different from using method but I would recommend method if you want to use this functionality often there are minor differences in** memory usage**
syntax goes like this
class Animal {
constructor(name) {
this.age= ()=>{//your function}
}}
use age like this
const cat = Animal()
, {age} = cat
age()
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
I'm studying JavaScript autodidactically, I was searching for ways to inherit from objects and extend their functionalities as well...and... wow! it turns out there are several ways to do it.
So there is a popular one which is extending an object through extend and class keywords (to say it in short) of which I put an example of what I saw right underneath. Ok, since I come from C++ and Python languages, this is indeed! very helpful... but I'm aware of this is sugar code to help programmers who come from object-oriented languages feel JavaScript cozier. So!, my goal is to know how to extend objects functionalities without using the aforementioned method since I'm eager to understanding how js works under the hood (at least to nighing considerably to it) and feeling comfortable with it.
Note
I know there are posts here on this topic, but i think those don't meet my needs, considering those which (are very good) dig deep into it are from around 2012.
With class and extend keywords way
REFERENCE: https://www.geeksforgeeks.org/how-to-extend-an-object-in-javascript/
// Declaring class
class Profile {
// Constructor of profile class
constructor(name, age) {
this.name = name;
this.age = age;
}
getName() {
// Method to return name
return this.name;
}
getAge() {
// Method to return age
return this.age;
}
getClass() {
return this;
}
}
// Class Student extends class Profile
class Student extends Profile {
// Each data of class Profile can be
// accessed from student class.
constructor(name, age, languages) {
// Acquiring of attributes of parent class
super(name, age);
this.lang = [...languages];
}
// Method to display all attributes
getDetails() {
console.log("Name : " + this.name);
console.log("Age : " + this.age);
console.log("Languages : " + this.lang);
}
}
// Creating object of child class with passing of values
var student1 = new Student("Ankit Dholakia", 24,
['Java', 'Python', 'PHP', 'JavaScript']);
student1.getDetails();
I tried this... but I'm not comfortable!
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.selfPresentation = function() {
console.log("Hello, my name is " + this.name + "and I'm " + this.age + " years old.");
}
function Worker(name, age, occupation) {
Person.call(this, name, age);
this.occupation = occupation;
}
Worker.prototype = new Person;
Worker.prototype.selfPresentation = function() {
// I think there undoubtedly is a best approach for this...
this.__proto__.__proto__.selfPresentation.call(this);
console.log("I don't breath only, I'm a " + this.occupation);
}
let variable = new Worker("Arturo", 22, "Student");
variable.selfPresentation();
(This is a little aside my goal but..) For inheritance between objects I tried to mimic Object.create method
a = {
field: "field",
method: function() {
console.log("SuperMethod");
}
}
function inheritHimAll(object) {
function helperFunction() {}
helperFunction.prototype = object;
return new helperFunction;
}
b = inheritHimAll(a);
console.log(b.__proto__)
What would be the best approach for extending objects in javascript?
What would be the best approach for extending objects in javascript?
This is quite a subjective question, but I use the class syntax when I can. They are more intuitive, and require less boilerplate code.
However, I think you are more interested in knowing how to do inheritance without the class syntax. Here's what I would change from your example:
Don't create a new instance of the class for prototype. The properties all live in the object, not in the prototype. Instead, use Object.create() to create a new object with the class's prototype. See this answer for more information about this.
Never use __proto__. Instead, call the function from the class's prototype manually or use Object.getPrototypeOf().
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.selfPresentation = function() {
console.log("Hello, my name is " + this.name + "and I'm " + this.age + " years old.");
}
function Worker(name, age, occupation) {
Person.call(this, name, age);
this.occupation = occupation;
}
Worker.prototype = Object.create(Person.prototype);
Worker.prototype.selfPresentation = function() {
Person.prototype.selfPresentation.call(this);
console.log("I don't breath only, I'm a " + this.occupation);
}
let variable = new Worker("Arturo", 22, "Student");
variable.selfPresentation();
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.