diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..06e1b39
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "ltex.language": "en-GB"
+}
\ No newline at end of file
diff --git a/src/js/classes-and-instances.md b/src/js/classes-and-instances.md
index 895a2aa..69ae9df 100644
--- a/src/js/classes-and-instances.md
+++ b/src/js/classes-and-instances.md
@@ -1,5 +1,7 @@
# Classes and instances
+
+
In programming, a **class** is like a blueprint for creating objects. These
objects are known as **instances** of the class. Using a class for object
creation means objects are created the same way every time, which helps us
@@ -7,9 +9,7 @@ adhere to DRY (don't repeat yourself) principles.
## Defining a class
-Let's imagine we're writing software to manage smart home devices. We're going
-to be managing lots of smart lights, so we should make a class which will serve
-as a blueprint for every smart light in the home.
+We create a class using the `class` keyword.
```js
class SmartLight {
@@ -17,8 +17,7 @@ class SmartLight {
}
```
-As you can see, we use the `class` keyword followed by the desired name of the
-class. In javascript, we capitalise the first letter of class names.
+In javascript, it is conventional capitalise the first letter of class names.
## Creating instances
@@ -47,33 +46,13 @@ SmartLight {}
:::
The output shows us that we have two instances of `SmartLight`. But they don't
-have any properties yet. They are empty objects. Let's change that!
+have any properties yet. They are empty objects.
## Constructor function
A **constructor** is a special function inside a class that sets up new objects.
It's where we initialise properties.
-```js
-class SmartLight {
- constructor(color, brightness) {
- this.color = color
- this.brightness = brightness
- }
-}
-```
-
-Inside the constructor, we can't refer to the instances as `kitchenLight` or
-`bedroomLight` because they're still under construction! That's what the `this`
-key word is for: it refers to the current instance under production inside the
-class. Think of `this` as an unfinished smart light on the production line -
-we're adding things to it as we go.
-
-## Providing properties
-
-We can pass values to the constructor when we create new instances. This is what
-it looks like:
-
::: code-group
```js
@@ -98,44 +77,12 @@ SmartLight { color: 'cool blue', brightness: 50 }
:::
-### Instances are objects
-
-An instance of a class is just a javascript object, which means all of the
-normal stuff you can do with objects still works.
-
-::: code-group
-
-```js
-class SmartLight {
- constructor(color, brightness) {
- this.color = color
- this.brightness = brightness
- }
-}
-
-const kitchenLight = new SmartLight('warm white', 75)
-
-// access properties with dot notation
-console.log(kitchenLight.color)
-
-// update properties
-kitchenLight.brightness = 20
-console.log(kitchenLight.brightness)
-```
-
-```console [output]
-warm white
-20
-```
-
-:::
+Think of `this` as an unfinished smart light on the production line - we're
+adding things to it as we go.
## Additional properties
-There is no reason why all properties need to be passed to the constructor as
-arguments. The constructor can add extra properties. This is useful for adding
-default values that aren't intended to be provided by the creator of the
-instance.
+The constructor can add properties that are not passed in as arguments.
::: code-group
diff --git a/src/js/getters-and-setters.md b/src/js/getters-and-setters.md
index c07cf41..432599b 100644
--- a/src/js/getters-and-setters.md
+++ b/src/js/getters-and-setters.md
@@ -12,25 +12,27 @@ Let's revisit our smart light class:
```js
class SmartLight {
- #color
#brightness
+
constructor(color, brightness) {
- this.#color = color
+ this.color = color
this.#brightness = brightness
- this.isOn = false
}
getBrightness() {
- return `${this.#brightness}%`
+ if (this.isOn) {
+ return this.#brightness
+ } else {
+ return 0
+ }
}
- setBrightness(level) {
- if (level < 0 || level > 100) {
- console.log('Brightness must be between 0 and 100')
- } else {
- this.#brightness = level
- console.log(`Brightness set to ${this.#brightness}.`)
+ setBrightness(newBrightness) {
+ if (newBrightness < 0 || newBrightness > 100) {
+ throw new Error('Brightness must be between 0 and 100')
}
+
+ this.#brightness = newBrightness
}
}
```
@@ -41,27 +43,29 @@ Because the methods are concerned with getting and setting a property, they
could be refactored to make use of javascript's special `get` and `set` keywords
like this:
-```js{10,14}
+```js{9,17}
class SmartLight {
- #color
#brightness
+
constructor(color, brightness) {
- this.#color = color
+ this.color = color
this.#brightness = brightness
- this.isOn = false
}
- // [!code focus:13]
+
get brightness() {
- return `${this.#brightness}%`
+ if (this.isOn) {
+ return this.#brightness
+ } else {
+ return 0
+ }
}
- set brightness(level) {
- if (level < 0 || level > 100) {
- console.log('Brightness must be between 0 and 100')
- } else {
- this.#brightness = level
- console.log(`Brightness set to ${this.#brightness}.`)
+ set brightness(newBrightness) {
+ if (newBrightness < 0 || newBrightness > 100) {
+ throw new Error('Brightness must be between 0 and 100')
}
+
+ this.#brightness = newBrightness
}
}
```
@@ -78,29 +82,19 @@ but crucially:
This is what it looks like:
-::: code-group
-
```js
const bathroomLight = new SmartLight('amber', 50)
-// this will fail, it calls "set .brightness(104)"
+// this will throw an error, it calls "set brightness(104)"
bathroomLight.brightness = 104
-// but this is ok, it calls "set .brightness(50)"
+// but this is ok, it calls "set brightness(50)"
bathroomLight.brightness = 50
// this calls "get brightness()"
-console.log(bathroomLight.brightness)
+bathroomLight.brightness // 50
```
-```console [output]
-Brightness must be between 0 and 100
-Brightness set to 50.
-50%
-```
-
-:::
-
It is not necessary to use this special syntax, it comes down to style and
-preference. So long as your class is well documented and other developers can
+preference. So long as your class is well documented, and other developers can
use it, that is what's important.
diff --git a/src/js/inheritance.md b/src/js/inheritance.md
index bb4ba55..0c1dacc 100644
--- a/src/js/inheritance.md
+++ b/src/js/inheritance.md
@@ -1,94 +1,63 @@
# Inheritance
-Inheritance enables you to create a new class that is based on an existing
-class. The new class inherits the properties and methods of the existing class.
-This existing class is often referred to as the parent class, superclass, or
-base class, while the new class is known as the child class, subclass, or
-derived class.
-
-## Understanding Inheritance
-
-The primary benefit of inheritance is code reuse. You can define common
-functionality in a parent class and then extend this class to create child
-classes that inherit this functionality, adding or overriding properties and
-methods as needed.
-
-## Example Scenario
-
-Let's continue with our smart home theme. Imagine we have a general
-`SmartDevice` class that defines properties and methods common to all smart
-devices, like connectivity status or device name. We can then create more
-specific device classes (like `SmartLight` or `SmartCamera`) that inherit from
-`SmartDevice` and add their unique features.
+
## Creating a parent class
-First, we define our parent class, `SmartDevice`, with some basic properties and
-methods.
+If we want to create a set of classes that share some common functionality, we
+should create a parent class. This parent class will contain the shared
+functionality, and the child classes will inherit from it.
```js
class SmartDevice {
- constructor(name) {
- this.name = name
- this.isConnected = false
+ constructor() {
+ this.isOn = false
}
- connect() {
- this.isConnected = true
- console.log(`${this.name} is now connected.`)
+ togglePower() {
+ this.isOn = !this.isOn
}
}
```
-We don't intend to use this class to directly create objects, but we'll use it
-as a starting point to build our more specific classes.
-
## Inheriting from a parent class
-Next, we create a child class that inherits from `SmartDevice`. We use the
-`extends` keyword for inheritance in JavaScript.
+To inherit from a parent class, we use the `extends` keyword, and then call the
+`super()` method in the child class's constructor.
-An important step is calling the special `super()` function inside the child's
-`constructor()`. The `super()` function calls the parent's constructor, which is
-necessary to set up shared properties like `name` and `isConnected`.
+::: info
-::: code-group
+The `super()` method calls the parent class's constructor, passing in any
+arguments that are needed for the parent class's constructor.
-```js
-class SmartLight extends SmartDevice {
- constructor(name, color) {
- super(name) // Calls parent's constructor with the name parameter
- this.color = color
- }
+:::
+
+::: code-group
- changeColor(newColor) {
- this.color = newColor
- console.log(`${this.name}'s color changed to ${this.color}.`)
+```js {1,3}
+class SmartCamera extends SmartDevice {
+ constructor(location) {
+ super()
+ this.location = location
+ this.batteryLife = 100
}
}
-const studyLight = new SmartLight('Epic Lamp', 'yellow')
-console.log(studyLight)
-
-// use the inherited .connect() method
-studyLight.connect()
-
-// use the child-specific .changeColor() method
-studyLight.changeColor('ochre')
+const poolCam = new SmartCamera('Pool House')
+poolCam.togglePower()
+console.log(poolCam)
```
```console [output]
-SmartLight { name: 'Epic Lamp', isConnected: false, color: 'yellow' }
-Epic Lamp is now connected.
-Epic Lamp's color changed to ochre.
+SmartCamera {
+ isOn: true,
+ batteryLife: 100,
+ location: 'Pool House'
+}
```
:::
-Because we have used `extends` and `super()`, we now have access to all
-`SmartDevice` functionality on our `SmartLight`. Neat!
-
-The next step would be to create a `SmartCamera` class which also inherits from
-`SmartDevice`. Each child class doesn't need to implement things like `name` and
-`connect()`, meaning we do not repeat and have to maintain all the repeated
-code.
+Notice that the `SmartCamera` class has access to the `togglePower()` method and
+the `isOn` property, even though we didn't define them in the `SmartCamera`
+class. This is because `SmartCamera` inherits from `SmartDevice`.
diff --git a/src/js/instance-methods.md b/src/js/instance-methods.md
index 8dc9f23..9f0f990 100644
--- a/src/js/instance-methods.md
+++ b/src/js/instance-methods.md
@@ -1,52 +1,40 @@
# Instance methods
+
+
Instance methods are functions defined inside a class that operate on instances
of the class. They can access and modify the instance's properties, take
-parameters, return values, and even call other methods. Let's continue with our
-smart home theme and enhance our `SmartLight` class.
+parameters, return values, and even call other methods
## Defining a method
Let's start by adding a simple method `.togglePower()` to toggle our smart light
on and off.
-::: code-group
-
-```js
+```js{8-10}
class SmartLight {
constructor(color, brightness) {
this.color = color
this.brightness = brightness
this.isOn = false
}
- // [!code focus:5]
+
togglePower() {
this.isOn = !this.isOn
- console.log(`Light is now ${this.isOn ? 'on' : 'off'}.`)
}
}
-// [!code focus:6]
-const kitchenLight = new SmartLight('warm white', 75)
-// we access methods using dot notation:
-kitchenLight.togglePower()
-kitchenLight.togglePower()
-```
+const kitchenLight = new SmartLight('warm white', 75)
-```console [output]
-Light is now on.
-Light is now off.
+kitchenLight.togglePower() // on
+kitchenLight.togglePower() // off
```
-:::
-
## Methods with parameters
Methods can take parameters to allow more dynamic operations.
-::: code-group
-
-```js
+```js{12-14}
class SmartLight {
constructor(color, brightness) {
this.color = color
@@ -56,35 +44,22 @@ class SmartLight {
togglePower() {
this.isOn = !this.isOn
- console.log(`Light is now ${this.isOn ? 'on' : 'off'}.`)
}
- // [!code focus:5]
+
changeColor(newColor) {
this.color = newColor
- console.log(`Light color changed to ${this.color}.`)
}
}
-// [!code focus:3]
const kitchenLight = new SmartLight('warm white', 75)
kitchenLight.changeColor('lava red')
```
-```console [output]
-Light color changed to lava red.
-```
-
-:::
-
## Returning values
-Methods can return values as well, which allows instances to do useful
-calculations. For example, we could add a `.currentBrightness()` which returns
-`0` if the light is off, otherwise it returns the brightness.
-
-::: code-group
+Methods can return values as well.
-```js
+```js{16-22}
class SmartLight {
constructor(color, brightness) {
this.color = color
@@ -94,14 +69,12 @@ class SmartLight {
togglePower() {
this.isOn = !this.isOn
- console.log(`Light is now ${this.isOn ? 'on' : 'off'}.`)
}
changeColor(newColor) {
this.color = newColor
- console.log(`Light color changed to ${this.color}.`)
}
- // [!code focus:8]
+
currentBrightness() {
if (this.isOn) {
return this.brightness
@@ -110,25 +83,14 @@ class SmartLight {
}
}
}
-// [!code focus:9]
-const kitchenLight = new SmartLight('warm white', 75)
-// check the brightness when off
-console.log(kitchenLight.currentBrightness())
+const kitchenLight = new SmartLight('warm white', 75)
+console.log(kitchenLight.currentBrightness()) // 0
-// now turn it on and call again
kitchen.togglePower()
-console.log(kitchenLight.currentBrightness())
+console.log(kitchenLight.currentBrightness()) // 75
```
-```console [output]
-0
-Light is now on.
-75
-```
-
-:::
-
## Calling other methods
The last thing we need to know about methods is that they can call each other
@@ -136,7 +98,7 @@ using the `this` key word.
::: code-group
-```js
+```js{26-33}
class SmartLight {
constructor(color, brightness) {
this.color = color
@@ -161,9 +123,8 @@ class SmartLight {
return 0
}
}
- // [!code focus:10]
+
factoryReset() {
- console.log('Reverting to factory settings...')
this.changeColor('white')
this.brightness = 100
@@ -172,7 +133,7 @@ class SmartLight {
}
}
}
-// [!code focus:8]
+
// make a light and turn it on
const bedroomLight = new SmartLight('cool blue', 50)
bedroomLight.togglePower()
@@ -183,11 +144,6 @@ console.log(bedroomLight)
```
```console [output]
-Light is now on.
-
-Reverting to factory settings...
-Light color changed to white.
-Light is now off.
SmartLight { color: 'white', brightness: 100, isOn: false }
```
diff --git a/src/js/private-properties.md b/src/js/private-properties.md
index afe9277..e2fe9d1 100644
--- a/src/js/private-properties.md
+++ b/src/js/private-properties.md
@@ -1,40 +1,23 @@
# Private properties
-In object-oriented programming, encapsulation is a core principle that involves
-bundling the data (properties) and methods that operate on the data into a
-single unit, or class, and restricting access to some of the object's
-components. Private properties are a way to enforce encapsulation in JavaScript.
-They are properties accessible only within the class that declares them, making
-them invisible to any code outside that class.
+
-## Understanding Private Properties
+## Defining private properties
Private properties in JavaScript are defined by prefixing the property name with
a hash (`#`). This syntax ensures that the property cannot be accessed or
modified directly from outside the class.
-### Why Use Private Properties?
-
-- **Encapsulation**: They help in keeping certain details of an object hidden
- from the outside world.
-- **Control**: They provide control over how a property is accessed or modified
- through public methods.
-- **Security**: They make it harder to access data.
-
-## Defining private properties
-
-Let's refactor our `SmartLight` class to benefit from private properties.
-
::: code-group
-```js
+```js{2,6}
class SmartLight {
- #color
- #brightness
+ #brightness // declare private property
+
constructor(color, brightness) {
- this.#color = color
+ this.color = color
this.#brightness = brightness
- this.isOn = false // Public property for simplicity
+ this.isOn = false
}
}
@@ -48,30 +31,22 @@ SmartLight { isOn: false }
:::
-Notice that the output only displays the `isOn` property - `color` and
-`brightness` are no longer accessible.
+Notice that the `brightness` property is not logged to the console.
+
+## Private properties cannot be accessed directly
If we try and modify these properties directly, we will get an error:
::: code-group
```js
-bathroomLight.#color = 'ice blue'
+bathroomLight.#brightness = 90
```
```console [output]
-bathroomLight.#color = 'ice blue'
- ^
-
+bathroomLight.#brightness = 90
+ ^
SyntaxError: Private field '#color' must be declared in an enclosing class
- at internalCompileFunction (node:internal/vm:73:18)
- at wrapSafe (node:internal/modules/cjs/loader:1153:20)
- at Module._compile (node:internal/modules/cjs/loader:1197:27)
- at Module._extensions..js (node:internal/modules/cjs/loader:1287:10)
- at Module.load (node:internal/modules/cjs/loader:1091:32)
- at Module._load (node:internal/modules/cjs/loader:938:12)
- at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:83:12)
- at node:internal/main/run_main_module:23:47
```
:::
@@ -81,95 +56,29 @@ SyntaxError: Private field '#color' must be declared in an enclosing class
Since private properties cannot be accessed directly from outside the class, we
need to provide public _methods_ to work with them.
-::: code-group
-
-```js
+```js{9-15,17-23}
class SmartLight {
- #color
#brightness
- constructor(color, brightness) {
- this.#color = color
- this.#brightness = brightness
- this.isOn = false // Public property for simplicity
- }
- // [!code focus:8]
- getColor() {
- return this.#color
- }
-
- setColor(newColor) {
- this.#color = newColor
- console.log(`Color set to ${this.#color}.`)
- }
-}
-// [!code focus:4]
-const bathroomLight = new SmartLight('amber', 50)
-console.log(bathroomLight.getColor())
-bathroomLight.setColor('neon green')
-```
-```console [output]
-amber
-Color set to neon green.
-```
-
-:::
-
-## What is the point?
-
-By now, you are probably wondering why bother with all this? It's a fair
-question - we can read and write properties with our public methods just the
-same as if we accessed them directly.
-
-In the next example, though, we will see that private properties with public
-methods allow us to control **how** people interact with the object.
-
-::: code-group
-
-```js
-class SmartLight {
- #color
- #brightness
constructor(color, brightness) {
- this.#color = color
+ this.color = color
this.#brightness = brightness
- this.isOn = false // Public property for simplicity
}
- // [!code focus:13]
+
getBrightness() {
- return `${this.#brightness}%`
+ if (this.isOn) {
+ return this.#brightness
+ } else {
+ return 0
+ }
}
- setBrightness(level) {
- if (level < 0 || level > 100) {
- console.log('Brightness must be between 0 and 100.')
- } else {
- this.#brightness = level
- console.log(`Brightness set to ${this.#brightness}.`)
+ setBrightness(newBrightness) {
+ if (newBrightness < 0 || newBrightness > 100) {
+ throw new Error('Brightness must be between 0 and 100')
}
+
+ this.#brightness = newBrightness
}
}
-// [!code focus:5]
-const bathroomLight = new SmartLight('amber', 50)
-bathroomLight.setBrightness(104)
-bathroomLight.setBrightness(50)
-console.log(bathroomLight.getBrightness())
-```
-
-```console [output]
-Brightness must be between 0 and 100.
-Brightness set to 50.
-50%
```
-
-:::
-
-The `.getBrightness()` method formats the return value with a `%` symbol.
-Although the brightness is stored privately as an integer, which is more
-convenient for internal class operations like adjusting the brightness, it is
-displayed publicly as a formatted string, which is better for presentation.
-
-The `.setBrightness()` method prevents the user from setting the brightness
-level to a value outside `0` to `100`. We allow the brightness to be modified,
-but we've taken control over _how_ it can be modified as the user cannot
-directly overwrite the property with an invalid number.
diff --git a/src/js/static-properties.md b/src/js/static-properties.md
index 329e926..151c422 100644
--- a/src/js/static-properties.md
+++ b/src/js/static-properties.md
@@ -1,62 +1,31 @@
# Static properties
-Static properties and methods in JavaScript provide a way for you to add
-properties and functions to classes themselves, rather than to instances of
-those classes. This feature is incredibly useful for utility functions,
-constants, or any data and methods that should be shared across all instances of
-a class.
-
-## Understanding static properties
-
-Static properties are part of the class definition itself. They are accessed
-using the class name, _not_ an instance of the class. This makes them ideal for
-utility functions or data that is common to all instances of a class.
-
-Why Use Static Properties and Methods?
-
-- **Utility methods**: Perfect for utility or helper functions that don't
- require access to instance data.
-- **Constants**: Useful for defining constants related to the class.
-- **Singleton patterns**: Can assist in implementing singleton patterns by
- ensuring only one instance of the class can be created.
+
## Identifying static properties
Let's say we're adding some smart cameras to our smart home system.
-::: code-group
-
```js
+import SmartDevice from './SmartDevice.js'
+
class SmartCamera {
constructor(location) {
this.location = location
- this.batteryLife = 100 // charged by default
+ this.batteryLife = 100
}
}
const gardenCam = new SmartCamera('Garden')
-console.log(gardenCam)
```
-```console [output]
-SmartCamera {
- location: 'Garden',
- batteryLife: 100
-}
-```
-
-:::
-
-Now let us suppose we need to manage assigning ip addresses to all the cameras.
-Should we put this functionality on camera instances? _No_. One camera doesn't
-have access to the properties of another camera, so it wouldn't know which
-addresses are taken and which aren't. Because this is "meta" information about
-the whole class of cameras, it belongs as a static property.
+If we want to add a property to our camera class which is shared by all cameras,
+we should use a static property.
## Defining static properties
Let's add a static property to our camera class which holds information about
-the ip range cameras are allowed to use.
+the IP range cameras are allowed to use.
::: code-group