I am trying to emulate polymorphism through ES6 Classes, in order to be able to better understand this theory.
Concept is clear (designing objects to share behaviors and to be able to override shared behaviors with specific ones) but I am afraid my code above is not a valid polymorphism example.
Due to my lack of experience, I appreciate if you answer these questions in a comprehensive way:
The fact that both Classes have an equally named method, and that instances made from each Class get access correctly to their respective methods, makes this a polymorphic example?
In case this does not emulate polimorphism, what changes should be
done in the code for it?
I've tried removing the Employee.prototype = new Person(); line, and it still works. This is why I am afraid I'm not getting this concept.
class Person {
constructor (name, age) {
this._name = name;
this._age = age;
}
}
Person.prototype.showInfo = function(){
return "Im " + this._name + ", aged " + this._age;
};
class Employee {
constructor (name, age, sex) {
this._name = name;
this._age = age;
this._sex = sex;
}
}
Employee.prototype = new Person();
Employee.prototype.showInfo = function(){
return "Im " + this._sex + ", named " + this._name + ", aged " + this._age;
};
var myPerson = new Person('Jon', 20);
var myEmployee = new Employee('Doe', 10, 'men');
document.write(myPerson.showInfo() + "<br><br>"); // Im Jon, aged 20
document.write(myEmployee.showInfo() + "<br><br>"); // Im men, named Doe, aged 10
Every JavaScript object has an internal "prototype" property, often called [[prototype]], which points to the object from which it directly inherits.
Every JavaScript function [object] has a property prototype, which is initialized with an [nearly] empty object. When you create a new instance of this function by calling it as a constructor, the [[prototype]] of that new object will point to the constructor's prototype object.
So, when you write this var myPerson = new Person('Jon', 20);,you have the method showInfo because you have this
Person.prototype.showInfo = function(){
return "Im " + this._name + ", aged " + this._age;
};
With ES6 if you want to see the polymorphism you could do that :
class Person {
constructor (name, age) {
this._name = name;
this._age = age;
}
showInfo () {
return "Im " + this._name + ", aged " + this._age;
}
}
class Employee extends Person {
constructor (name, age, sex) {
super(name,age);
this._sex = sex;
}
showInfo(){
return "Im " + this._sex + ", named " + this._name + ", aged " + this._age;
}
}
var myPerson = new Person('Jon', 20);
var myEmployee = new Employee('Doe', 10, 'men');
document.write(myPerson.showInfo() + "<br><br>"); // Im Jon, aged 20
document.write(myEmployee.showInfo() + "<br><br>"); // Im men, named Doe, aged 10
You are mixing ES5 and ES6. Also, you simply created two classes. Employee does not really inherit from Person. The code you want should look like this:
class Person {
constructor(name, age) {
this._name = name;
this._age = age;
}
showInfo() {
return `I'm ${this._name}, aged ${this._age}.`;
}
}
class Employee extends Person {
constructor(name, age, sex) {
super(name, age);
this._sex = sex;
}
showInfo() {
return `I'm a ${this._sex} named ${this._name}, aged ${this._age}.`;
}
}
const alice = new Person("Alice", 20);
const bob = new Employee("Bob", 25, "male");
console.log(alice.showInfo());
console.log(bob.showInfo());
So, here's a quick recap of what's changed.
First off, no need to assign a prototype anymore. Instead, you extend the parent class to create a child class. You can call the parent constructor using the super() call in the child constructor - this will set the properties that are handled by the parent ctor. Note that we don't set name or age in the child ctor anymore! This is why your example still worked after removing the line setting the child prototype: you set the properties manually anyway.
Methods are defined inside the class; no need to use the prototype. You override the method in the child class by just declaring it there.
Should you need any additional clarification, please ask in the comment.
Related
I have just started to learn OOP. I am still trying to understand how everything works. I am trying to make a new function that will basically allow me to create a new object by passing the parameters of said class.
Is this even possible, and if so what am I doing wrong?
As stated, still learning so any advice will be appreciated.
class Person {
constructor(name) {
this.persName = name;
}
myName() {
return "My name is " + this.persName;
}
}
function getPers(_perName, fullName) {
_personName = new Person(fullName);
}
$(document).ready(function() {
getPers(John, "John Doe");
});
You can follow the following exapmle. Please note there is not much logic init. The example is just to show you how OOP can work. Of cause, there are many other and better ways to got with that, but for the first try, it should be good to use.
class Person {
//declare the constructor with required name values and optional age value
constructor(firstName, lastName, age = 0) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
//declare a typically setter, e.g. for age
setAge(age) {
this.age = age;
}
//declare a getter for the person with age
getPerson() {
return this.firstName + ' ' + this.lastName + ' is ' + this.age + ' years old.';
}
}
//here you got with some function within you need to define a new person
function getCreatedPerson(firstName, lastName, age = 0) {
//define the person with required firstname and lastname
const person = new Person(firstName, lastName);
//use the setter to set age of person
person.setAge(age);
//return the person by using the person getter
return person.getPerson();
}
//here you can call the createdPerson function
$(document).ready(function() {
console.log(getCreatedPerson('John', 'Doe', 32));
});
Hope it helps a bit to understand how it could work.
Yes, you can take the fullname as a parameter to your function and pass it through to the OOP method or constructor you're calling.
But you can't take a "reference to a variable" as an argument, like you tried with _perName/_personName. Instead, use the return keyword to return the created object:
function createPerson(fullName) {
return new Person(fullName);
}
$(document).ready(function() {
var john = createPerson("John Doe");
… // use john
});
I am relearning JavaScript and I have run into an issue that I can not figure out. If someone has the answer to this and could give me an explanation as to where I have gone wrong I would greatly appreciate it.
I have 2 functions, 1 is called Person with the parameters (name, petProp). I set petProp to look at the second function which is called Pet with the parameters of (name, species).
I then create a new Person and a new Pet.
Next I create a function which will assign the pet to the person. I want to be able to add new people and new pets to this.
Lastly I want to write a reportPets function that takes a Person object as a parameter and outputs to the console the person's name followed colon followed by a comma-separated list of pets with the species of pet in parentheses after each pet.
My issue is the first 3 functions and getting them to work properly. I can get a console.log output which has 4 parameters where I am really only looking for 3. Here is the code and I do hope that these questions make sense.
function Person(name, petProp) {
this.name = name;
this.petProp = Pet;
}
let myPerson = new Person("Mary");
function Pet(name, species) {
this.name = name;
this.species = species;
}
let myPet = new Pet("Fluffy", "Cat");
function assignPetToPerson(Pet, Person) {
return this.myPet + " " + this.myPerson;
}
assignPetToPerson(myPet, myPerson);
console.log(myPerson, myPet);
Any help is greatly aprreciated to further my knowledge and understanding of this. Thank you.
You need to proofread carefully, and think of objects as unique items, not members of a class. You can add properties at any time.
You reference a nonexistent object Pet in your Person function. You pass in an argument petProp to that same function that you aren't doing anything with. You can just ignore the pet portion of things until you want to add it in the assignPetToPerson function.
Creating the functions and then creating the objects might help keep things clear.
function Person(name) {
this.name = name;
}
function Pet(name, species) {
this.name = name;
this.species = species;
}
function assignPetToPerson(Pet, Person) {
Person.pet = Pet;
}
const myPet = new Pet("Fluffy", "Cat");
const myPerson = new Person("Mary");
assignPetToPerson(myPet, myPerson);
console.log(myPerson);
I assume you want to have myPet as the petProp of myPerson?
This is (one of the many ways) how you can achieve that:
function Person(name) {
this.name = name;
this.setPet = function(pet) {
this.petProp = pet;
}
}
let myPerson = new Person("Mary");
function Pet(name, species) {
this.name = name;
this.species = species;
}
let myPet = new Pet("Fluffy", "Cat");
myPerson.setPet(myPet);
console.log('myPerson:', myPerson);
console.log('myPet:', myPet);
What the code does it to create a public method setPet for every Person instances. Instances are created by calling new Person().
The public method can be called from the instance, so you can call myPerson.setPet(myPet). The setPet function will set this.petProp according to the value of myPet.
this in this context refers to the instance itself. For example, if you are calling myPerson.setPet(), the this in setPet refers to myPerson. You can test this by accessing myPerson.petProp directly after setting the pet, the value will be the same.
BUT,
you actually don't need to create a public method for this purpose. You can just do myPerson.petProp = myPet. Javascript is very dynamic like that. But if you are coming from an OO background, this might be something you want to know that is possible in Javascript.
function Person(name, petProp) {
this.name = name;
this.petProp = Pet;
}
var myPerson = new Person("Mary");
function Pet(name, species) {
this.name = name;
this.species = species;
}
var myPet = new Pet("Fluffy", "Cat");
function assignPetToPerson() {
return this.myPerson.name + ' ' + this.myPet.name + ' ' + this.myPet.species
}
console.log(assignPetToPerson());
With the appearance of ES6 we got a new way of creating objects. My question is how should we create objects now?
Let's say that the new operator works like this
function createNewObject(parentFunction){
var obj = {};
Object.setPrototypeOf(obj, parentFunction.prototype);
return parentFunction.apply(obj, Array.prototype.slice.call(arguments,1)) || obj;
}
But what exactly is happening when class is created? And what is the current "proper" way of creating objects in es6?
With es6 we would create a class using the following syntax:
class Animal{
constructor(name){
this.name = name;
}
saySomething(){
console.log('Hi, my name is' + this.name);
}
}
If we want to create a sub class called Cat, it would look like this:
class Cat extends Animal {
constructor(name){
super(name);
}
saySomething(){
console.log('Meow, my name is ' + this.name);
}
}
If we want to create an object of type Cat, we would do this:
let cat1 = new Cat('Felix');
cat1.saySomething(); //Outputs 'meow, my name is Felix'
The es6 class feature is syntactic sugar over the prototype method. If we were to create the Animal class using a regular prototype method it would look like this:
var Animal = function(name){
this.name = name;
}
Animal.prototype.saySomething = function(){
console.log('hi, my name is ' + this.name);
}
And the subclass would look like this:
var Cat = function(name){
Animal.call(this, name);
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.saySomething = function(){
console.log('Meow, my name is ' + this.name);
}
I have a class Player and want to extend it to have a class Team.
My logic would be:
class Player {
constructor(name, age) { //class constructor
this.name = name;
this.age = age;
}
sayName() { //class method
console.log('Hi, I am ', this.name + '.');
}
sayAge() {
console.log('Hi, I am ', this.age + '.');
}
}
class Team extends Player {
constructor(members) {
var players = [];
for (var i = 0; i < members; i++) {
players.push(super(members[i].name, members[i].age));
}
this.players = players; // to not call "this" before super
}
teamNames() {
console.log(this.players);
}
}
let p = new Player('John', 25);
p.sayName(); // John
var team = [{
name: 'Jonh',
age: 25
},{
name: 'Anna',
age: 27
}];
var A_Team = new Team(team);
A_Team.temNames();
fiddle: http://www.es6fiddle.net/ik5s7ale/
But this gives errors and the class does not work. I understand calling super inside a loop is wrong, but what would be the solution here? Is the relation between these two classes not a "extend" situation, should I use/declare separated classes for this without extending?
This is not a superclass-subclass situation.
If you have a class X, and you want to create a special, enhanced version of X, then you should extend/subclass it. That's not what's going on in your case; you should simply compose the Team class, using the Player class.
class Player {
constructor(name, age, team) { //class constructor
this.name = name;
this.age = age;
this.team = team;
}
sayName() { //class method
console.log('Hi, I am ', this.name + '.');
}
sayAge() {
console.log('Hi, I am ', this.age + '.');
}
}
class Team {
constructor(members) {
this.players = [];
for (var i = 0; i < members; i++) {
this.players.push(new Player(members[i].name, members[i].age, this));
}
}
teamNames() {
console.log(this.players);
}
}
If you want the players to have access to Team methods or constants, you're going to have to give the Player instances a reference to their parent Team instance, and call it as team.teamMethod().
Relation between Team and Player is surely not inheritance.
Team is not the special case of player, but rather Team consists of players.
Correct would be declaring separate classes. You have 2 ways of achieving it, depends of how you are using
Team takes array of players in parameter of constructor - this is good in most of cases.
Team is created "empty" and later you set players. Maybe the Team can create instance of that player based on some parameters ( createPlayer(name, age)
Well this was for your last part of question, now why your code does not work.
When you call super, you are calling the constructor of parent object.
The parant object is only one and super does not return pointer to it (instead it returns undefined), so you can not push it to the array.
Also you had a few typos in your code.
See an updated fiddle
EDIT
I Was writting answer and didn't notice another answer posted in the meantime :-)
OK, I've revised most of the techniques to implement inheritance in JavaScript OOP.
As a Java programmer, I'm interested in the classical approach but here's the problem; say I want to create the Animal class (I know it's not a real class, but let me use this term) like this:
function Animal(name){
this.name = name;
}
Animal.prototype.getName = function() {
return this.name;
}
It is important to note that this is a concrete class in my first intention, I want to instantiate it, not just use it as a superclass. I may create several Animal instances, each one with its own name.
A possible way to extend this class is to do the following:
function Cat(name, owner) {
this.name = name;
this.owner = owner;
}
// ALTERNATIVE 1:
Cat.prototype = Object.create(Animal.prototype);
// ALTERNATIVE 2:
Cat.prototype = new Animal('LOLA');
// END OF ALTERNATIVES
Cat.constructor.prototype = Cat;
Cat.prototype.jump = function() {
alert(this.name + " jumping");
}
With the ALTERNATIVE 1 we just inherit the methods of the superclass, in fact we need to redefine the name property in the Cat. With the ALTERNATIVE 2 nothing actually changes, we just have one more object in the chain that holds a name property that's quite useless: it's the same for all the Cat instances.
The point here is that I've written the Animal class with its own name and I just throw it away as soon as I extend it. What I'd like to have though is a way to inherit both properties and methods and, most of all, I'd like to be able to reuse the Animal constructor.
The traditional way to inherit the properties of the base constructor is as follows:
function Cat(name, owner) {
Animal.call(this, name); // call the base constructor
this.owner = owner;
}
Cat.prototype = new Animal;
Cat.prototype.constructor = Cat;
Cat.prototype.jump = function () {
alert(this.name + " jumping");
};
The above code is equivalent to the following class in other languages:
class Cat extends Animal {
constructor(name, owner) {
super(name);
this.owner = owner;
}
jump() {
alert(this.name + " jumping");
}
}
The new way to inherit properties is exactly the same, save that we replace new Animal with Object.create(Animal.prototype). The reason we prefer the new way is because:
Calling new Animal is unnecessary overhead. The Cat constructor calls it again anyway.
Calling new Animal might not return a blank object. It might add some properties to the object.
We don't know what arguments to call new Animal with yet. Hence it makes no sense to call it.
Thus the preferred way of inheritance is now:
function Cat(name, owner) {
Animal.call(this, name); // call the base constructor
this.owner = owner;
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
Cat.prototype.jump = function () {
alert(this.name + " jumping");
};
Note that it's important to call the base constructor because it may do some initialization which is necessary for the instance to work properly.
If you're interested in writing JavaScript code in a classical style then take a look at the following answer which describes prototype-class isomorphism. The following code it taken from the above answer:
function CLASS(prototype, base) {
switch (typeof base) {
case "function": base = base.prototype;
case "object": prototype = Object.create(base, descriptorOf(prototype));
}
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
function descriptorOf(object) {
return Object.keys(object).reduce(function (descriptor, key) {
descriptor[key] = Object.getOwnPropertyDescriptor(object, key);
return descriptor;
}, {});
}
Using the CLASS function we can define pseudo-classes in JavaScript as follows:
var Animal = CLASS({
constructor: function (name) {
this.name = name;
},
getName: function () {
return this.name;
}
});
var Cat = CLASS({
constructor: function (name, owner) {
Animal.call(this, name);
this.owner = owner;
},
jump: function () {
alert(this.name + " jumping");
}
}, Animal);
There are other ways to do inheritance in JavaScript as well. I suggest you read my blog post on Why Prototypal Inheritance Matters to understand more about inheritance in JavaScript.