diff --git a/javascript/organizing_your_javascript_code/factory_functions_and_module_pattern.md b/javascript/organizing_your_javascript_code/factory_functions_and_module_pattern.md index 3606190be04..a9aa824ddb2 100644 --- a/javascript/organizing_your_javascript_code/factory_functions_and_module_pattern.md +++ b/javascript/organizing_your_javascript_code/factory_functions_and_module_pattern.md @@ -6,14 +6,12 @@ We have discussed object constructors in the previous lesson. However, they are This section contains a general overview of topics that you will learn in this lesson. -- Describe the scope of a variable. -- Explore what closures are. -- Briefly consider the disadvantages of using constructors. -- Discuss Factory functions with examples. -- Discuss Private variables and functions concerning factory functions. -- Showcase object inheritance with the help of factory functions. -- Describe what module pattern and IIFEs are. -- Discuss encapsulation and how the module pattern helps with namespacing. +- Variable scope. +- Closures. +- Factory functions. +- Private variables. +- IIFEs and the module pattern. +- Encapsulation. ### Scoopfuls of scopes @@ -102,11 +100,11 @@ One of the key arguments is how they *look* like regular JavaScript functions, e Yet another issue stems from misusing `instanceof`. In other programming languages, the keyword is a reliable way to know the code with which an object was made; but in JavaScript, it checks the presence of a constructor's prototype in an object's *entire* prototype chain - which does nothing to confirm if an object was made with that constructor since the constructor's prototype can even be reassigned after the creation of an object. -Because of that, constructors have become unpopular in favor of a pattern that is similar but addresses a ton of these problems by not relying on those troublesome features: Factory Functions. +Because of that, some people (not all) disliked using constructors in favor of a pattern that is similar but addresses a ton of these problems by not relying on those troublesome features: Factory Functions. ### Factory functions 🏭 -These fancy-sounding functions work very similar to how constructors did, but with one key difference - they levy the power of closures. Instead of using the `new` keyword to create an object, factory functions set up and return the new object when you call the function. They do not use the prototype, which incurs a performance penalty - but as a general rule, this penalty isn’t significant unless you’re creating thousands of objects. Let's take a basic example to compare them to constructor functions. +These fancy-sounding functions work very similar to how constructors did, but with one key difference - they levy the power of closures. Instead of using the `new` keyword to create an object, factory functions set up and return the new object when you call the function. They do not use the prototype, which does incur a performance penalty, but as a general rule, this penalty isn’t significant unless you’re creating thousands of objects. Let's take a basic example to compare them to constructor functions. ```javascript function User(name) { @@ -253,7 +251,22 @@ function createPlayer(name, level) { } ``` -### The module pattern: IIFEs +### The module pattern + +#### IIFEs + +Oftentimes, you do not need a factory to produce multiple objects - instead, you are using it to wrap sections of code together, hiding the variables and functions that you do not need elsewhere as private. This is easily achievable by wrapping your factory function in parentheses and immediately calling (invoking) it. This immediate function call is commonly referred to as an Immediately Invoked Function Expression (duh) or IIFE in short. IIFEs are quite literally just function expressions that are called immediately: + +```javascript +// This is a function expression +() => console.log("foo"); + +// The function expression is now an IIFE! +// Although this one is not particularly useful of course +(() => console.log("foo"))(); +``` + +#### Using IIFEs to implement the module pattern
@@ -263,39 +276,44 @@ ECMAScript 6 (released in 2015) introduced a new JavaScript feature called "modu
-Oftentimes, you do not need a factory to produce multiple objects - instead, you are using it to wrap sections of code together, hiding the variables and functions that you do not need elsewhere as private. This is easily achievable by wrapping your factory function in parentheses and immediately calling (invoking) it. This immediate function call is commonly referred to as an Immediately Invoked Function Expression (duh) or IIFE in short. IIFEs are quite literally just function expressions that are called immediately: - -```javascript -// This is an IIFE! Though not particularly useful, of course. -(() => console.log('foo'))(); -``` - A more helpful use of IIFEs is the pattern of wrapping "private" code inside an IIFE: the module pattern. This is often done with factory functions: ```javascript -const calculator = (function () { - const add = (a, b) => a + b; - const sub = (a, b) => a - b; - const mul = (a, b) => a * b; - const div = (a, b) => a / b; - - return { add, sub, mul, div }; +const calculator = (() => { + let lastResult; + + const add = (a, b) => { + lastResult = a + b; + return lastResult; + }; + const subtract = (a, b) => { + lastResult = a - b; + return lastResult; + }; + const multiply = (a, b) => { + lastResult = a * b; + return lastResult; + }; + const divide = (a, b) => { + lastResult = a / b; + return lastResult; + }; + const getLastResult = () => lastResult; + + return { add, subtract, multiply, divide, getLastResult }; })(); -calculator.add(3,5); // 8 -calculator.sub(6,2); // 4 -calculator.mul(14,5534); // 77476 +console.log(calculator.add(3, 5)); // 8 +console.log(calculator.subtract(6, 2)); // 4 +console.log(calculator.getLastResult()); // 4 +console.log(calculator.multiply(14, 5534)); // 77476 ``` -In this example, we have a factory function creating some basic operations that we need only once. We can wrap it in parentheses and immediately call it by adding `()` - returning the result object that we store in `calculator`. In this way we can write code, wrapping away things that we do not need as private variables and functions inside our factory function and while they are tucked inside of our module, we can use the returned variables and functions outside the factory, as necessary. - -#### Encapsulating with the module pattern - -At first glance, this does not seem particularly useful. If we have some code that we use only once, why not write it in the main section of our JavaScript file itself? After all, the power of factory functions lies in being, well, a factory to make multiple objects, right? +Here, we have a calculator with four basic arithmetic methods and a method to read the most recent calculation's result. We only want the one calculator object but we still use a factory function! Why not just use an object literal directly? -This is where we encounter the word **encapsulation** - bundling data, code, or something into a single unit, with selective access to the things inside that unit itself. While it sounds general, this is what happens when we wrap, or encapsulate our code into modules - we don't expose everything to the body of our program itself. This encapsulation leads to an effect called **namespacing**. Namespacing is a technique that is used to avoid naming collisions in our programs. +All object properties are public whether we like it or not. If we just made a calculator object literal, we'd have a `.lastResult` property that's public, meaning it allows the possibility of reassigning it directly (e.g. `calculator.lastResult = 111100105110`). We want to keep `lastResult` private and expose the value publicly for reading only. Reassignment should only happen internally on our terms. The only way we can truly hide the `lastResult` variable from anything that doesn't actually need it would be to put it inside a function, away from the outside scope, then create the object within the same scope and return it. A factory function... that we only need to call once! -Take the calculator example into consideration. It's very easy to imagine a scenario where you can accidentally create multiple functions with the name `add`. What does `add` do - does it add two numbers? Strings? Does it take its input directly from the DOM and display the result? What would you name the functions that do these things? Instead, we can easily encapsulate them inside a module called `calculator` which generates an object with that name, allowing us to explicitly call `calculator.add(a, b)` or `calculator.sub(a, b)`. +This is where we encounter the word **encapsulation**: bundling data, code, or something into a single unit, with selective access to the things inside that unit itself. While it sounds general, this is what happens when we wrap (or encapsulate) our code into modules. We don't expose everything to the body of our program itself, only what is needed for other things to interact with whatever's inside the "module". #### Why the IIFE? @@ -323,5 +341,5 @@ The following questions are an opportunity to reflect on key topics in this less - [What are private variables in factory functions and how can they be useful?](#private-variables-and-functions) - [How can we implement prototypal inheritance with factory functions?](#prototypal-inheritance-with-factories) - [How does the module pattern work?](https://dev.to/tomekbuszewski/module-pattern-in-javascript-56jm) -- [What does IIFE stand for and what are they?](#the-module-pattern-iifes) -- [What is the concept of namespacing and how do factory functions help with encapsulation?](#encapsulating-with-the-module-pattern) +- [What does IIFE stand for and what are they?](#iifes) +- [How do factory functions help with encapsulation?](#using-iifes-to-implement-the-module-pattern) diff --git a/javascript/organizing_your_javascript_code/objects_and_object_constructors.md b/javascript/organizing_your_javascript_code/object_constructors.md similarity index 69% rename from javascript/organizing_your_javascript_code/objects_and_object_constructors.md rename to javascript/organizing_your_javascript_code/object_constructors.md index c4b302afa34..4303f6640aa 100644 --- a/javascript/organizing_your_javascript_code/objects_and_object_constructors.md +++ b/javascript/organizing_your_javascript_code/object_constructors.md @@ -1,151 +1,70 @@ ### Introduction -In our JavaScript fundamentals course, you should have learned the [basics of using objects](https://www.theodinproject.com/paths/foundations/courses/foundations/lessons/fundamentals-part-5) to store and retrieve data. Let's start with a little refresher. - -There are multiple ways to define objects but in most cases, it is best to use the **object literal** syntax as follows: - -```javascript -const myObject = { -  property: 'Value!', -  otherProperty: 77, -  "obnoxious property": function() { -    // do stuff! -  } -}; -``` - -There are also 2 ways to get information out of an object: dot notation and bracket notation. - -```javascript -// dot notation -myObject.property; // 'Value!' - -// bracket notation -myObject["obnoxious property"]; // [Function] -``` - -Which method you use will depend on context. Dot notation is cleaner and is usually preferred, but there are plenty of circumstances when it is not possible to use it. For example, `myObject."obnoxious property"` won't work because that property is a string with a space in it. Likewise, you cannot use variables in dot notation: - -```javascript -const variable = 'property'; - -myObject.variable; // this gives us 'undefined' because it's looking for a property named 'variable' in our object - -myObject[variable]; // this is equivalent to myObject['property'] and returns 'Value!' -``` - -If you are feeling rusty on using objects, now might be a good time to go back and review the content in our [object basics lesson](https://www.theodinproject.com/lessons/foundations-object-basics) from our JavaScript Basics course. +Now that you've got a basic understanding of *why* and *how* you might use objects to organize data and functionality, it's important to learn some basic strategies for creating duplicates (often called **instances**) of objects, and using existing types of objects as a base for creating new ones through **inheritance**. ### Lesson overview This section contains a general overview of topics that you will learn in this lesson. -- How to write an object constructor and instantiate the object. +- Instantiating objects using constructors. - What a prototype is and how it can be used. - Prototypal inheritance. - Basic do's and don'ts of prototypal inheritance. -- The `this` keyword. - -### Objects as a design pattern - -One of the simplest ways you can begin to organize your code is by grouping things into objects. Take these examples from a 'tic tac toe' game: - -```javascript -// example one -const playerOneName = "tim"; -const playerTwoName = "jenn"; -const playerOneMarker = "X"; -const playerTwoMarker = "O"; - -// example two -const playerOne = { -  name: "tim", -  marker: "X" -}; - -const playerTwo = { -  name: "jenn", -  marker: "O" -}; -``` - -At first glance, the first doesn't seem so bad... and it actually takes fewer lines to write than the example using objects, but the benefits of the second approach are huge! Let me demonstrate: - -```javascript -function printName(player) { -  console.log(player.name); -} -``` - -This is something that you just could NOT do with the example one setup. Instead, every time you wanted to print a specific player's name, you would have to remember the correct variable name and then manually `console.log` it: - -```javascript -console.log(playerOneName); -console.log(playerTwoName); -``` - -Again, this isn't *that* bad... but what if you *don't know* which player's name you want to print? - -```javascript -function gameOver(winningPlayer){ -  console.log("Congratulations!"); -  console.log(winningPlayer.name + " is the winner!"); -} -``` - -Or, what if we aren't making a 2 player game, but something more complicated such as an online shopping site with a large inventory? In that case, using objects to keep track of an item's name, price, description and other things is the only way to go. Unfortunately, in that type of situation, manually typing out the contents of our objects is not feasible either. We need a cleaner way to create our objects, which brings us to... +- How the `this` keyword behaves in different situations. ### Object constructors -When you have a specific type of object that you need to duplicate like our player or inventory items, a better way to create them is using an object constructor, which is just a regular function that by convention is named with an uppercase initial letter. It looks like this: +Manually typing out the contents of all of our objects with object literals is not always feasible. When you have a specific type of object that you need to make multiple of, a better way to create them is using an object constructor, which is really just a function: ```javascript function Player(name, marker) { -  this.name = name; -  this.marker = marker; + this.name = name; + this.marker = marker; } ``` -and you can use it by calling the function with the keyword `new`. +The only difference is that you use it by calling the function with the keyword `new`: ```javascript -const player = new Player('steve', 'X'); -console.log(player.name); // 'steve' +const player = new Player("steve", "X"); +console.log(player.name); // "steve" ``` -Just like with objects created using the Object Literal method, you can add functions to the object: +This is not the same as calling `Player("steve", "X")` (without the `new` keyword). When we call a function with `new`, it creates a new object, makes `this` inside the function refer to that object, and makes that object inherit from the function's `.prototype` property (more on that later). The new object is then returned (even though we don't specify a `return` value in the constructor function). + +Just like with objects created using the object literal method, you can add functions to the object: ```javascript function Player(name, marker) { -  this.name = name; -  this.marker = marker; -  this.sayName = function() { -    console.log(this.name) -  }; + this.name = name; + this.marker = marker; + this.sayName = function() { + console.log(this.name); + }; } -const player1 = new Player('steve', 'X'); -const player2 = new Player('also steve', 'O'); -player1.sayName(); // logs 'steve' -player2.sayName(); // logs 'also steve' +const player1 = new Player("steve", "X"); +const player2 = new Player("also steve", "O"); +player1.sayName(); // logs "steve" +player2.sayName(); // logs "also steve" ```
#### Safeguarding constructors -Note that, as constructors are just regular functions, they could be called without using `new` by mistake, which would cause hard-to-track errors. To prevent that, you can use the `new.target` meta-property like this: +Since constructors can be called without using `new` by mistake, which would cause hard-to-track errors as it won't do all the new object and `this` binding stuff, we should safeguard them. You can use the `new.target` meta-property like this, which will throw an error if `Player` is called without `new`: ```javascript function Player(name, marker) { if (!new.target) { throw Error("You must use the 'new' operator to call the constructor"); } -  this.name = name; -  this.marker = marker; -  this.sayName = function() { -    console.log(this.name) -  }; + this.name = name; + this.marker = marker; + this.sayName = function() { + console.log(this.name); + }; } ``` @@ -155,17 +74,19 @@ function Player(name, marker) { Write a constructor for making "Book" objects. We will revisit this in the next project. Your book objects should have the book's `title`, `author`, the number of `pages`, and whether or not you have `read` the book. -Put a function into the constructor that can report the book info like so: +Put a function `info()` into the constructor that can report the book info like so: ```javascript -theHobbit.info(); // "The Hobbit by J.R.R. Tolkien, 295 pages, not read yet" +console.log(theHobbit.info()); // "The Hobbit by J.R.R. Tolkien, 295 pages, not read yet" ``` -Note: It is almost *always* best to `return` things rather than putting `console.log()` directly into the function. In this case, return the `info` string and log it after the function has been called: +
-```javascript -console.log(theHobbit.info()); -``` +#### console.log vs return + +We use examples of functions that call `console.log()` for demonstration, but instead of making functions directly log things, it's generally more sensible to make them `return` values. That way, you can pass the values wherever you wish without being tied to whatever that function does; you may not always want to log the value. + +
### The prototype @@ -210,7 +131,7 @@ The last sub-item needs a little more explanation. What does defining 'on the pr ```javascript Player.prototype.sayHello = function() { - console.log("Hello, I'm a player!"); + console.log("Hello, I'm a player!"); }; player1.sayHello(); // logs "Hello, I'm a player!" @@ -261,14 +182,14 @@ What's this `.valueOf` function, and where did it come from if we did not define How do we know that this `.valueOf` function is defined on `Object.prototype`? We make use of another function called `.hasOwnProperty`: ```javascript -player1.hasOwnProperty('valueOf'); // false -Object.prototype.hasOwnProperty('valueOf'); // true +player1.hasOwnProperty("valueOf"); // false +Object.prototype.hasOwnProperty("valueOf"); // true ``` Now where did this [`.hasOwnProperty` function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) come from? A quick check helps: ```javascript -Object.prototype.hasOwnProperty('hasOwnProperty'); // true +Object.prototype.hasOwnProperty("hasOwnProperty"); // true ``` Essentially, this is how JavaScript makes use of prototypes. An object inherits from its `[[Prototype]]` object which in turn inherits from its own `[[Prototype]]` etc., thus forming a chain. This kind of inheritance using prototypes is hence named as Prototypal inheritance. JavaScript figures out which properties exist (or do not exist) on the object and starts traversing the chain to find the property or function, like so: @@ -294,16 +215,16 @@ function Person(name) { } Person.prototype.sayName = function() { -  console.log(`Hello, I'm ${this.name}!`); + console.log(`Hello, I'm ${this.name}!`); }; function Player(name, marker) { this.name = name; -  this.marker = marker; + this.marker = marker; } Player.prototype.getMarker = function() { - console.log(`My marker is '${this.marker}'`); + console.log(`My marker is "${this.marker}"`); }; Object.getPrototypeOf(Player.prototype); // returns Object.prototype @@ -312,14 +233,14 @@ Object.getPrototypeOf(Player.prototype); // returns Object.prototype Object.setPrototypeOf(Player.prototype, Person.prototype); Object.getPrototypeOf(Player.prototype); // returns Person.prototype -const player1 = new Player('steve', 'X'); -const player2 = new Player('also steve', 'O'); +const player1 = new Player("steve", "X"); +const player2 = new Player("also steve", "O"); player1.sayName(); // Hello, I'm steve! player2.sayName(); // Hello, I'm also steve! -player1.getMarker(); // My marker is 'X' -player2.getMarker(); // My marker is 'O' +player1.getMarker(); // My marker is "X" +player2.getMarker(); // My marker is "O" ``` From the code, we can see that we've defined a `Person` from whom a `Player` inherits properties and functions, and that the created `Player` objects are able to access both the `.sayName` and the `.getMarker` functions, in spite of them being defined on two separate `.prototype` objects! This is enabled by the use of the `Object.setPrototypeOf()` function. It takes two arguments - the first is the one which inherits and the second argument is the one which you want the first argument to inherit from. This ensures that the created `Player` objects are able to access the `.sayName` and `.getMarker` functions through their prototype chain. @@ -342,12 +263,12 @@ function Person(name) { } Person.prototype.sayName = function() { -  console.log(`Hello, I'm ${this.name}!`); + console.log(`Hello, I'm ${this.name}!`); }; function Player(name, marker) { this.name = name; -  this.marker = marker; + this.marker = marker; } // Don't do this! @@ -355,8 +276,8 @@ function Player(name, marker) { Player.prototype = Person.prototype; function Enemy(name) { -  this.name = name; -  this.marker = '^'; + this.name = name; + this.marker = "^"; } // Not again! @@ -364,10 +285,10 @@ function Enemy(name) { Enemy.prototype = Person.prototype; Enemy.prototype.sayName = function() { - console.log('HAHAHAHAHAHA'); + console.log("HAHAHAHAHAHA"); }; -const carl = new Player('carl', 'X'); +const carl = new Player("carl", "X"); carl.sayName(); // Uh oh! this logs "HAHAHAHAHAHA" because we edited the sayName function! ``` @@ -394,12 +315,3 @@ The following questions are an opportunity to reflect on key topics in this less - [What is prototypal inheritance?](https://javascript.info/prototype-inheritance) - [What are the basic do's and don't's of prototypal inheritance?](#recommended-method-for-prototypal-inheritance) - [How does `this` behave in different situations?](https://www.javascripttutorial.net/javascript-this/) - -### Additional resources - -This section contains helpful links to related content. It isn't required, so consider it supplemental. - -- This [`Object.create` method video by techsith](https://www.youtube.com/watch?v=MACDGu96wrA) provides another point of view on how to use `Object.create` to extend objects by setting the prototype. -- The first answer on this StackOverflow question regarding [defining methods via the prototype vs in the constructor](https://stackoverflow.com/questions/9772307/declaring-javascript-object-method-in-constructor-function-vs-in-prototype/9772864#9772864) helps explain when you might want to use one over the other. -- [Interactive Scrim on objects and object constructors.](https://scrimba.com/scrim/co2624f87981575448091d5a2) -- Check out this video explanation on the [`this` keyword from DevSage](https://www.youtube.com/watch?v=cwChC4BQF0Q) that gives a different perspective on how its context changes, as well as scenarios in which `this` behaves unexpectedly. diff --git a/javascript/organizing_your_javascript_code/organizing_code_with_objects.md b/javascript/organizing_your_javascript_code/organizing_code_with_objects.md new file mode 100644 index 00000000000..69cc6779bfd --- /dev/null +++ b/javascript/organizing_your_javascript_code/organizing_code_with_objects.md @@ -0,0 +1,261 @@ +### Introduction + +In our JavaScript fundamentals course, you should have learned the [basics of using objects](https://www.theodinproject.com/paths/foundations/courses/foundations/lessons/fundamentals-part-5) to store and retrieve data. In this lesson, we'll start with a little refresher, then explore using objects in more detail. Folks on the Ruby track, you'll have a little more experience with some of these concepts already, but this lesson will still be good to go through as JavaScript is a little more flexible with its use of objects. + +### Lesson overview + +This section contains a general overview of topics that you will learn in this lesson. + +- Using objects to organize data. +- Using objects to organize functionality. +- Object methods and the `this` keyword. + +### Refresher + +There are multiple ways to define objects in JavaScript, but in many cases **object literal** syntax is used as follows: + +```javascript +const myObject = { + property: "Value!", + otherProperty: 77, + "obnoxious property": function() { + // do stuff! + }, +}; +``` + +There are also 2 ways to get information out of an object: dot notation and bracket notation. + +```javascript +// dot notation +console.log(myObject.property); // "Value!" + +// bracket notation +console.log(myObject["obnoxious property"]); // [Function] +``` + +Which method you use will depend on context. Dot notation is cleaner and is usually preferred, but there are plenty of circumstances when it is not possible to use it. For example, `myObject."obnoxious property"` won't work because that property is a string with a space in it. Likewise, you cannot use variables in dot notation: + +```javascript +const variable = "property"; + +// "undefined" because it's looking for a property named "variable" in our object +console.log(myObject.variable); + +// this is equivalent to myObject["property"] and returns "Value!" +console.log(myObject[variable]); +``` + +### Objects as a data structure + +You've already been introduced to the basic use of a JavaScript object - storing related information with key/value pairs. This is one of the simplest ways you can begin to organize your code! Take these examples from a 'tic tac toe' game - first without objects: + +```javascript +// without objects +const playerOneName = "tim"; +const playerTwoName = "jenn"; +const playerOneMarker = "X"; +const playerTwoMarker = "O"; +``` + +Now using objects: + +```javascript +// with objects +const playerOne = { + name: "tim", + marker: "X", +}; + +const playerTwo = { + name: "jenn", + marker: "O", +}; +``` + +At first glance, the first doesn't seem so bad... but the benefits of the second approach are huge! Since the values are assigned to keys in objects, instead of a bunch of long and isolated variable names, we can use briefer variable names that are arguably easier to read at a glance, where the object name helps give context. Normally, `name` and `marker` would not be reusable in the same scope, but via "namespacing", we can still use them as part of `playerOne.name` or `playerTwo.marker` etc. Folks on the Ruby track may be familiar with doing something similar via Ruby's modules; in JavaScript, it's very normal to use objects for this purpose. + +But naming isn't the only benefit. Grouping related data together into objects allows you to pass all the data around more easily. For example: + +```javascript +function gameOver(winningPlayer) { + console.log("Congratulations!"); + console.log(`${winningPlayer.name} (${winningPlayer.marker}) is the winner!`); +} +``` + +Instead of having to have separate parameters for the name and the marker, we have just the one for the entire object and now we have access to all the properties within. If we end up adding more properties to the player objects and needing them in the `gameOver` function, again, we have access to that already since we passed the whole object in, rather than into individual parameters. + +But what if we aren't making a simple 2-player game? Something more complicated such as an online shopping site with a large inventory? Using objects to group together each particular item's name, price, description and other things is the only way to go. You will continue to use and see objects used in this way throughout the curriculum. + +### Objects as a design pattern + +The grouping power of objects isn't just useful for organizing data, it's useful for organizing *functionality* as well! Using objects for this purpose is one of the core tenants of Object Oriented Programming (OOP), which is a programming paradigm based on the concept of "objects", which can contain data and code: data in the form of fields (often known as attributes or properties), and code in the form of procedures (often known as methods). In OOP, computer programs are designed by making them out of objects that interact with one another. + +This means we're not limited to storing data in objects, we can store logic as well via **methods** (which are just functions that are part of an object), then use those methods to interact with the data. + +Nearly *anything* you can think about can be described as an object. To do so, all you have to do is ask yourself is "What properties (physical or conceptual) does my thing have?", and "How can I interact with it?". The properties or attributes of a *thing* are expressed as properties, and the ways you can interact with that thing are expressed as methods. + +Let's take an example of some *thing* - we'll choose a car. A car can have a make, model, registration year, color and price. These might be expressed as properties of an object: + +```javascript +const car = { + make: "Volkswagen", + model: "Golf", + year: 2026, + color: "blue", + priceUSD: 40000, +}; +``` + +You may want to have the ability to apply a discount to the car, or get a summary of all of the details in one go. For this, may want to use methods. The easiest way to get started creating methods to interact with your objects might be combining object literal syntax with JavaScript's `this` keyword. The `this` keyword is used to refer to the object a particular method is called from. + +```javascript +const car = { + make: "Volkswagen", + model: "Golf", + year: 2026, + color: "blue", + priceUSD: 40000, + + // a method is just a function assigned to a property + applyDiscount: function(discountPercentage) { + const multiplier = 1 - discountPercentage / 100; + this.priceUSD *= multiplier; + }, + // shorthand way to add a method to an object literal + getSummary() { + return `${this.year} ${this.make} ${this.model} in ${this.color}, priced at $${priceUSD} (USD).`; + }, + + // ...any other methods... +}; +``` + +
+ +#### Arrow functions and "this" + +The `this` keyword behaves differently inside arrow functions compared to traditional function expressions (which includes the shorthand syntax). We don't need to dive into how or why it differs yet, just know that if you used arrow functions in the above example, they would not behave as you expect. + +
+ +These methods use the `this` keyword to refer to the object they get called from (`car`). The `this` keyword can be used to read and assign properties of an object in exactly the same way you would for any other variable that points to an object, and we use methods just the same as we might use a function - creating reusable code under an intuitive name. Much nicer than manually writing the logic out each and every time we want to do some of those things. + +#### Objects for more abstract concepts + +Moving past physical items, we could also try to describe something a little bit more abstract like a game as an object. Since we've already explored Rock Paper Scissors in Foundations, let's use that as an example. + +A rock paper scissors game might involve a couple of basic things: + +- Players' scores +- The ability to play a round (and playing a round should update a player's score) + +And might also include a couple nice-to-haves: + +- The ability to determine the current winning player +- The ability to restart the game + +So, at its most basic, an object that represents the game might look something like this (assuming we're playing against a computer player): + +```javascript +const rps = { + playerScore: 0, + computerScore: 0, + playRound(playerChoice) { + // code to play the round, update score if needed, then return the result + }, +}; +``` + +And if we fleshed it out, our object may come out to look something like this: + +```javascript +const rps = { + playerScore: 0, + computerScore: 0, + playRound(playerChoice) { + // code to play the round, update score if needed, then return the result + }, + getWinningPlayer() { + // return the player with the most points ("player", "computer", or "tie") + }, + reset() { + // reset both players' scores to 0 + }, +}; +``` + +Let's play a bit and see who's in the lead! + +```javascript +rps.playRound("rock"); // returns "player" because we're awesome at RPS +console.log(rps.playerScore); // 1 - we won and so our score increased + +rps.playRound("rock"); // returns "computer" because... luck... +console.log(rps.computerScore); // 1 + +rps.playRound("scissors"); // returns "player" because we're awesome at RPS (again) +console.log(rps.playerScore); // 2 +console.log(rps.getWinningPlayer()); // "player" since we're 2-1 up +``` + +We've had enough fun for a day so let's reset everything for the next person. + +```javascript +rps.reset(); +console.log(rps.playerScore); // 0 +console.log(rps.computerScore); // 0 +``` + +
+ +#### Underscore properties + +Out in the wild, you may see code with object properties that start with `_` e.g. `_someProperty`. This is purely a developer convention that indicates the property is intended to be "private". A private property is one that's only meant for internal use and not meant to be read or called outside of the object itself (such as helper methods). + +JavaScript does not actually have the concept of real private properties for objects, at least not for object literals. Developers historically would add a leading `_` to indicate a property should be treated as if it was private, even if technically they'd still be accessible outside of the object internals ("public"). There are ways to implement actual privacy (which would actually prevent accessing something outside of the object itself) but they involve more advanced things and will be covered in later lessons. + +
+ +### Objects as machines + +As we've just seen, we can use objects to represent not just physical things but conceptual things too, such as the game of Rock Paper Scissors. Objects can be used to represent almost anything you can think of. It'll be impossible to give a comprehensive list of examples, but some uses include: + +- An object that manages other objects, such as an "inventory" object that contains item objects in an array, and methods that can be done to interact with that array of items +- An object that can listen for events that happen and respond appropriately (think of `.addEventListener` on DOM elements) +- An object that manages all things relating to the DOM, by setting event listeners that call other objects' methods, and displaying data from other objects on the web page. + +You may have trouble figuring out what these kinds of objects might contain at first, but these things come with experience, especially with later learning. One way you might conceptualize these objects though, might be to imagine them as little "machines" you're making out of code. + +The properties of the machine could be thought as if they were displays that might show information such as: + +- A list of the items you've collected and the maximum number of items you can carry +- A list of functions that are listening for an event +- The DOM elements for the buttons for interaction, and elements for displaying data + +The methods of your machine might be akin to buttons and such that make the machinde *do* a specific thing, such as: + +- Remove an item you own from a list and add new items +- Fire all the functions that are listening to a "click" event, or add a new function to listen to the "click" event +- Read data from somewhere else and set the `.textContent` of certain DOM elements + +Again, objects can be used to represent almost anything you can think of. The limit is your imagination! + +### Assignment + +
+ +No assignment for this particular lesson! While JavaScript is a very flexible language that involves concepts from many different programming paradigms, a lot of it is built around "Object-oriented programming" (OOP). + +While this lesson has touched on the basic ideas behind why we might use objects in the first place, we will explore these concepts much more practically in the coming lessons via a multitude of techniques for creating and using objects. + +
+ +### Knowledge check + +The following questions are an opportunity to reflect on key topics in this lesson. If you can't answer a question, click on it to review the material, but keep in mind you are not expected to memorize or master this knowledge. + +- [What are two ways you can use objects to organize code?](#objects-as-a-data-structure) +- [What is a method?](#objects-as-a-design-pattern) +- [In object methods, what is the `this` keyword used for?](#this-keyword)