Skip to content

Commit 3908595

Browse files
committed
public & private, protected properties and methods
1 parent f0bc903 commit 3908595

File tree

2 files changed

+211
-2
lines changed

2 files changed

+211
-2
lines changed

.vscode/settings.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{
22
"cSpell.ignoreWords": [
3-
"iphone"
3+
"delonghi",
4+
"iphone",
5+
"nespresso"
46
]
57
}

ObjectOrientedProgramming.md

Lines changed: 208 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,4 +269,211 @@ function sayAge() {
269269
}
270270

271271
sayAge() // -> my age is 26
272-
```
272+
```
273+
274+
---
275+
<br>
276+
277+
## Inheritance
278+
279+
Here we're going to create a base (super) class of Character and then create a sub class of Elf which will extend Character, inheriting it's properties. Elf will now have a prototype of Character.
280+
281+
The constructor within the Elf class exists only within Elf. If we want to set any properties within this constructor using the `this` keyword, we have to use the `super` keyword to call the superclass first. In doing so `super` will run the constructor and give us an instance of the Character base class. The `this` keyword will now refer to that instance, and we can now add on additional properties for the Elf subclass.
282+
283+
```javascript
284+
class Character {
285+
constructor(name, weapon) {
286+
this.name = name;
287+
this.weapon = weapon;
288+
}
289+
attack() {
290+
return 'attack with ' + this.weapon;
291+
}
292+
}
293+
294+
class Elf extends Character {
295+
constructor(name, weapon, type) {
296+
super(name, weapon);
297+
this.type = type;
298+
}
299+
}
300+
301+
class Ogre extends Character {
302+
constructor(name, weapon, colour) {
303+
super(name, weapon);
304+
this.colour = colour;
305+
}
306+
makeFort() {
307+
return 'Strong fort made';
308+
}
309+
}
310+
311+
const dobby = new Elf('dobby', 'cloth', 'house');
312+
313+
const shrek = new Ogre('shrek', 'club', 'green');
314+
shrek.makeFort() // -> Strong fort made
315+
```
316+
317+
---
318+
<br>
319+
320+
## Public & Private - Protected properties and methods
321+
322+
<i>[Reference Article](https://javascript.info/private-protected-properties-methods)</i>
323+
324+
### Internal and external interface
325+
326+
In object-oriented programming, properties and methods are split into two groups:
327+
328+
<b>Internal interface</b> – Methods and properties are accessible from other methods of the class, but not from the outside.
329+
330+
<b>External interface</b> – Methods and properties are accessible also from outside the class.
331+
332+
In JavaScript, there are two types of object fields (properties and methods):
333+
334+
<b>Public:</b> accessible from anywhere. They comprise the external interface. Till now we were only using public properties and methods.
335+
336+
<b>Private:</b> accessible only from inside the class. These are for the internal interface.
337+
338+
<br>
339+
340+
#### <i>Example:</i> Let's make a coffee machine
341+
342+
```javascript
343+
class CoffeeMachine {
344+
waterAmount = 0;
345+
346+
constructor(power) {
347+
this.power = power;
348+
console.log(`Created a coffee machine. Power: ${power}.`)
349+
}
350+
}
351+
352+
const nespresso = new CoffeeMachine(100);
353+
// -> Created a coffee machine. Power: 100.
354+
355+
nespresso.waterAmount = 200;
356+
```
357+
358+
#### Protected properties
359+
360+
Now let’s make the `waterAmount` property protected so we have more control over it. For instance, we don’t want anyone to set it below zero.
361+
362+
Protected properties are usually prefixed with an underscore `_`.
363+
364+
```javascript
365+
class CoffeeMachine {
366+
_waterAmount = 0;
367+
368+
set waterAmount(value) {
369+
if (value < 0) throw new Error('Negative water');
370+
this._waterAmount = value;
371+
}
372+
373+
get waterAmount() {
374+
return this._waterAmount;
375+
}
376+
377+
constructor(power) {
378+
this.power = power;
379+
console.log(`Created a coffee machine. Power: ${power}.`)
380+
}
381+
}
382+
383+
const nespresso = new CoffeeMachine(100);
384+
// -> Created a coffee machine. Power: 100.
385+
386+
nespresso.waterAmount = -50; // -> Uncaught Error: Negative water
387+
```
388+
389+
#### Read-only properties
390+
391+
Let’s make the `power` property read-only.
392+
393+
Some properties will be set at creation time and never modified. That’s exactly the case for a coffee machine: power never changes.
394+
395+
To do so, we only need to make getter, but not the setter:
396+
397+
```javascript
398+
class CoffeeMachine {
399+
_waterAmount = 0;
400+
401+
set waterAmount(value) {
402+
if (value < 0) throw new Error('Negative water');
403+
this._waterAmount = value;
404+
}
405+
406+
get waterAmount() {
407+
return this._waterAmount;
408+
}
409+
410+
constructor(power) {
411+
this._power = power;
412+
}
413+
414+
get power() {
415+
return this._power;
416+
}
417+
}
418+
419+
const nespresso = new CoffeeMachine(100);
420+
421+
console.log(`Power is: ${nespresso.power}W`); // -> Power is 100W
422+
423+
nespresso.power = 250; // -> Error (no setter)
424+
```
425+
426+
#### Private properties
427+
428+
<i>Note: Not yet universally supported.</i>
429+
430+
There’s a finished JavaScript proposal, almost in the standard, that provides language-level support for private properties and methods.
431+
432+
Privates should start with `#`. They are only accessible from inside the class.
433+
434+
For instance, here’s a private `#waterLimit` property and the water-checking private method `#checkWater`:
435+
436+
```javascript
437+
class CoffeeMachine {
438+
#waterLimit = 200;
439+
440+
#checkWater(value) {
441+
if (value < 0) throw new Error("Negative water");
442+
if (value > this.#waterLimit) throw new Error("Too much water");
443+
}
444+
445+
}
446+
447+
let nespresso = new CoffeeMachine();
448+
449+
// can't access privates from outside of the class
450+
nespresso.#checkWater(); // Error
451+
nespresso.#waterLimit = 1000; // Error
452+
```
453+
454+
On the language level, `#` is a special sign that the field is private. We can’t access it from outside or from inheriting classes.
455+
456+
Private fields do not conflict with public ones. We can have both private `#waterAmount` and public`#waterAmount` fields at the same time.
457+
458+
```javascript
459+
class CoffeeMachine {
460+
461+
#waterAmount = 0;
462+
463+
get waterAmount() {
464+
return this.#waterAmount;
465+
}
466+
467+
set waterAmount(value) {
468+
if (value < 0) throw new Error("Negative water");
469+
this.#waterAmount = value;
470+
}
471+
}
472+
473+
let machine = new CoffeeMachine();
474+
475+
machine.waterAmount = 100;
476+
alert(machine.#waterAmount); // Error
477+
```
478+
479+
Unlike protected ones, private fields are enforced by the language itself. That’s a good thing. But if we inherit from `CoffeeMachine`, then we’ll have no direct access to `#waterAmount`. We’ll need to rely on `waterAmount` getter/setter. In many scenarios such limitation is too severe. If we extend a `CoffeeMachine`, we may have legitimate reason to access its internals. That’s why protected fields are used more often, even though they are not supported by the language syntax.

0 commit comments

Comments
 (0)