I'm playing around with ES6 classes and my eventual goal is to understand the difference between classes vs constructor functions vs factory functions. My current understanding is that constructor functions and classes are basically using the same design pattern, classes just being the new syntax for the constructor functions. Based on this assumption, I'm trying to create some examples that display the contrast between class/constructor functions and factory functions.
With the examples below I aim to return new objects and some methods that are inherited.
My first example is rather straightforward with the class syntax.
Example #1: Classes
class Person {
constructor(name, age, location, occupation) {
this.name = name;
this.age = age;
this.location = location;
this.occupation = occupation;
}
printDescription() {
console.log(`My name is ${this.name} and I'm ${this.age} years old. I live in ${this.location} and I work as a ${this.occupation}.`);
}
}
const firstUser = new Person('Tom', 30, 'Sydney', 'Teacher');
firstUser.printDescription();
With the second example, I'm trying to replicate the first one with a different approach but I'm not sure if this is factory constructor or a factory function.
Example #2: ?
function PersonMaker (name, age, location, occupation) {
let person = {name, age, location, occupation};
person.printDetails = () => {
console.log(`My name is ${name} and I'm ${age} years old. I live in ${location} and I work as a ${occupation}.`);
};
return person;
}
const secondUser = PersonMaker('Johnny', 25, 'London', 'Driver');
secondUser.printDetails();
I need to clarify the pattern used in the second example and if it's not a factory function, how I could turn it into one?
Since it returns an object its a factory function - it's already explained there.
Constuctor functions behaviour different from this, it doesn't return value:
function Person(name, age, location, occupation){
this.name = name
this.age = age
this.location = location
this.occupation = occupation
}
Person.prototype.printDetails = function(){
console.log(`My name is ${this.name} and I'm ${this.age} years old. I live in ${this.location} and I work as a ${this.occupation}.`);
};
const secondUser = new Person('Johnny', 25, 'London', 'Driver');
secondUser.printDetails();
I used definition of methods by extending prototype just to separate constructor function, and you can still define a methods inside constructor function as well:
function Person(name, age, location, occupation){
this.name = name
this.age = age
this.location = location
this.occupation = occupation
this.printDetails = function(){
console.log(`My name is ${this.name} and I'm ${this.age} years old. I live in ${this.location} and I work as a ${this.occupation}.`);
};
}
const secondUser = new Person('Johnny', 25, 'London', 'Driver');
secondUser.printDetails();
Javascript takes its roots in Prototypal Inheritance:
// Prototype
var ProtoCtr = function ProtoCtr(name, age) {
this.name = name;
this.age = age;
};
ProtoCtr.prototype.printDetails = function printDetails() { console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old`); };
var protoInstance = new ProtoCtr('John', 21);
protoInstance.printDetails();
In short:
Functions expose prototypes
Defines a this context referring to the instance
Use the new keyword to create an instance
Then, with ES6, the language offered the possibility to use the class keyword (to please classical OO developers). It's a syntax which allow to group the declaration of your object in a block, avoiding to extend the prototype "manually". Under the hood, it does the exact same thing than the prototypal model, it's just an other way to write it.
// Class
class ClassCtr {
constructor(name, age) {
this.name = name;
this.age = age;
}
printDetails() {
console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old`);
}
}
var classInstance = new ClassCtr('John', 21);
classInstance.printDetails();
Key concepts are about the same, but you don't use prototype to expose methods to your instance but declare it directly inside the class block.
Then, there is the factory pattern, which is not a language specs but a pattern, a way to do things. It's my personal favorite. The factory function builds and return the instance. With this method, you get rid of the new keyword and don't need the this anymore to refer to you instance:
// Factory
var factoryCtr = function factoryCtr(name, age) {
var instance = {
name: name,
age: age,
printDetails: function printDetails() { console.log(`Hi, I'm ${instance.name} and I'm ${instance.age} years old`); }
};
return instance;
}
var factoryInstance = factoryCtr('John', 21);
factoryInstance.printDetails();
Even if Class is not covered by this talk, I recommand you to watch this video:
https://www.youtube.com/watch?v=ya4UHuXNygM
I hope this helped :)
To avoid passing many arguments, you can do it like this.
const PersonMaker = description => {
const {name, age, location, occupation} = description;
return {
printDetails: () => {
console.log(
`My name is ${name} and I'm ${age} years old. I live in ${location} and I work as a ${occupation}.`,
);
},
};
};
const secondUser = PersonMaker({
name: 'Johnny',
age: '25',
location: 'London',
occupation: 'Driver',
});
console.log(secondUser.printDetails());
Related
So as I learn more about the Prototypal Inheritance in JS, I read the following in MDN article (read lines just above this link)
Note that when we are calling our constructor function, we are defining greeting() every time, which isn't ideal. To avoid this, we can define functions on the prototype instead, which we will look at later.
The idea recommended is to add properties on Function and methods on the prototype (read here)
Person.prototype.farewell = function() {
alert(this.name.first + ' has left the building. Bye for now!');
};
I read at multiple places that this way
The object creation is faster, since the farewell is not created for every object creation. This is because it is created once and attached to the prototype, and all the objects link to the prototype.
The methods are looked up on the prototypal chain so every object links to the same method on the prototype.
Now comes the ES6 class. I have the following code
class Person {
constructor(first, last, age, gender, interests) {
this.name = {
first: first,
last: last
};
this.age = age;
this.gender = gender;
this.interests = interests;
}
greeting () {
console.log("Hi! I am", this.name.first);
}
farewell () {
console.log(this.name.first, "has left the building. Bye for now!");
}
}
It seems that with this approach greeting and farewell will be created again (following up on same logic as Functions since class is a syntactic sugar)
So, I changed the class to
class Person {
constructor(first, last, age, gender, interests) {
this.name = {
first: first,
last: last
};
this.age = age;
this.gender = gender;
this.interests = interests;
}
}
Person.prototype.greeting = function () {
console.log("Hi! I am", this.name.first);
}
Person.prototype.farewell = function () {
console.log(this.name.first, "has left the building. Bye for now!");
}
Question
1. Does the latter way (Adding methods on prototype in ES6 class) is a recommended way?
2. Does the logic of class and traditional new Function to create a new object align? Copy vs Method on Prototype?
Please let me know if there is anything else I am missing
Thanks
UPDATE
After few answers, I retried my examples and confirmed the answers. My code looks like
class Phone {
constructor(company, name, color, price) {
this.company = company;
this.name = name;
this.color = color;
this.price = price;
}
print() {
console.log(this.company, this.name, this.color, this.price);
}
}
class Apple extends Phone {
constructor(name, color, price) {
super("Apple", name, color, price);
this.companyWork = "ClosedSource";
}
}
let iPhone11 = new Apple("iPhone11", "black", 900);
iPhone11.print()
After I ran this code, I can confirm that print() is available on Phone.prototype
As you've already discovered, class definitions put methods on the prototype. You asked for a reference so here's a little code you can run to see for yourself:
class Person {
constructor(first, last, age, gender, interests) {
this.name = {
first: first,
last: last
};
this.age = age;
this.gender = gender;
this.interests = interests;
}
greeting () {
console.log("Hi! I am", this.name.first);
}
farewell () {
console.log(this.name.first, "has left the building. Bye for now!");
}
}
let p = new Person("Jack", "Beanstalk", 201, "giant", ["crushing things", "stomping things"]);
console.log("Person", Object.getOwnPropertyNames(Person));
console.log("p", Object.getOwnPropertyNames(p));
let p_prototype = Object.getPrototypeOf(p);
console.log("p_prototype === Person.prototype is ", p_prototype === Person.prototype);
console.log("Person.prototype", Object.getOwnPropertyNames(Person.prototype));
That generates this output:
Person [ 'length', 'prototype', 'name' ]
p [ 'name', 'age', 'gender', 'interests' ]
p_prototype === Person.prototype is true
Person.prototype [ 'constructor', 'greeting', 'farewell' ]
So, you can draw these conclusions:
The Person class has only the expected properties for a constructor function and the prototype object, none of the class-defined methods are there.
An instance of the class has only the expected data properties that are assigned in the constructor, none of the class-defined methods are on the object itself.
The prototype you get from an instance is the same as the prototype of the class (thus no new object is created for each instance).
The Person.prototype has the expected methods from the class definition on it plus a property that points to the constructor itself (used in inheriting and calling derived constructor).
So, you can verify from this that methods defined with the class syntax go on the Classname.prototype object and that object is shared with all instances.
AFAIK
No, it's not a recommended way. Class methods are created as methods on prototype. In other words classes are just syntactic sugar for regular prototypal inheritance.
In both cases methods won't be created multiple times if multiple objects are created. Methods will be created only once.
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 :-)
This question already has answers here:
What is the difference between a function expression vs declaration in JavaScript? [duplicate]
(5 answers)
Closed 7 years ago.
I am reading a JavaScript book and here the constructor function is created like this:
var Person = function (living, age, gender) {
this.living = living;
this.age = age;
this.gender = gender;
this.getGender = function () { return this.gender; };
};
And I read somewhere something like this:
function Person (living, age, gender) {
this.living = living;
this.age = age;
this.gender = gender;
this.getGender = function () { return this.gender; };
};
I wonder what the difference is between these two. Is there any difference when we create instances or are they the same, just two different ways of creating object constructors?
The first is a function expression, the second a function declaration (it doesn't need the semicolon at the end, btw). The distinction is not tied to constructors, however and applies to ordinary functions.
As you probably know, functions are first-class values in javascript. One of the things that implies is that functions can be assigned to variables. So, just like you can assign numbers to variables (e.g. var pi = 3.14), you can assign functions to variables, var add = function(a,b) {return a + b}. That's what your first declaration does, it creates a function (implemented as a closure) and then stores the reference to it in the variable Person. You can think of the second one as a shortcut for the first.
For the syntactical details, check out §14.1 of the spec.
Both are similar and you can create instances from them with new Person().
The only difference is that in the 1st case it's an expression ,so it must be defined before you use it while in the second case , due to hoisting you can use the function anywhere in your file
This is defined at run-time (function expression):
var Person = function (living, age, gender) {
this.living = living;
this.age = age;
this.gender = gender;
this.getGender = function () { return this.gender; };
};
And this is defined at parse-time (function declaration):
function Person (living, age, gender) {
this.living = living;
this.age = age;
this.gender = gender;
this.getGender = function () { return this.gender; };
};
As an example:
funA(); // does not work
funB(); // it works
var funA = function(){console.log("testA");}
function funB(){console.log("testB");}
funA(); // it works
funB(); // it works
Other StackOverflow reference that supports my answer --> var-functionname-function-vs-function-functionname
I'm starting to understand javascript, but are there any benefits of using javascript objects like so ...
var Person(name, age) {
var obj = {
species: "Homo sapien",
location: "Earth"
};
obj.name = name;
obj.age = age;
return obj;
}
var Mike = Person("Mike", 17); // => { ... name: "Mike", age: 17}
versus the standard
var personProtoype = {
name: "anon",
age: 0,
species: "Homo sapien",
location: "Earth"
}
var Person = function(name, age) {
this.name = name;
this.age = age;
}
Person.prototype = personPrototype;
var Mike = new Person("Mike", 17);
because it seems to use less code and easier to implement and understand. "Inheritance" is also pretty straightforward with the first method too.
var Student(name, age, major) {
var obj = Person(name, age); // => { ... name: name, age: age}
obj.major = major; // => { ... name: name, age: age, major: major}
return obj;
}
I understand that the following doesn't use prototype chains at all, just functions that simply construct objects. I was just wondering if there was any benefit of doing things this way? Maybe to have objects that don't look back at a huge chain of prototypes? (if that type of behavior is ever desired)
I can’t think of any advantages beyond the ones you listed, although there may be some. Here are some of the disadvantages:
No instanceof testing. Using the Person implementations in your question, slightly renamed:
var Mike1 = Person_no_prototype("Mike", 17);
var Mike2 = new Person_with_prototype("Mike", 17);
console.log(Mike1 instanceof Person_no_prototype); // false
console.log(Mike2 instanceof Person_with_prototype); // true
When methods exist, uses more memory. Consider an implementation of a greet method, put on either obj or Person.prototype:
/* obj or Person.prototype */.greet = function greet() {
alert("Hi there, " + this.name + "!");
};
Using a prototype, the greet function is created only once. Without a prototype, you create a new function each time, with whatever overhead that implies.
I have an object written like this:
Object1.prototype = {
isInit: false,
Get : function (){}
}
Now I'd like to add a constructor which takes one parameter. How can I do it?
Class declaration
var User = function(name, age) { // constructor
}
User.prototype = {}
Instance variables (members)
var User = function(name, age) {
this.name = name;
this.age = age;
}
User.prototype = {}
Static variables
var User = function(name, age) {
this.name = name;
this.age = age;
}
User.prototype = {
staticVar: 15,
anotherStaticVar: 'text'
}
Here I defined two static variables. Each User instance has access to these two variables. Note, that we can initialize it with value;
Instance functions (methods)
var User = function(name, age) {
this.name = name;
this.age = age;
}
User.prototype = {
getName: function() {
return this.name;
},
setName: function(name) {
this.name = name;
}
}
Usage example:
var user = new User('Mike', 29);
user.setName('John');
alert(user.getName()); //should be 'John'
Static functions
var User = function(name, age) {
this.name = name;
this.age = age;
}
User.create = function(name, age) {
return new User(name, age);
}
User.prototype = {}
Assuming that by "ctor" you mean "constructor", in JavaScript that's just a function. In this case your constructor would need to be "Object1" itself - in other words, what you've got there makes sense if you have already defined "Object1" to be a function.
Thus,
function Object1(param) {
// constructor code
}
would be the constructor for your type.
Now there are some JavaScript libraries that provide a utility layer for defining classes. With those, you generally pass some sort of object (like you've got) that includes an "init" function. The libraries provide APIs for creating "classes" and for extending one class from another.
Javascript has prototype based object model. Check this mozilla wiki page and suddenly you'll feel much better in js land.
We can define a constructor in javaScript is same as we fine function, so constructor is just a function.
//function declaration
function func(){}
In case of Contructor We use initial letter in caps in construct like
//constructor
function Func(){}
now do whatever you want to with your constructor
var constructor1 = new Func();
class CLASS_NAME
{
private:
int variable;
public:
CLASS_NAME() //constructor
{
variable = 0;
}
};