One place for hosting & domains

      Understanding

      Understanding Modules and Import and Export Statements in JavaScript


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      In the early days of the Web, websites consisted primarily of HTML and CSS. If any JavaScript loaded into a page at all, it was usually in the form of small snippets that provided effects and interactivity. As a result, JavaScript programs were often written entirely in one file and loaded into a script tag. A developer could break the JavaScript up into multiple files, but all variables and functions would still be added to the global scope.

      But as websites have evolved with the advent of frameworks like Angular, React, and Vue, and with companies creating advanced web applications instead of desktop applications, JavaScript now plays a major role in the browser. As a result, there is a much greater need to use third-party code for common tasks, to break up code into modular files, and to avoid polluting the global namespace.

      The ECMAScript 2015 specification introduced modules to the JavaScript language, which allowed for the use of import and export statements. In this tutorial, you will learn what a JavaScript module is and how to use import and export to organize your code.

      Modular Programming

      Before the concept of modules appeared in JavaScript, when a developer wanted to organize their code into segments, they would create multiple files and link to them as separate scripts. To demonstrate this, create an example index.html file and two JavaScript files, functions.js and script.js.

      The index.html file will display the sum, difference, product, and quotient of two numbers, and link to the two JavaScript files in script tags. Open index.html in a text editor and add the following code:

      index.html

      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="utf-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      
          <title>JavaScript Modules</title>
        </head>
      
        <body>
          <h1>Answers</h1>
          <h2><strong id="x"></strong> and <strong id="y"></strong></h2>
      
          <h3>Addition</h3>
          <p id="addition"></p>
      
          <h3>Subtraction</h3>
          <p id="subtraction"></p>
      
          <h3>Multiplication</h3>
          <p id="multiplication"></p>
      
          <h3>Division</h3>
          <p id="division"></p>
      
          <script src="https://www.digitalocean.com/community/tutorials/functions.js"></script>
          <script src="https://www.digitalocean.com/community/tutorials/script.js"></script>
        </body>
      </html>
      

      This HTML will display the value of variables x and y in an h2 header, and the value of operations on those variables in the following p elements. The id attributes of the elements are set for DOM manipulation, which will happen in the script.js file; this file will also set the values of x and y. For more information on HTML, check out our How To Build a Website with HTML series.

      The functions.js file will contain the mathematical functions that will be used in the second script. Open the functions.js file and add the following:

      functions.js

      function sum(x, y) {
        return x + y
      }
      
      function difference(x, y) {
        return x - y
      }
      
      function product(x, y) {
        return x * y
      }
      
      function quotient(x, y) {
        return x / y
      }
      

      Finally, the script.js file will determine the values of x and y, apply the functions to them, and display the result:

      script.js

      
      const x = 10
      const y = 5
      
      document.getElementById('x').textContent = x
      document.getElementById('y').textContent = y
      
      document.getElementById('addition').textContent = sum(x, y)
      document.getElementById('subtraction').textContent = difference(x, y)
      document.getElementById('multiplication').textContent = product(x, y)
      document.getElementById('division').textContent = quotient(x, y)
      

      After setting up these files and saving them, you can open index.html in a browser to display your website with all the results:

      Rendered HTML with the values 10 and 5 and the results of the functions.js operations.

      For websites with a few small scripts, this is an effective way to divide the code. However, there are some issues associated with this approach, including:

      • Polluting the global namespace: All the variables you created in your scripts—sum, difference, etc.—now exist on the window object. If you attempted to use another variable called sum in another file, it would become difficult to know which value would be used at any point in the scripts, since they would all be using the same window.sum variable. The only way a variable could be private was by putting it within a function scope. There could even be a conflict between an id in the DOM named x and var x.
      • Dependency management: Scripts would have to be loaded in order from top to bottom to ensure the correct variables were available. Saving the scripts as different files gives the illusion of separation, but it is essentially the same as having a single inline <script> in the browser page.

      Before ES6 added native modules to the JavaScript language, the community attempted to come up with several solutions. The first solutions were written in vanilla JavaScript, such as writing all code in objects or immediately invoked function expressions (IIFEs) and placing them on a single object in the global namespace. This was an improvement on the multiple script approach, but still had the same problems of putting at least one object in the global namespace, and did not make the problem of consistently sharing code between third parties any easier.

      After that, a few module solutions emerged: CommonJS, a synchronous approach that was implemented in Node.js, Asynchronous Module Definition (AMD), which was an asynchronous approach, and Universal Module Definition (UMD), which was intended to be a universal approach that supported both previous styles.

      The advent of these solutions made it easier for developers to share and reuse code in the form of packages, modules that can be distributed and shared, such as the ones found on npm. However, since there were many solutions and none were native to JavaScript, tools like Babel, Webpack, or Browserify had to be implemented to use modules in browsers.

      Due to the many problems with the multiple file approach and the complexity of the solutions proposed, developers were interested in bringing the modular programming approach to the JavaScript language. Because of this, ECMAScript 2015 supports the use of JavaScript modules.

      A module is a bundle of code that acts as an interface to provide functionality for other modules to use, as well as being able to rely on the functionality of other modules. A module exports to provide code and imports to use other code. Modules are useful because they allow developers to reuse code, they provide a stable, consistent interface that many developers can use, and they do not pollute the global namespace.

      Modules (sometimes referred to as ECMAScript modules or ES Modules) are now available natively in JavaScript, and in the rest of this tutorial you will explore how to use and implement them in your code.

      Native JavaScript Modules

      Modules in JavaScript use the import and export keywords:

      • import: Used to read code exported from another module.
      • export: Used to provide code to other modules.

      To demonstrate how to use this, update your functions.js file to be a module and export the functions. You will add export in front of each function, which will make them available to any other module.

      Add the following highlighted code to your file:

      functions.js

      export function sum(x, y) {
        return x + y
      }
      
      export function difference(x, y) {
        return x - y
      }
      
      export function product(x, y) {
        return x * y
      }
      
      export function quotient(x, y) {
        return x / y
      }
      

      Now, in script.js, you will use import to retrieve the code from the functions.js module at the top of the file.

      Note: import must always be at the top of the file before any other code, and it is also necessary to include the relative path (./ in this case).

      Add the following highlighted code to script.js:

      script.js

      
      import { sum, difference, product, quotient } from './functions.js'
      
      const x = 10
      const y = 5
      
      document.getElementById('x').textContent = x
      document.getElementById('y').textContent = y
      
      document.getElementById('addition').textContent = sum(x, y)
      document.getElementById('subtraction').textContent = difference(x, y)
      document.getElementById('multiplication').textContent = product(x, y)
      document.getElementById('division').textContent = quotient(x, y)
      

      Notice that individual functions are imported by naming them in curly braces.

      In order to ensure this code gets loaded as a module and not a regular script, add type="module" to the script tags in index.html. Any code that uses import or export must use this attribute:

      index.html

      ...
      <script type="module" src="https://www.digitalocean.com/community/tutorials/functions.js"></script>
      <script type="module" src="https://www.digitalocean.com/community/tutorials/script.js"></script>
      

      At this point, you will be able to reload the page with the updates and the website will now use modules. Browser support is very high, but caniuse is available to check which browsers support it. Note that if you are viewing the file as a direct link to a local file, you will encounter this error:

      Output

      Access to script at 'file:///Users/your_file_path/script.js' from origin 'null' has been blocked by CORS policy: Cross-origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.

      Because of the CORS policy, Modules must be used in a server environment, which you can set up locally with http-server or on the internet with a hosting provider.

      Modules are different from regular scripts in a few ways:

      • Modules do not add anything to the global (window) scope.
      • Modules always are in strict mode.
      • Loading the same module twice in the same file will have no effect, as modules are only executed once.
      • Modules require a server environment.

      Modules are still often used alongside bundlers like Webpack for increased browser support and additional features, but they are also available for use directly in browsers.

      Next, you will explore some more ways in which the import and export syntax can be used.

      Named Exports

      As demonstrated earlier, using the export syntax will allow you to individually import values that have been exported by their name. For example, take this simplified version of functions.js:

      functions.js

      export function sum() {}
      export function difference() {}
      

      This would let you import sum and difference by name using curly braces:

      script.js

      import { sum, difference } from './functions.js'
      

      It is also possible to use an alias to rename the function. You might do this to avoid naming conflicts within the same module. In this example, sum will be renamed to add and difference will be renamed to subtract.

      script.js

      import {
        sum as add,
        difference as subtract
      } from './functions.js'
      
      add(1, 2) // 3
      

      Calling add() here will yield the result of the sum() function.

      Using the * syntax, you can import the contents of the entire module into one object. In this case, sum and difference will become methods on the mathFunctions object.

      script.js

      import * as mathFunctions from './functions.js'
      
      mathFunctions.sum(1, 2) // 3
      mathFunctions.difference(10, 3) // 7
      

      Primitive values, function expressions and definitions, asynchronous functions, classes, and instantiated classes can all be exported, as long as they have an identifier:

      // Primitive values
      export const number = 100
      export const string = 'string'
      export const undef = undefined
      export const empty = null
      export const obj = { name: 'Homer' }
      export const array = ['Bart', 'Lisa', 'Maggie']
      
      // Function expression
      export const sum = (x, y) => x + y
      
      // Function definition
      export function difference(x, y) {
        return x - y
      }
      
      // Asynchronous function
      export async function getBooks() {}
      
      // Class
      export class Book {
        constructor(name, author) {
          this.name = name
          this.author = author
        }
      }
      
      // Instantiated class
      export const book = new Book('Lord of the Rings', 'J. R. R. Tolkien')
      

      All of these exports can be successfully imported. The other type of export that you will explore in the next section is known as a default export.

      Default Exports

      In the previous examples, you exported multiple named exports and imported them individually or as one object with each export as a method on the object. Modules can also contain a default export, using the default keyword. A default export will not be imported with curly brackets, but will be directly imported into a named identifier.

      For example, take the following contents for the functions.js file:

      functions.js

      export default function sum(x, y) {
        return x + y
      }
      

      In the script.js file, you could import the default function as sum with the following:

      script.js

      import sum from './functions.js'
      
      sum(1, 2) // 3
      

      This can be dangerous, as there are no restrictions on what you can name a default export during the import. In this example, the default function is imported as difference although it is actually the sum function:

      script.js

      import difference from './functions.js'
      
      difference(1, 2) // 3
      

      For this reason, it is often preferred to use named exports. Unlike named exports, default exports do not require an identifier—a primitive value by itself or anonymous function can be used as a default export. Following is an example of an object used as a default export:

      functions.js

      export default {
        name: 'Lord of the Rings',
        author: 'J. R. R. Tolkien',
      }
      

      You could import this as book with the following:

      script.js

      import book from './functions.js'
      

      Similarly, the following example demonstrates exporting an anonymous arrow function as the default export:

      functions.js

      export default () => 'This function is anonymous'
      

      This could be imported with the following script.js:

      script.js

      import anonymousFunction from './functions.js'
      

      Named exports and default exports can be used alongside each other, as in this module that exports two named values and a default value:

      functions.js

      export const length = 10
      export const width = 5
      
      export default function perimeter(x, y) {
        return 2 * (x + y)
      }
      

      You could import these variables and the default function with the following:

      script.js

      import calculatePerimeter, { length, width } from './functions.js'
      
      calculatePerimeter(length, width) // 30
      

      Now the default value and named values are both available to the script.

      Conclusion

      Modular programming design practices allow you to separate code into individual components that can help make your code reusable and consistent, while also protecting the global namespace. A module interface can be implemented in native JavaScript with the import and export keywords.

      In this article, you learned about the history of modules in JavaScript, how to separate JavaScript files into multiple top-level scripts, how to update those files using a modular approach, and the import and export syntax for named and default exports.

      To learn more about modules in JavaScript, read Modules on the Mozilla Developer Network. If you’d like to explore modules in Node.js, try our How To Create a Node.js Module tutorial.



      Source link

      Understanding the DOM — Document Object Model eBook


      Download the Complete eBook!

      Understanding the DOM — Document Object Model eBook in EPUB format

      Understanding the DOM — Document Object Model eBook in PDF format

      Introduction to the eBook

      JavaScript is the de facto programming language of the web, but the language itself does not include any built-in method for working with input/output (I/O), such as graphics display and sound. Instead, the web browser provides an API for accessing the HTML document in a tree structure known as the Document Object Model (DOM). The combination of JavaScript and the DOM is what allows us to create interactive, dynamic websites.

      Many modern frameworks, such as React, Vue, and Svelte abstract away much of the DOM from the developer, but frameworks also use the DOM under the hood. The JavaScript library jQuery was also created to make working with the DOM easier, but the modern development practice is to work with the DOM directly. In order to be a proficient web developer, having a deep understanding of what the DOM is and how to work with it is essential. The goal of this book is to provide a base understanding of the DOM, as well as explore examples of the most common and useful methods for interacting with the DOM.

      This book is based on the Understanding the DOM tutorial series found on the DigitalOcean Community. The topics that it covers include:

      • The DOM and DOM tree structure
      • How to access, traverse, and modify nodes and elements in the DOM
      • How to modify attributes, classes, and styles in the DOM
      • Use events to make interactive, dynamic websites

      Each chapter is self-contained and can be followed independently of the others. However, if you are not yet familiar with the concept of the DOM and DOM tree, it is recommended that you read the introductory chapters first.

      Download the eBook

      You can download the eBook in either the EPUB or PDF format by following the links below.

      Download the Complete eBook!

      Understanding the DOM — Document Object Model eBook in EPUB format

      Understanding the DOM — Document Object Model eBook in PDF format

      If you’d like to learn more about JavaScript, visit the DigitalOcean Community’s JavaScript section. You can follow along with the How to Code in JavaScript series for a directed learning experience.



      Source link

      Understanding Hoisting in JavaScript


      Introduction

      In this tutorial, we’ll investigate how the famed hoisting mechanism occurs in JavaScript. Before we dive in, let’s get to grips with what hoisting is.

      Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope before code execution.

      Inevitably, this means that no matter where functions and variables are declared, they are moved to the top of their scope regardless of whether their scope is global or local.

      Of note however, is the fact that the hoisting mechanism only moves the declaration. The assignments are left in place.

      If you’ve ever wondered why you were able to call functions before you wrote them in your code, then read on!

      undefined vs ReferenceError

      Before we begin in earnest, let’s deliberate on a few things.

      console.log(typeof variable); // Output: undefined
      

      This brings us to our first point of note:

      In JavaScript, an undeclared variable is assigned the value undefined at execution and is also of type undefined.

      Our second point is:

      console.log(variable); // Output: ReferenceError: variable is not defined
      

      In JavaScript, a ReferenceError is thrown when trying to access a previously undeclared variable.

      The behaviour of JavaScript when handling variables becomes nuanced because of hoisting. We’ll look at this in depth in subsequent sections.

      Hoisting variables

      The following is the JavaScript lifecycle and indicative of the sequence in which variable declaration and initialisation occurs.

      variable-hoisting

      However, since JavaScript allows us to both declare and initialise our variables simultaneously, this is the most used pattern:

      var a = 100;
      

      It is however important to remember that in the background, JavaScript is religiously declaring then initialising our variables.

      As we mentioned before, all variable and function declarations are hoisted to the top of their scope. I should also add that variable declarations are processed before any code is executed.

      However, in contrast, undeclared variables do not exist until code assigning them is executed.
      Therefore, assigning a value to an undeclared variable implicitly creates it as a global variable when the assignment is executed. This means that, all undeclared variables are global variables.

      To demonstrate this behaviour, have a look at the following:

      function hoist() {
        a = 20;
        var b = 100;
      }
      
      hoist();
      
      console.log(a); 
      /* 
      Accessible as a global variable outside hoist() function
      Output: 20
      */
      
      console.log(b); 
      /*
      Since it was declared, it is confined to the hoist() function scope.
      We can't print it out outside the confines of the hoist() function.
      Output: ReferenceError: b is not defined
      */
      

      Since this is one of the eccentricities of how JavaScript handles variables, it is recommended to always declare variables regardless of whether they are in a function or global scope. This clearly delineates how the interpreter should handle them at run time.

      ES5

      var

      The scope of a variable declared with the keyword var is its current execution context. This is either the enclosing function or for variables declared outside any function, global.
      Let’s look at a few examples to identify what this means:

      global variables

      console.log(hoist); // Output: undefined
      
      var hoist="The variable has been hoisted.";
      
      

      We expected the result of the log to be: ReferenceError: hoist is not defined, but instead, its output is undefined.

      Why has this happened?

      This discovery brings us closer to wrangling our prey.

      JavaScript has hoisted the variable declaration. This is what the code above looks like to the interpreter:

      var hoist;
      
      console.log(hoist); // Output: undefined
      hoist="The variable has been hoisted.";
      

      Because of this, we can use variables before we declare them. However, we have to be careful because the hoisted variable is initialised with a value of undefined.
      The best option would be to declare and initialise our variable before use.

      Function scoped variables

      As we’ve seen above, variables within a global scope are hoisted to the top of the scope. Next, let’s look at how function scoped variables are hoisted.

      function hoist() {
        console.log(message);
        var message="Hoisting is all the rage!"
      }
      
      hoist();
      

      Take an educated guess as to what our output might be.

      If you guessed, undefined you’re right. If you didn’t, worry not, we’ll soon get to the bottom of this.

      This is how the interpreter views the above code:

      function hoist() {
        var message;
        console.log(message);
        message="Hoisting is all the rage!"
      }
      
      hoist(); // Ouput: undefined
      

      The variable declaration, var message whose scope is the function hoist(), is hoisted to the top of the function.

      To avoid this pitfall, we would make sure to declare and initialise the variable before we use it:

      function hoist() {
        var message="Hoisting is all the rage!"
        return (message);
      }
      
      hoist(); // Ouput: Hoisting is all the rage!
      

      Strict Mode

      Thanks to a utility of the es5 version of JavaScript known as strict-mode, we can be more careful about how we declare our variables.
      By enabling strict mode, we opt into a restricted variant of JavaScript that will not tolerate the usage of variables before they are declared.

      Running our code in strict mode:

      1. Eliminates some silent JavaScript errors by changing them to explicit throw errors which will be spit out by the interpreter.
      2. Fixes mistakes that make it difficult for JavaScript engines to perform optimisations.
      3. Prohibits some syntax likely to be defined in future versions of JavaScript.

      We enable strict mode by prefacing our file or function with

      'use strict';
      
      // OR
      "use strict";
      

      Let’s test it out.

      'use strict';
      
      console.log(hoist); // Output: ReferenceError: hoist is not defined
      hoist="Hoisted"; 
      

      We can see that instead of assuming that we missed out on declaring our variable, use strict has stopped us in our tracks by explicitly throwing a Reference error. Try it out without use strict and see what happens.

      Strict mode behaves differently in different browsers however, so it’s advisable to perform feature testing thoroughly before relying on it in production.

      ES6

      ECMAScript 6, ECMAScript 2015 also known as ES6 is the latest version of the ECMAScript standard, as the writing of this article, Jan 2017 and introduces a few changes to es5.

      Of interest to us is how changes in the standard affect the declaration and initialisation of JavaScript variables.

      let

      Before we start, to be noted is the fact that variables declared with the keyword let are block scoped and not function scoped. That’s significant, but it shouldn’t trouble us here. Briefly, however, it just means that the variable’s scope is bound to the block in which it is declared and not the function in which it is declared.

      Let’s start by looking at the let keyword’s behaviour.

      console.log(hoist); // Output: ReferenceError: hoist is not defined ...
      let hoist="The variable has been hoisted.";
      

      Like before, for the var keyword, we expect the output of the log to be undefined. However, since the es6 let doesn’t take kindly on us using undeclared variables, the interpreter explicitly spits out a Reference error.

      This ensures that we always declare our variables first.

      However, we still have to be careful here. An implementation like the following will result in an ouput of undefined instead of a Reference error.

      let hoist;
      
      console.log(hoist); // Output: undefined
      hoist="Hoisted"
      

      Hence, to err on the side of caution, we should declare then assign our variables to a value before using them.

      const

      The const keyword was introduced in es6 to allow immutable variables. That is, variables whose value cannot be modified once assigned.

      With const, just as with let, the variable is hoisted to the top of the block.

      Let’s see what happens if we try to reassign the value attached to a const variable.

      const PI = 3.142;
      
      PI = 22/7; // Let's reassign the value of PI
      
      console.log(PI); // Output: TypeError: Assignment to constant variable.
      

      How does const alter variable declaration? Let’s take a look.

      console.log(hoist); // Output: ReferenceError: hoist is not defined
      const hoist="The variable has been hoisted.";
      

      Much like the let keyword, instead of silently exiting with an undefined, the interpreter saves us by explicitly throwing a Reference error.

      The same occurs when using const within functions.

      function getCircumference(radius) {
        console.log(circumference)
        circumference = PI*radius*2;
        const PI = 22/7;
      }
      
      getCircumference(2) // ReferenceError: circumference is not defined
      

      With const , es6 goes further. The interpreter throws an error if we use a constant before declaring and initialising it.

      Our linter is also quick to inform us of this felony:

      PI was used before it was declared, which is illegal for const variables.
      

      Globally,

      
      const PI;
      console.log(PI); // Ouput: SyntaxError: Missing initializer in const declaration
      PI=3.142;
      

      Therefore, a constant variable must be both declared and initialised before use.


      As a prologue to this section, it’s important to note that indeed, JavaScript hoists variables declared with es6 let and const. The difference in this case is how it initialises them.
      Variables declared with let and const remain uninitialised at the beginning of execution whilst variables declared with var are initialised with a value of undefined.

      Hoisting functions

      JavaScript functions can be loosely classified as the following:

      1. Function declarations
      2. Function expressions

      We’ll investigate how hoisting is affected by both function types.

      Function declarations

      These are of the following form and are hoisted completely to the top. Now, we can understand why JavaScript enable us to invoke a function seemingly before declaring it.

      hoisted(); // Output: "This function has been hoisted."
      
      function hoisted() {
        console.log('This function has been hoisted.');
      };
      

      Function expressions

      Function expressions, however are not hoisted.

      expression(); //Output: "TypeError: expression is not a function
      
      var expression = function() {
        console.log('Will this work?');
      };
      

      Let’s try the combination of a function declaration and expression.

      expression(); // Ouput: TypeError: expression is not a function
      
      var expression = function hoisting() {
        console.log('Will this work?');
      };
      

      As we can see above, the variable declaration var expression is hoisted but it’s assignment to a function is not. Therefore, the intepreter throws a TypeError since it sees expression as a variable and not a function.

      Order of precedence

      It’s important to keep a few things in mind when declaring JavaScript functions and variables.

      1. Variable assignment takes precedence over function declaration
      2. Function declarations take precedence over variable declarations

      Function declarations are hoisted over variable declarations but not over variable assignments.

      Let’s take a look at what implications this behaviour has.

      Variable assignment over function declaration

      var double = 22;
      
      function double(num) {
        return (num*2);
      }
      
      console.log(typeof double); // Output: number
      

      Function declarations over variable declarations

      var double;
      
      function double(num) {
        return (num*2);
      }
      
      console.log(typeof double); // Output: function
      

      Even if we reversed the position of the declarations, the JavaScript interpreter would still consider double a function.

      Hoisting classes

      JavaScript classes too can be loosely classified either as:

      1. Class declarations
      2. Class expressions

      Class declarations

      Much like their function counterparts, JavaScript class declarations are hoisted. However, they remain uninitialised until evaluation.
      This effectively means that you have to declare a class before you can use it.

      
      var Frodo = new Hobbit();
      Frodo.height = 100;
      Frodo.weight = 300;
      console.log(Frodo); // Output: ReferenceError: Hobbit is not defined
      
      class Hobbit {
        constructor(height, weight) {
          this.height = height;
          this.weight = weight;
        }
      }
      

      I’m sure you’ve noticed that instead of getting an undefined we get a Reference error. That evidence lends claim to our position that class declarations are hoisted.

      If you’re paying attention to your linter, it supplies us with a handy tip.

      Hobbit was used before it is declared, which is illegal for class variables
      

      So, as far as class declarations go, to access the class declaration, you have to declare first.

      class Hobbit {
        constructor(height, weight) {
          this.height = height;
          this.weight = weight;
        }
      }
      
      var Frodo = new Hobbit();
      Frodo.height = 100;
      Frodo.weight = 300;
      console.log(Frodo); // Output: { height: 100, weight: 300 }
      

      Class expressions

      Much like their function counterparts, class expressions are not hoisted.

      Here’s an example with the un-named or anonymous variant of the class expression.

      var Square = new Polygon();
      Square.height = 10;
      Square.width = 10;
      console.log(Square); // Output: TypeError: Polygon is not a constructor
      
      var Polygon = class {
        constructor(height, width) {
          this.height = height;
          this.width = width;
        }
      };
      

      Here’s an example with a named class expression.

      var Square = new Polygon();
      Square.height = 10;
      Square.width = 10;
      console.log(Square); // Output: TypeError: Polygon is not a constructor
      
      
      var Polygon = class Polygon {
        constructor(height, width) {
          this.height = height;
          this.width = width;
        }
      };
      
      

      The correct way to do it is like this:

      var Polygon = class Polygon {
        constructor(height, width) {
          this.height = height;
          this.width = width;
        }
      };
      
      var Square = new Polygon();
      Square.height = 10;
      Square.width = 10;
      console.log(Square);
      

      Caveat

      There’s a bit of an argument to be made as to whether Javascript es6 let, const variables and classes are actually hoisted, roughly hoisted or not hoisted. Some argue that they are actually hoisted but uninitialised whilst some argue that they are not hoisted at all.

      Conclusion

      Let’s summarise what we’ve learned so far:

      1. While using es5 var, trying to use undeclared variables will lead to the variable being assigned a value of undefined upon hoisting.
      2. While using es6 let and const, using undeclared variables will lead to a Reference Error because the variable remains uninitialised at execution.

      Therefore,

      1. We should make it a habit to declare and initialise JavaScript variables before use.
      2. Using strict mode in JavaScript es5 can help expose undeclared variables.

      I hope this article will serve as a good introduction to the concept of hoisting in JavaScript and spur your interest regarding the subtleties of the JavaScript language.



      Source link