Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9f87bdd

Browse files
committed
completed OOP fundamental
1 parent 3b9cec3 commit 9f87bdd

File tree

1 file changed

+119
-57
lines changed

1 file changed

+119
-57
lines changed

fundamentals/oop_classes.md

Lines changed: 119 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
### Object Literals and Imperative Programming
44

5-
In earlier JavaScript lectures we saw that we can define object literals and use functions to access and manipulate their properties. A typical example is shown below, where we have defined an array of objects, in this case each representing the name of a month and its associated number of days.
5+
In earlier JavaScript lectures we saw that we can create objects through object literals and use functions to access and manipulate their properties. A typical example is shown below, where we have defined an array of objects, in this case each representing the name of a month and its associated number of days.
66

7-
We're looping through the months and printing a message to the console for each month having 31 days.
7+
We're looping through the months and printing an informational message to the console for each month having 31 days.
88

9-
This style of programming is called Imperative Programming: we instruct the computer how to perform the task(s) at hand.
9+
This style of programming is called Imperative Programming: in the code detail containing the `for` loop we instruct the computer _how to perform the task(s) at hand_.
1010

1111
```js
1212
const months = [
@@ -33,13 +33,15 @@ for (const month of months) {
3333

3434
## Functional Programming
3535

36-
In the Functional Programming style (also referred to as Declarative Programming), we prefer to declare what the computer should do.
36+
In the Functional Programming style (also referred to as Declarative Programming), we prefer to declare _what the computer should do_.
3737

3838
In the next example we have used the `filter` method to extract a subset of months having 31 days, used the `map` method to create an informational string for each month and a `forEach` method to output those strings to the console.
3939

40-
Or, put in other words, we state or **declare** that the computer should **filter** our array according to some predefined criterion, then **map** each filtered object to a string and print out each string to the console.
40+
Put in other words, we state or **declare** that the computer should **filter** our array according to some predefined criterion, then **map** each filtered object to a string and print out each string to the console.
4141

42-
In contrast to the imperative style we do not have to infer from looking at the code what we are actually doing. Instead, the names of our functions and methods already imply what it is we want to achieve.
42+
> A prime example of a declarative language is SQL, which you will learn in the HYF Database module.
43+
44+
In contrast to the Imperative Style we do not have to infer from looking at the code what is actually happening. Instead, the names of the functions and methods already indicate what it is we want to achieve.
4345

4446
```js
4547
const months = [
@@ -63,7 +65,15 @@ months
6365
.forEach(string => console.log(string));
6466
```
6567

66-
### Constructor Functions (pre-ES6)
68+
### Constructor Functions (pre-ES6) and the `new` keyword
69+
70+
In the example below we use a function that is used in conjunction with the `new` keyword. Such a function is called a **constructor** function, and, by convention, we start its name with an uppercase letter.
71+
72+
When a function is called and preceded by the `new` keyword, something special happens. The JavaScript engine creates a new, empty object and assigns that object to the `this` variable.
73+
74+
> The `this` variable is always present in JavaScript. Its value is dependent on the current execution context. Most of the time, the value of `this` is `undefined`. However, when calling a method on an object, the `this` variable holds a reference to the object it is called on. We can use the `this` variable inside the method implementation to get at other properties and methods within the object.
75+
76+
We can now add properties to the new object through the `this` variable, as shown below.
6777

6878
```js
6979
function Month(name, days) {
@@ -96,14 +106,30 @@ months
96106

97107
- StackOverflow: [What is the 'new' keyword in JavaScript?](https://stackoverflow.com/questions/1646698/what-is-the-new-keyword-in-javascript)
98108

99-
### ES6 Classes vs pre-ES6 Constructor Functions
109+
### Introducing Object-Oriented Programming
110+
111+
In the preceding example, there was not much to be gained from using a constructor function in conjunction with the `new` keyword, as compared to just using object literals. The advantages become more clear when we start to add **methods** to the object. Methods are just plain JavaScript functions that you call on an object, using dot notation. In the code snippet below, we have defined a couple of functions and assigned them to object properties through the `this` variable in the constructor function. This makes these functions into methods.
100112

101113
```js
102-
class Month {
103-
constructor(name, days) {
104-
this.name = name;
105-
this.days = days;
106-
}
114+
function Month(name, days) {
115+
this.name = name;
116+
this.days = days;
117+
118+
this.hasDays = function (days) {
119+
return this.days === days;
120+
};
121+
122+
this.isLongMonth = function () {
123+
return this.hasDays(31);
124+
};
125+
126+
this.toString = function () {
127+
return `${this.name} has ${this.days} days.`;
128+
};
129+
130+
this.toConsole = function () {
131+
console.log(this.toString());
132+
};
107133
}
108134

109135
const months = [
@@ -122,39 +148,47 @@ const months = [
122148
];
123149

124150
months
125-
.filter(month => month.days === 31)
126-
.map(month => `${month.name} has ${month.days} days.`)
127-
.forEach(string => console.log(string));
151+
.filter(month => month.isLongMonth())
152+
.forEach(month => month.toConsole());
128153
```
129154

130-
### ES6 Classes and Object-Oriented Programming
155+
We can now call these methods using dot notation, as in:
131156

132-
The remaining style of programming we will discuss here is called Object-Oriented Programming.
157+
```js
158+
month.isLongMonth()
159+
month.toConsole()
160+
```
161+
162+
We have already seen this notation when we used, for instance, `map` and `filter`.
163+
164+
When we add methods to an object to operate on data contained in the object, we have created a more or less self-contained object. The object knows how to operate its data and external code need not know anything about its internals. This concept is known as Object-Oriented Programming. It is the default style of programming in object-oriented languages such as Java, C# and C++. In JavaScript it is optional. In the HYF React module, ES6 classes are used extensively.
165+
166+
### Prototypes
133167

134-
TO BE CONTINUED
168+
The code from the previous example has a significant inefficiency: each object get its own copy of the methods (`hasDays` etc). This takes up unnecessary memory. It would be far better if the objects could share a common set of methods. This is where JavaScript's concept of a `prototype` comes in.
169+
170+
Each JavaScript function has a `prototype` property that points to an, initially empty, prototype object. It only comes into play when using that function as a **constructor** function. We can assign functions to this prototype which are shared by all objects we create through calling the constructor function in combination with the `new` keyword.
135171

136172
```js
137-
class Month {
138-
constructor(name, days) {
139-
this.name = name;
140-
this.days = days;
141-
}
173+
function Month(name, days) {
174+
this.name = name;
175+
this.days = days;
176+
}
142177

143-
hasDays(days) {
144-
return this.days === days;
145-
}
178+
Month.prototype.hasDays = function (days) {
179+
return this.days === days;
180+
};
146181

147-
isLongMonth() {
148-
return this.hasDays(31);
149-
}
182+
Month.prototype.isLongMonth = function () {
183+
return this.hasDays(31);
184+
};
150185

151-
toString() {
152-
return `${this.name} has ${this.days} days.`;
153-
}
186+
Month.prototype.toString = function () {
187+
return `${this.name} has ${this.days} days.`;
188+
};
154189

155-
toConsole() {
156-
console.log(this.toString());
157-
}
190+
Month.prototype.toConsole = function () {
191+
console.log(this.toString());
158192
}
159193

160194
const months = [
@@ -177,32 +211,41 @@ months
177211
.forEach(month => month.toConsole());
178212
```
179213

180-
### The Prototype Chain
214+
215+
The diagram below depicts how this sharing works out. At this time it is not necessary that you understand every detail. Just note how there is a single copy of functions, shared by all instances of the `Months` objects.
181216

182217
![prototype](assets/prototype.png)
183218

184-
### Pre-ES6 Constructor Functions and Prototypes
219+
### ES6 Classes
220+
221+
In ES6 a new way of defining objects and its methods was introduced. It uses the same `prototype` mechanism behind the scenes, but its syntax is closer to that of other object-oriented languages, such as Java, etc. Because it is only new syntax, hiding the intricacies of the `prototype`, it is often designated as 'syntactic sugaring'.
222+
223+
In ES6 classes we use the `class` keyword to define a class. The `constructor` method takes the place of the constructor function of the previous examples.
224+
225+
We define methods by creating functions inside the class body, however without the `function` keyword. As previously, the `this` keyword refers to the object that a method is called upon.
185226

186227
```js
187-
function Month(name, days) {
188-
this.name = name;
189-
this.days = days;
190-
}
228+
class Month {
229+
constructor(name, days) {
230+
this.name = name;
231+
this.days = days;
232+
}
191233

192-
Month.prototype.hasDays = function (days) {
193-
return this.days === days;
194-
};
234+
hasDays(days) {
235+
return this.days === days;
236+
}
195237

196-
Month.prototype.isLongMonth = function () {
197-
return this.hasDays(31);
198-
};
238+
isLongMonth() {
239+
return this.hasDays(31);
240+
}
199241

200-
Month.prototype.toString = function () {
201-
return `${this.name} has ${this.days} days.`;
202-
};
242+
toString() {
243+
return `${this.name} has ${this.days} days.`;
244+
}
203245

204-
Month.prototype.toConsole = function () {
205-
console.log(this.toString());
246+
toConsole() {
247+
console.log(this.toString());
248+
}
206249
}
207250

208251
const months = [
@@ -225,24 +268,43 @@ months
225268
.forEach(month => month.toConsole());
226269
```
227270

271+
228272
### Bonus: Array.prototype.map & Array.prototype.filter Implementations
229273

274+
Now that we know a bit more about objects, prototypes and the `this` variable, it might be useful to revisit the `map` and `filter` methods we used before and examine how they might be implemented internally.
275+
276+
In the examples below, we have defined alternative implementations for `map` and `filter` and named them `myMap` and `myFilter`. If we run this code we are actually adding these methods to the existing `Array` constructor function (in general, it is a bad idea to modify standard JavaScript objects, but we use it here for illustrative purposes).
277+
278+
The `this` variable inside the method implementations refer to the array (which is technically an object: `typeof arr === 'object'`) on which the method is called.
279+
280+
As you can see, both methods use a `for` loop internally, saving us the trouble of writing a `for` loop ourselves. Both methods call a callback that was passed as a parameter. The callback, in its turn, is called for every loop iteration with three parameters, viz:
281+
282+
1. The current array element
283+
2. The current loop index value
284+
3. The complete array itself
285+
286+
For the `map` method, the value that we return from our callback is pushed onto a new, initially empty array.
287+
288+
For the `filter` method, the current element is pushed unmodified to a new, initially empty array if, and only if our callback returns a 'truthy' value.
289+
290+
Finally, both methods return the newly constructed array as their return value.
291+
230292
```js
231-
Array.prototype.myMap = function (mapFn) {
293+
Array.prototype.myMap = function (callback) {
232294
const arr = [];
233295
for (let i = 0; i < this.length; i++) {
234-
arr.push(mapFn(this[i], i, this));
296+
arr.push(callback(this[i], i, this));
235297
}
236298
return arr;
237299
};
238300
```
239301

240302

241303
```js
242-
Array.prototype.myFilter = function (predicateFn) {
304+
Array.prototype.myFilter = function (callback) {
243305
const arr = [];
244306
for (let i = 0; i < this.length; i++) {
245-
if (predicateFn(this[i], i, this)) {
307+
if (callback(this[i], i, this)) {
246308
arr.push(this[i]);
247309
}
248310
}

0 commit comments

Comments
 (0)