One place for hosting & domains

      JavaScript

      Understanding This, Bind, Call, and Apply in JavaScript


      The author selected the Open Internet/Free Speech Fund to receive a donation as part of the Write for DOnations program.

      The this keyword is a very important concept in JavaScript, and also a particularly confusing one to both new developers and those who have experience in other programming languages. In JavaScript, this is a reference to an object. The object that this refers to can vary, implicitly based on whether it is global, on an object, or in a constructor, and can also vary explicitly based on usage of the Function prototype methods bind, call, and apply.

      Although this is a bit of a complex topic, it is also one that appears as soon as you begin writing your first JavaScript programs. Whether you’re trying to access an element or event in the Document Object Model (DOM), building classes for writing in the object-oriented programming style, or using the properties and methods of regular objects, you will encounter this.

      In this article, you’ll learn what this refers to implicitly based on context, and you’ll learn how to use the bind, call, and apply methods to explicitly determine the value of this.

      Implicit Context

      There are four main contexts in which the value of this can be implicitly inferred:

      • the global context
      • as a method within an object
      • as a constructor on a function or class
      • as a DOM event handler

      Global

      In the global context, this refers to the global object. When you’re working in a browser, the global context is would be window. When you’re working in Node.js, the global context is global.

      Note: If you are not yet familiar with the concept of scope in JavaScript, please review Understanding Variables, Scope, and Hoisting in JavaScript.

      For the examples, you will practice the code in the browser’s Developer Tools console. Read How to Use the JavaScript Developer Console if you are not familiar with running JavaScript code in the browser.

      If you log the value of this without any other code, you will see what object this refers to.

      console.log(this)
      

      Output

      Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

      You can see that this is window, which is the global object of a browser.

      In Understanding Variables, Scope, and Hoisting in JavaScript, you learned that functions have their own context for variables. You might be tempted to think that this would follow the same rules inside a function, but it does not. A top-level function will still retain the this reference of the global object.

      You write a top-level function, or a function that is not associated with any object, like this:

      function printThis() {
        console.log(this)
      }
      
      printThis()
      

      Output

      Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

      Even within a function, this still refers to the window, or global object.

      However, when using strict mode, the context of this within a function on the global context will be undefined.

      'use strict'
      
      function printThis() {
        console.log(this)
      }
      
      printThis()
      

      Output

      undefined

      Generally, it is safer to use strict mode to reduce the probability of this having an unexpected scope. Rarely will someone want to refer to the window object using this.

      For more information about strict mode and what changes it makes regarding mistakes and security, read the Strict mode documentation on MDN.

      An Object Method

      A method is a function on an object, or a task that an object can perform. A method uses this to refer to the properties of the object.

      const america = {
        name: 'The United States of America',
        yearFounded: 1776,
      
        describe() {
          console.log(`${this.name} was founded in ${this.yearFounded}.`)
        },
      }
      
      america.describe()
      

      Output

      "The United States of America was founded in 1776."

      In this example, this is the same as america.

      In a nested object, this refers to the current object scope of the method. In the following example, this.symbol within the details object refers to details.symbol.

      const america = {
        name: 'The United States of America',
        yearFounded: 1776,
        details: {
          symbol: 'eagle',
          currency: 'USD',
          printDetails() {
            console.log(`The symbol is the ${this.symbol} and the currency is ${this.currency}.`)
          },
        },
      }
      
      america.details.printDetails()
      

      Output

      "The symbol is the eagle and the currency is USD."

      Another way of thinking about it is that this refers to the object on the left side of the dot when calling a method.

      A Function Constructor

      When you use the new keyword, it creates an instance of a constructor function or class. Function constructors were the standard way to initialize a user-defined object before the class syntax was introduced in the ECMAScript 2015 update to JavaScript. In Understanding Classes in JavaScript, you will learn how to create a function constructor and an equivalent class constructor.

      function Country(name, yearFounded) {
        this.name = name
        this.yearFounded = yearFounded
      
        this.describe = function() {
          console.log(`${this.name} was founded in ${this.yearFounded}.`)
        }
      }
      
      const america = new Country('The United States of America', 1776)
      
      america.describe()
      

      Output

      "The United States of America was founded in 1776."

      In this context, this is now bound to the instance of Country, which is contained in the america constant.

      A Class Constructor

      A constructor on a class acts the same as a constructor on a function. Read more about the similarities and differences between function constructors and ES6 classes in Understanding Classes in JavaScript.

      class Country {
        constructor(name, yearFounded) {
          this.name = name
          this.yearFounded = yearFounded
        }
      
        describe() {
          console.log(`${this.name} was founded in ${this.yearFounded}.`)
        }
      }
      
      const america = new Country('The United States of America', 1776)
      
      america.describe()
      

      this in the describe method refers to the instance of Country, which is america.

      Output

      "The United States of America was founded in 1776."

      A DOM Event Handler

      In the browser, there is a special this context for event handlers. In an event handler called by addEventListener, this will refer to event.currentTarget. More often than not, developers will simply use event.target or event.currentTarget as needed to access elements in the DOM, but since the this reference changes in this context, it is important to know.

      In the following example, we’ll create a button, add text to it, and append it to the DOM. When we log the value of this within the event handler, it will print the target.

      const button = document.createElement('button')
      button.textContent = 'Click me'
      document.body.append(button)
      
      button.addEventListener('click', function(event) {
        console.log(this)
      })
      

      Output

      <button>Click me</button>

      Once you paste this into your browser, you will see a button appended to the page that says “Click me”. If you click the button, you will see <button>Click me</button> appear in your console, as clicking the button logs the element, which is the button itself. Therefore, as you can see, this refers to the targeted element, which is the element we added an event listener to.

      Explicit Context

      In all of the previous examples, the value of this was determined by its context—whether it is global, in an object, in a constructed function or class, or on a DOM event handler. However, using call, apply, or bind, you can explicitly determine what this should refer to.

      It is difficult to define exactly when to use call, apply, or bind, as it will depend on the context of your program. bind can be particularly helpful when you want to use events to access properties of one class within another class. For example, if you were to write a simple game, you might separate the user interface and I/O into one class, and the game logic and state into another. Since the game logic would need to access input, such as key press and click, you would want to bind the events to access the this value of the game logic class.

      The important part is to know how to determine what object this refers to, which you can do implicitly with what you learned in the previous sections, or explicitly with the three methods you will learn next.

      Call and Apply

      call and apply are very similar—they invoke a function with a specified this context, and optional arguments. The only difference between call and apply is that call requires the arguments to be passed in one-by-one, and apply takes the arguments as an array.

      In this example, we’ll create an object, and create a function that references this but has no this context.

      const book = {
        title: 'Brave New World',
        author: 'Aldous Huxley',
      }
      
      function summary() {
        console.log(`${this.title} was written by ${this.author}.`)
      }
      
      summary()
      

      Output

      "undefined was written by undefined"

      Since summary and book have no connection, invoking summary by itself will only print undefined, as it’s looking for those properties on the global object.

      Note: Attempting this in strict mode would result in Uncaught TypeError: Cannot read property 'title' of undefined, as this itself would be undefined.

      However, you can use call and apply to invoke the this context of book on the function.

      summary.call(book)
      // or:
      summary.apply(book)
      

      Output

      "Brave New World was written by Aldous Huxley."

      There is now a connection between book and summary when these methods are applied. Let’s confirm exactly what this is.

      function printThis() {
        console.log(this)
      }
      
      printThis.call(book)
      // or:
      whatIsThis.apply(book)
      

      Output

      {title: "Brave New World", author: "Aldous Huxley"}

      In this case, this actually becomes the object passed as an argument.

      This is how call and apply are the same, but there is one small difference. In addition to being able to pass the this context as the first argument, you can also pass additional arguments through.

      function longerSummary(genre, year) {
        console.log(
          `${this.title} was written by ${this.author}. It is a ${genre} novel written in ${year}.`
        )
      }
      

      With call each additional value you want to pass is sent as an additional argument.

      longerSummary.call(book, 'dystopian', 1932)
      

      Output

      "Brave New World was written by Aldous Huxley. It is a dystopian novel written in 1932."

      If you try to send the exact same arguments with apply, this is what happens:

      longerSummary.apply(book, 'dystopian', 1932)
      

      Output

      Uncaught TypeError: CreateListFromArrayLike called on non-object at <anonymous>:1:15

      Instead, for apply, you have to pass all the arguments in an array.

      longerSummary.apply(book, ['dystopian', 1932])
      

      Output

      "Brave New World was written by Aldous Huxley. It is a dystopian novel written in 1932."

      The difference between passing the arguments individually or in an array is subtle, but it’s important to be aware of. It might be simpler and more convenient to use apply, as it would not require changing the function call if some parameter details changed.

      Bind

      Both call and apply are one-time use methods—if you call the method with the this context it will have it, but the original function will remain unchanged.

      Sometimes, you might need to use a method over and over with the this context of another object, and in that case you could use the bind method to create a brand new function with an explicitly bound this.

      const braveNewWorldSummary = summary.bind(book)
      
      braveNewWorldSummary()
      

      Output

      "Brave New World was written by Aldous Huxley"

      In this example, every time you call braveNewWorldSummary, it will always return the original this value bound to it. Attempting to bind a new this context to it will fail, so you can always trust a bound function to return the this value you expect.

      const braveNewWorldSummary = summary.bind(book)
      
      braveNewWorldSummary() // Brave New World was written by Aldous Huxley.
      
      const book2 = {
        title: '1984',
        author: 'George Orwell',
      }
      
      braveNewWorldSummary.bind(book2)
      
      braveNewWorldSummary() // Brave New World was written by Aldous Huxley.
      

      Although this example tries to bind braveNewWorldSummary once again, it retains the original this context from the first time it was bound.

      Arrow Functions

      Arrow functions do not have their own this binding. Instead, they go up to the next level of execution.

      const whoAmI = {
        name: 'Leslie Knope',
        regularFunction: function() {
          console.log(this.name)
        },
        arrowFunction: () => {
          console.log(this.name)
        },
      }
      
      whoAmI.regularFunction() // "Leslie Knope"
      whoAmI.arrowFunction() // undefined
      

      It can be useful to use the arrow function in cases where you really want this to refer to the outer context. For example, if you had an event listener inside of a class, you would probably want this to refer to some value in the class.

      In this example, you’ll create and append button to the DOM like before, but the class will have an event listener that will change the text value of the button when clicked.

      const button = document.createElement('button')
      button.textContent = 'Click me'
      document.body.append(button)
      
      class Display {
        constructor() {
          this.buttonText = 'New text'
      
          button.addEventListener('click', event => {
            event.target.textContent = this.buttonText
          })
        }
      }
      
      new Display()
      

      If you click the button, the text content will change to the value of buttonText. If you hadn’t used an arrow function here, this would be equal to event.currentTarget, and you wouldn’t be able to use it to access a value within the class without explicitly binding it. This tactic is often used on class methods in frameworks like React.

      Conclusion

      In this article, you learned about this in JavaScript, and the many different values it might have based on implicit runtime binding, and explicit binding through bind, call, and apply. You also learned about how the lack of this binding in arrow functions can be used to refer to a different context. With this knowledge, you should be able to determine the value of this in your programs.



      Source link

      How To Use Object Methods in JavaScript


      Introduction

      Objects in JavaScript are collections of key/value pairs. The values can consist of properties and methods, and may contain all other JavaScript data types, such as strings, numbers, and Booleans.

      All objects in JavaScript descend from the parent Object constructor. Object has many useful built-in methods we can use and access to make working with individual objects straightforward. Unlike Array prototype methods like sort() and reverse() that are used on the array instance, Object methods are used directly on the Object constructor, and use the object instance as a parameter. This is known as a static method.

      This tutorial will go over important built-in object methods, with each section below dealing with a specific method and providing an example of use.

      Prerequisites

      In order to get the most out of this tutorial, you should be familiar with creating, modifying, and working with objects, which you can review in the “Understanding Objects in JavaScript” article.

      For additional guidance on JavaScript in general, you can review our How To Code in JavaScript series.

      Object.create()

      The Object.create() method is used to create a new object and link it to the prototype of an existing object.

      We can create a job object instance, and extend it to a more specific object.

      // Initialize an object with properties and methods
      const job = {
          position: 'cashier',
          type: 'hourly',
          isAvailable: true,
          showDetails() {
              const accepting = this.isAvailable ? 'is accepting applications' : "is not currently accepting applications";
      
              console.log(`The ${this.position} position is ${this.type} and ${accepting}.`);
          }
      };
      
      // Use Object.create to pass properties
      const barista = Object.create(job);
      
      barista.position = "barista";
      barista.showDetails();
      

      Output

      The barista position is hourly and is accepting applications.

      The barista object now has one property — position — but all the other properties and methods from job are available through the prototype. Object.create() is useful for keeping code DRY by minimizing duplication.

      Object.keys()

      Object.keys() creates an array containing the keys of an object.

      We can create an object and print the array of keys.

      // Initialize an object
      const employees = {
          boss: 'Michael',
          secretary: 'Pam',
          sales: 'Jim',
          accountant: 'Oscar'
      };
      
      // Get the keys of the object
      const keys = Object.keys(employees);
      
      console.log(keys);
      

      Output

      ["boss", "secretary", "sales", "accountant"]

      Object.keys can be used to iterate through the keys and values of an object.

      // Iterate through the keys
      Object.keys(employees).forEach(key => {
          let value = employees[key];
      
           console.log(`${key}: ${value}`);
      });
      

      Output

      boss: Michael secretary: Pam sales: Jim accountant: Oscar

      Object.keys is also useful for checking the length of an object.

      // Get the length of the keys
      const length = Object.keys(employees).length;
      
      console.log(length);
      

      Output

      4

      Using the length property, we were able to count the 4 properties of employees.

      Object.values()

      Object.values() creates an array containing the values of an object.

      // Initialize an object
      const session = {
          id: 1,
          time: `26-July-2018`,
          device: 'mobile',
          browser: 'Chrome'
      };
      
      // Get all values of the object
      const values = Object.values(session);
      
      console.log(values);
      

      Output

      [1, "26-July-2018", "mobile", "Chrome"]

      Object.keys() and Object.values() allow you to return the data from an object.

      Object.entries()

      Object.entries() creates a nested array of the key/value pairs of an object.

      // Initialize an object
      const operatingSystem = {
          name: 'Ubuntu',
          version: 18.04,
          license: 'Open Source'
      };
      
      // Get the object key/value pairs
      const entries = Object.entries(operatingSystem);
      
      console.log(entries);
      

      Output

      [ ["name", "Ubuntu"] ["version", 18.04] ["license", "Open Source"] ]

      Once we have the key/value pair arrays, we can use the forEach() method to loop through and work with the results.

      // Loop through the results
      entries.forEach(entry => {
          let key = entry[0];
          let value = entry[1];
      
          console.log(`${key}: ${value}`);
      });
      

      Output

      name: Ubuntu version: 18.04 license: Open Source

      The Object.entries() method will only return the object instance’s own properties, and not any properties that may be inherited through its prototype.

      Object.assign()

      Object.assign() is used to copy values from one object to another.

      We can create two objects, and merge them with Object.assign().

      // Initialize an object
      const name = {
          firstName: 'Philip',
          lastName: 'Fry'
      };
      
      // Initialize another object
      const details = {
          job: 'Delivery Boy',
          employer: 'Planet Express'
      };
      
      // Merge the objects
      const character = Object.assign(name, details);
      
      console.log(character);
      

      Output

      {firstName: "Philip", lastName: "Fry", job: "Delivery Boy", employer: "Planet Express"}

      It is also possible to use the spread operator (...) to accomplish the same task. In the code below, we’ll modify how we declare character through merging the name and details objects.

      // Initialize an object
      const name = {
          firstName: 'Philip',
          lastName: 'Fry'
      };
      
      // Initialize another object
      const details = {
          job: 'Delivery Boy',
          employer: 'Planet Express'
      };
      
      // Merge the object with the spread operator
      const character = {...name, ...details}
      
      console.log(character);
      

      Output

      {firstName: "Philip", lastName: "Fry", job: "Delivery Boy", employer: "Planet Express"}

      This spread syntax in object literals is also known as shallow-cloning.

      Object.freeze()

      Object.freeze() prevents modification to properties and values of an object, and prevents properties from being added or removed from an object.

      // Initialize an object
      const user = {
          username: 'AzureDiamond',
          password: 'hunter2'
      };
      
      // Freeze the object
      const newUser = Object.freeze(user);
      
      newUser.password = '*******';
      newUser.active = true;
      
      console.log(newUser);
      

      Output

      {username: "AzureDiamond", password: "hunter2"}

      In the example above, we tried to override the password hunter2 with *******, but the password property remained the same. We also tried to add a new property, active, but it was not added.

      Object.isFrozen() is available to determine whether an object has been frozen or not, and returns a Boolean.

      Object.seal()

      Object.seal() prevents new properties from being added to an object, but allows the modification of existing properties. This method is similar to Object.freeze(). Refresh your console before implementing the code below to avoid an error.

      // Initialize an object
      const user = {
          username: 'AzureDiamond',
          password: 'hunter2'
      };
      
      // Seal the object
      const newUser = Object.seal(user);
      
      newUser.password = '*******';
      newUser.active = true;
      
      console.log(newUser);
      

      Output

      {username: "AzureDiamond", password: "*******"}

      The new active property was not added to the sealed object, but the password property was successfully changed.

      Object.getPrototypeOf()

      Object.getPrototypeOf() is used to get the internal hidden [[Prototype]] of an object, also accessible through the __proto__ property.

      In this example, we can create an array, which has access to the Array prototype.

      const employees = ['Ron', 'April', 'Andy', 'Leslie'];
      
      Object.getPrototypeOf(employees);
      

      Output

      [constructor: ƒ, concat: ƒ, find: ƒ, findIndex: ƒ, pop: ƒ, …]

      We can see in the output that the prototype of the employees array has access to pop, find, and other Array prototype methods. We can confirm this by testing the employees prototype against Array.prototype.

      Object.getPrototypeOf(employees) === Array.prototype;
      

      Output

      true

      This method can be useful to get more information about an object or ensure it has access to the prototype of another object.

      There is also a related Object.setPrototypeOf() method that will add one prototype to another object. It is recommended that you use Object.create() instead as it is faster and more performant.

      Conclusion

      Objects have many useful methods that help us modify, protect, and iterate through them. In this tutorial, we reviewed how to create and assign new objects, iterate through the keys and/or values of an object, and freeze or seal an object.

      If you need to review JavaScript objects you can read “Understanding Objects in JavaScript.” If you would like to familiarize yourself with the prototype chain, you can take a look at “Understanding Prototypes and Inheritance in JavaScript.”



      Source link