One place for hosting & domains

      Archives hassan latif

      Understanding Pointers in Go


      Introduction

      When you write software in Go you’ll be writing functions and methods. You pass data to these functions as arguments. Sometimes, the function needs a local copy of the data, and you want the original to remain unchanged. For example, if you’re a bank, and you have a function that shows the user the changes to their balance depending on the savings plan they choose, you don’t want to change the customer’s actual balance before they choose a plan; you just want to use it in calculations. This is called passing by value, because you’re sending the value of the variable to the function, but not the variable itself.

      Other times, you may want the function to be able to alter the data in the original variable. For instance, when the bank customer makes a deposit to their account, you want the deposit function to be able to access the actual balance, not a copy. In this case, you don’t need to send the actual data to the function; you just need to tell the function where the data is located in memory. A data type called a pointer holds the memory address of the data, but not the data itself. The memory address tells the function where to find the data, but not the value of the data. You can pass the pointer to the function instead of the data, and the function can then alter the original variable in place. This is called passing by reference, because the value of the variable isn’t passed to the function, just its location.

      In this article, you will create and use pointers to share access to the memory space for a variable.

      Defining and Using Pointers

      When you use a pointer to a variable, there are a couple of different syntax elements that you need to understand. The first one is the use of the ampersand (&). If you place an ampersand in front of a variable name, you are stating that you want to get the address, or a pointer to that variable. The second syntax element is the use of the asterisk (*) or dereferencing operator. When you declare a pointer variable, you follow the variable name with the type of the variable that the pointer points to, prefixed with an *, like this:

      var myPointer *int32 = &someint
      

      This creates myPointer as a pointer to an int32 variable, and initializes the pointer with the address of someint. The pointer doesn’t actually contain an int32, just the address of one.

      Let’s take a look at a pointer to a string. The following code declares both a value of a string, and a pointer to a string:

      main.go

      package main
      
      import "fmt"
      
      func main() {
          var creature string = "shark"
          var pointer *string = &creature
      
          fmt.Println("creature =", creature)
          fmt.Println("pointer =", pointer)
      }
      
      

      Run the program with the following command:

      When you run the program, it will print out the value of the variable, as well as the address of where the variable is stored (the pointer address). The memory address is a hexadecimal number, and not meant to be human-readable. In practice, you’ll probably never output a memory address to look at it. We’re showing you for illustrative purposes. Because each program is created in its own memory space when it is run, the value of the pointer will be different each time you run it, and will be different than the output shown here:

      Output

      creature = shark pointer = 0xc0000721e0

      The first variable we defined we named creature, and set it equal to a string with the value of shark. We then created another variable named pointer. This time, we set the value of the pointer variable to the address of the creature variable. We store the address of a value in a variable by using the ampersand (&) symbol. This means that the pointer variable is storing the address of the creature variable, not the actual value.

      This is why when we printed out the value of pointer, we received the value of 0xc0000721e0, which is the address of where the creature variable is currently stored in computer memory.

      If you want to print out the value of the variable being pointed at from the pointer variable, you need to dereference that variable. The following code uses the * operator to dereference the pointer variable and retrieve its value:

      main.go

      
      package main
      
      import "fmt"
      
      func main() {
          var creature string = "shark"
          var pointer *string = &creature
      
          fmt.Println("creature =", creature)
          fmt.Println("pointer =", pointer)
      
          fmt.Println("*pointer =", *pointer)
      }
      

      If you run this code, you’ll see the following output:

      Output

      creature = shark pointer = 0xc000010200 *pointer = shark

      The last line we added now dereferences then pointer variable, and prints out the value that is stored at that address.

      If you want to modify the value stored at the pointer variable’s location, you can use the dereference operator as well:

      main.go

      package main
      
      import "fmt"
      
      func main() {
          var creature string = "shark"
          var pointer *string = &creature
      
          fmt.Println("creature =", creature)
          fmt.Println("pointer =", pointer)
      
          fmt.Println("*pointer =", *pointer)
      
          *pointer = "jellyfish"
          fmt.Println("*pointer =", *pointer)
      }
      
      

      Run this code to see the output:

      Output

      creature = shark pointer = 0xc000094040 *pointer = shark *pointer = jellyfish

      We set the value the pointer variable refers to by using the asterisk (*) in front of the variable name, and then providing a new value of jellyfish. As you can see, when we print the dereferenced value, it is now set to jellyfish.

      You may not have realized it, but we actually changed the value of the creature variable as well. This is because the pointer variable is actually pointing at the creature variable’s address. This means that if we change the value pointed at from the pointer variable, we also change the value of the creature variable.

      main.go

      package main
      
      import "fmt"
      
      func main() {
          var creature string = "shark"
          var pointer *string = &creature
      
          fmt.Println("creature =", creature)
          fmt.Println("pointer =", pointer)
      
          fmt.Println("*pointer =", *pointer)
      
          *pointer = "jellyfish"
          fmt.Println("*pointer =", *pointer)
      
          fmt.Println("creature =", creature)
      }
      

      The output looks like this:

      Output

      creature = shark pointer = 0xc000010200 *pointer = shark *pointer = jellyfish creature = jellyfish

      Although this code illustrates how a pointer works, this is not the typical way in which you would use pointers in Go. It is more common to use them when defining function arguments and return values, or using them when defining methods on custom types. Let’s look at how you would use pointers with functions to share access to a variable.

      Again, keep in mind that we are printing the value of pointer to illustrate that it is a pointer. In practice, you wouldn’t use the value of a pointer, other than to reference the underlying value to retrieve or update that value.

      Function Pointer Receivers

      When you write a function, you can define arguments to be passed ether by value, or by reference. Passing by value means that a copy of that value is sent to the function, and any changes to that argument within that function only effect that variable within that function, and not where it was passed from. However, if you pass by reference, meaning you pass a pointer to that argument, you can change the value from within the function, and also change the value of the original variable that was passed in. You can read more about how to define functions in our How To Define and Call Functions in Go.

      Deciding when to pass a pointer as opposed when to send a value is all about knowing if you want the value to change or not. If you don’t want the value to change, send it as a value. If you want the function you are passing your variable to be able to change it, then you would pass it as a pointer.

      To see the difference, let’s first look at a function that is passing in an argument by value:

      main.go

      package main
      
      import "fmt"
      
      type Creature struct {
          Species string
      }
      
      func main() {
          var creature Creature = Creature{Species: "shark"}
      
          fmt.Printf("1) %+vn", creature)
          changeCreature(creature)
          fmt.Printf("3) %+vn", creature)
      }
      
      func changeCreature(creature Creature) {
          creature.Species = "jellyfish"
          fmt.Printf("2) %+vn", creature)
      }
      
      

      The output looks like this:

      Output

      1) {Species:shark} 2) {Species:jellyfish} 3) {Species:shark}

      First we created a custom type named Creature. It has one field named Species, which is a string. In the main function, we created an instance of our new type named creature and set the Species field to shark. We then printed out the variable to show the current value stored within the creature variable.

      Next, we call changeCreature and pass in a copy of the creature variable.

      The function changeCreature is defined as taking one argument named creature, and it is of type Creature that we defined earlier. We then change the value of the Species field to jellyfish and print it out. Notice that within the changeCreature function, the value of Species is now jellyfish, and it prints out 2) {Species:jellyfish}. This is because we are allowed to change the value within our function scope.

      However, when the last line of the main function prints the value of creature, the value of Species is still shark. The reason that the value didn’t change is because we passed the variable by value. This means that a copy of the value was created in memory, and passed to the changeCreature function. This allows us to have a function that can make changes to any arguments passed in as needed, but will not affect any of those variables outside of the function.

      Next, let’s change the changeCreature function to take an argument by reference. We can do this by changing the type from creature to a pointer by using the asterisk (*) operator. Instead of passing a creature, we’re now passing a pointer to a creature, or a *creature. In the previous example, creature is a struct that has a Species value of shark. *creature is a pointer, not a struct, so its value is a memory location, and that’s what we pass to changeCreature().

      main.go

      package main
      
      import "fmt"
      
      type Creature struct {
          Species string
      }
      
      func main() {
          var creature Creature = Creature{Species: "shark"}
      
          fmt.Printf("1) %+vn", creature)
          changeCreature(&creature)
          fmt.Printf("3) %+vn", creature)
      }
      
      func changeCreature(creature *Creature) {
          creature.Species = "jellyfish"
          fmt.Printf("2) %+vn", creature)
      }
      

      Run this code to see the following output:

      Output

      1) {Species:shark} 2) &{Species:jellyfish} 3) {Species:jellyfish}

      Notice that now when we change the value of Species to jellyfish in the changeCreature function, it changes the original value defined in the main function as well. This is because we passed the creature variable by reference, which allows access to the original value and can change it as needed.

      Therefore, if you want a function to be able to change a value, you need to pass it by reference. To pass by reference, you pass the pointer to the variable, and not the variable itself.

      However, sometimes you may not have an actual value defined for a pointer. In those cases, it is possible to have a panic in the program. Let’s look at how that happens and how to plan for that potential problem.

      Nil Pointers

      All variables in Go have a zero value. This is true even for a pointer. If you declare a pointer to a type, but assign no value, the zero value will be nil. nil is a way to say that nothing has been initialized for the variable.

      In the following program, we are defining a pointer to a Creature type, but we are never instantiating that actual instance of a Creature and assigning the address of it to the creature pointer variable. The value will be nil and we can’t reference any of the fields or methods that would be defined on the Creature type:

      main.go

      package main
      
      import "fmt"
      
      type Creature struct {
          Species string
      }
      
      func main() {
          var creature *Creature
      
          fmt.Printf("1) %+vn", creature)
          changeCreature(creature)
          fmt.Printf("3) %+vn", creature)
      }
      
      func changeCreature(creature *Creature) {
          creature.Species = "jellyfish"
          fmt.Printf("2) %+vn", creature)
      }
      

      The output looks like this:

      Output

      1) <nil> panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x109ac86] goroutine 1 [running]: main.changeCreature(0x0) /Users/corylanou/projects/learn/src/github.com/gopherguides/learn/_training/digital-ocean/pointers/src/nil.go:18 +0x26 main.main() /Users/corylanou/projects/learn/src/github.com/gopherguides/learn/_training/digital-ocean/pointers/src/nil.go:13 +0x98 exit status 2

      When we run the program, it printed out the value of the creature variable, and the value is <nil>. We then call the changeCreature function, and when that function tries to set the value of the Species field, it panics. This is because there is no instance of the variable actually created. Because of this, the program has no where to actually store the value, so the program panics.

      It is common in Go that if you are receiving an argument as a pointer, you check to see if it was nil or not before performing any operations on it to prevent the program from panicking.

      This is a common approach for checking for nil:

      if someVariable == nil {
          // print an error or return from the method or fuction
      }
      

      Effectively you want to make sure you don’t have a nil pointer that was passed into your function or method. If you do, you’ll likely just want to return, or return an error to show that an invalid argument was passed to the function or method. The following code demonstrates checking for nil:

      main.go

      package main
      
      import "fmt"
      
      type Creature struct {
          Species string
      }
      
      func main() {
          var creature *Creature
      
          fmt.Printf("1) %+vn", creature)
          changeCreature(creature)
          fmt.Printf("3) %+vn", creature)
      }
      
      func changeCreature(creature *Creature) {
          if creature == nil {
              fmt.Println("creature is nil")
              return
          }
      
          creature.Species = "jellyfish"
          fmt.Printf("2) %+vn", creature)
      }
      

      We added a check in the changeCreature to see if the value of the creature argument was nil. If it was, we print out creature is nil, and return out of the function. Otherwise, we continue and change the value of the Species field. If we run the program, we will now get the following output:

      Output

      1) <nil> creature is nil 3) <nil>

      Notice that while we still had a nil value for the creature variable, we are no longer panicking because we are checking for that scenario.

      Finally, if we create an instance of the Creature type and assign it to the creature variable, the program will now change the value as expected:

      main.go

      package main
      
      import "fmt"
      
      type Creature struct {
          Species string
      }
      
      func main() {
          var creature *Creature
          creature = &Creature{Species: "shark"}
      
          fmt.Printf("1) %+vn", creature)
          changeCreature(creature)
          fmt.Printf("3) %+vn", creature)
      }
      
      func changeCreature(creature *Creature) {
          if creature == nil {
              fmt.Println("creature is nil")
              return
          }
      
          creature.Species = "jellyfish"
          fmt.Printf("2) %+vn", creature)
      }
      

      Now that we have an instance of the Creature type, the program will run and we will get the following expected output:

      Output

      1) &{Species:shark} 2) &{Species:jellyfish} 3) &{Species:jellyfish}

      When you are working with pointers, there is a potential for the program to panic. To avoid panicking, you should check to see if a pointer value is nil prior to trying to access any of the fields or methods defined on it.

      Next, let’s look at how using pointers and values affects defining methods on a type.

      Method Pointer Receivers

      A receiver in go is the argument that is defined in a method declaration. Take a look at the following code:

      type Creature struct {
          Species string
      }
      
      func (c Creature) String() string {
          return c.Species
      }
      

      The receiver in this method is c Creature. It is stating that the instance of c is of type Creature and you will reference that type via that instance variable.

      Just like the behavior of functions is different based on whether you send in an argument as a pointer or a value, methods also have different behavior. The big difference is that if you define a method with a value receiver, you are not able to make changes to the instance of that type that the method was defined on.

      There will be times that you would like your method to be able to update the instance of the variable that you are using. To allow for this, you would want to make the receiver a pointer.

      Let’s add a Reset method to our Creature type that will set the Species field to an empty string:

      main.go

      package main
      
      import "fmt"
      
      type Creature struct {
          Species string
      }
      
      func (c Creature) Reset() {
          c.Species = ""
      }
      
      func main() {
          var creature Creature = Creature{Species: "shark"}
      
          fmt.Printf("1) %+vn", creature)
          creature.Reset()
          fmt.Printf("2) %+vn", creature)
      }
      

      If we run the program, we will get the following output:

      Output

      1) {Species:shark} 2) {Species:shark}

      Notice that even though in the Reset method we set the value of Species to an empty string, that when we print out the value of our creature variable in the main function, the value is still set to shark. This is because we defined the Reset method has having a value receiver. This means that the method will only have access to a copy of the creature variable.

      If we want to be able to modify the instance of the creature variable in the methods, we need to define them as having a pointer receiver:

      main.go

      package main
      
      import "fmt"
      
      type Creature struct {
          Species string
      }
      
      func (c *Creature) Reset() {
          c.Species = ""
      }
      
      func main() {
          var creature Creature = Creature{Species: "shark"}
      
          fmt.Printf("1) %+vn", creature)
          creature.Reset()
          fmt.Printf("2) %+vn", creature)
      }
      

      Notice that we now added an asterisk (*) in front of the Creature type in when we defined the Reset method. This means that the instance of Creature that is passed to the Reset method is now a pointer, and as such when we make changes it will affect the original instance of that variables.

      Output

      1) {Species:shark} 2) {Species:}

      The Reset method has now changed the value of the Species field.

      Conclusion

      Defining a function or method as a pass by value or pass by reference will affect what parts of your program are able to make changes to other parts. Controlling when that variable can be changed will allow you to write more robust and predictable software. Now that you have learned about pointers, you can see how they are used in interfaces as well.



      Source link

      How To Run Transactions in Redis


      Introduction

      Redis is an open-source, in-memory key-value data store. Redis allows you to plan a sequence of commands and run them one after another, a procedure known as a transaction. Each transaction is treated as an uninterrupted and isolated operation, which ensures data integrity. Clients cannot run commands while a transaction block is being executed

      This tutorial goes over how to execute and cancel transactions, and also includes some information on pitfalls commonly associated with transactions.

      How To Use This Guide
      This guide is written as a cheat sheet with self-contained examples. We encourage you to jump to any section that is relevant to the task you’re trying to complete.

      The commands shown in this guide were tested on an Ubuntu 18.04 server running Redis version 4.0.9. To obtain a similar setup, you can follow Step 1 of our guide on How To Install and Secure Redis on Ubuntu 18.04. We will demonstrate how these commands behave by running them with redis-cli, the Redis command line interface. Note that if you’re using a different Redis interface — Redli, for example — the exact output of certain commands may differ.

      Alternatively, you could provision a managed Redis database instance to test these commands, but note that depending on the level of control allowed by your database provider, some commands in this guide may not work as described. To provision a DigitalOcean Managed Database, follow our Managed Databases product documentation. Then, you must either install Redli or set up a TLS tunnel in order to connect to the Managed Database over TLS.

      Running Transactions

      The multi command tells Redis to begin a transaction block. Any subsequent commands will be queued up until you run an exec command, which will execute them.

      The following commands form a single transaction block. The first command initiates the transaction, the second sets a key holding a string with the value of 1, the third increases the value by 1, the fourth increases its value by 40, the fifth returns the current value of the string, and the last one executes the transaction block:

      • multi
      • set key_MeaningOfLife 1
      • incr key_MeaningOfLife
      • incrby key_MeaningOfLife 40
      • get key_MeaningOfLife
      • exec

      After running multi, redis-cli will respond to each of the following commands with QUEUED. After you run the exec command, it will show the output of each of those commands individually:

      Output

      1) OK 2) (integer) 2 3) (integer) 42 4) "42"

      Commands included in a transaction block are run sequentially in the order they’re queued. Redis transactions are atomic, meaning that either every command in a transaction block is processed (meaning that it’s accepted as valid and queued to be executed) or none are. However, even if a command is successfully queued, it may still produce an error when executed. In such cases, the other commands in the transaction can still run, but Redis will skip the error-causing command. See the section on understanding transaction errors for more details.

      Canceling Transactions

      To cancel a transaction, run the discard command. This prevents any previously-queued commands from running:

      • multi
      • set key_A 146
      • incrby key_A 10
      • discard

      Output

      OK

      The discard command returns the connection to a normal state, which tells Redis to run single commands as usual. You’ll need to run multi again to tell the server you’re starting another transaction.

      Understanding Transaction Errors

      Some commands may be impossible to queue, such as commands with syntax errors. If you attempt to queue a syntactically incorrect command Redis will return an error.

      The following transaction createst a key named key_A and then attempts to increment it by 10. However, a spelling error in the incrby command causes and error and closes the transaction:

      • multi
      • set key_A 146
      • incrbuy key_A 10

      Output

      (error) ERR unknown command 'incrbuy'

      If you try to run an exec command after trying to queue a command with a syntax error like this one, you will receive another error message telling you that the transaction was discarded:

      Output

      (error) EXECABORT Transaction discarded because of previous errors.

      In cases like this, you’ll need to restart the transaction block and make sure you enter each command correctly.

      Some impossible commands are possible to queue, such as running incr on a key containing only a string. Because such command is syntactically correct, Redis won’t return an error if you try to include it in a transaction and won’t prevent you from running exec. In cases like this, all other commands in the queue will be executed, but the impossible command will return an error:

      • multi
      • set key_A 146
      • incrby key_A "ten"
      • exec

      Output

      1) OK 2) (error) ERR value is not an integer or out of range

      For more information on how Redis handles errors inside transactions, see the official documentation on the subject.

      Conclusion

      This guide details a number of commands used to create, run, and cancel transactions in Redis. If there are other related commands, arguments, or procedures you’d like to see outlined in this guide, please ask or make suggestions in the comments below.

      For more information on Redis commands, see our tutorial series on How to Manage a Redis Database.



      Source link

      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