|
| 1 | +# `this` Keyword |
| 2 | + |
| 3 | +In simple terms, `this` keyword in JavaScript refers to an object upon which a function is invoked. This means that `this` can only be used in a function, or globally. |
| 4 | + |
| 5 | +`this` is used to point to an instance of an object from its own constructor or its method and also to keep track of the execution context (lexical scope) - which is often based on where the function is called from. |
| 6 | + |
| 7 | +```js |
| 8 | +function myself() { |
| 9 | + this.name = "Prabesh"; |
| 10 | + console.log(this); // OUTPUT: myself {"name": "Prabesh"} |
| 11 | +} |
| 12 | + |
| 13 | +let intro = new myself(); |
| 14 | +``` |
| 15 | + |
| 16 | +In the example above, `myself` is used as constructor for the object of type `myself`. Here, `this` reference points to itself i.e. myself {name: "Prabesh"}. |
| 17 | + |
| 18 | +When we use the `new` keyword to create an instance of the `myself()` function, it creates new object and sets `this` to refer to that object. In this case, the `name` property is set to the new object created by the `new` operator. |
| 19 | + |
| 20 | +So, when we create a new instance of `myself()` using `let intro = new myself()`, the `name` property is set on the newly created object, and `this` refers to that object. Therefore, `intro` will refer to an object with a `name` property of "Prabesh". |
| 21 | + |
| 22 | +## `this` in function |
| 23 | + |
| 24 | +When used in either of function or globally, `this` refers to the window object. |
| 25 | + |
| 26 | +```js |
| 27 | +function foo() { |
| 28 | + console.log(this); |
| 29 | +} |
| 30 | + |
| 31 | +foo(); // OUTPUT : Window |
| 32 | +``` |
| 33 | + |
| 34 | +Here, when we call the `foo()` function without `new` keyword, `this` refers to the global object (which could be `window` in browser or `global` in Node.js). |
| 35 | + |
| 36 | +However, when we want to use the function as constructor of an object (create an object of type `foo`), `this` keyword changes its context to the instance of `bar` object itself and no longer global object. |
| 37 | + |
| 38 | +```js |
| 39 | +function foo() { |
| 40 | + console.log(this); |
| 41 | +} |
| 42 | + |
| 43 | +let bar = new foo(); //Output: foo{} |
| 44 | +typeof bar; //Ouput: 'object' |
| 45 | +``` |
| 46 | + |
| 47 | +## `this` in methods |
| 48 | + |
| 49 | +When a function is a method of an object, then `this` refers to object that the method is a member of. This allows us to access and manipulate the properties of the object within the method. |
| 50 | + |
| 51 | +_Example:_ |
| 52 | + |
| 53 | +```js |
| 54 | +const user = { |
| 55 | + name: "Prabesh", |
| 56 | + age: 23, |
| 57 | + greet: function () { |
| 58 | + console.log(`Hello, my name is ${this.name}`); |
| 59 | + }, |
| 60 | +}; |
| 61 | + |
| 62 | +user.greet(); // Output: "Hello, my name is Prabesh" |
| 63 | +``` |
| 64 | + |
| 65 | +In the above example, `user` is an object that has a `greet()` method. When we call `user.greet()`, `this` refers to the `user` object. Hence, within the `greet()` method, we can access the `name` property of `user` object using `this.name`. |
| 66 | + |
| 67 | +## `this` in event handlers |
| 68 | + |
| 69 | +The value of `this` inside handler function may not be what we expect it to be. The value of `this` inside an event handler function is determined by how the function is called. In most cases, `this` will refer to DOM element that triggered the event. |
| 70 | + |
| 71 | +For example, if you have a button with an `onClick` attribute that calls a function, the value of `this` inside that function will be the button element. We can use `this` keyword to access properties and methods of the object that the event is asscoiated with. For example, we can use `this.textContent` property to get the text content of the button object. |
| 72 | + |
| 73 | +```php |
| 74 | +<button id="myButton">Click me</button> |
| 75 | + |
| 76 | +<script> |
| 77 | +function myFunction() { |
| 78 | + console.log(this); // logs the button element |
| 79 | + // Output: <button id="myButton">Click me</button> |
| 80 | +} |
| 81 | + |
| 82 | +// Attach the event handler to the button's click event. |
| 83 | +document.getElementById("myButton").addEventListener("click", myFunction); |
| 84 | +</script> |
| 85 | +``` |
| 86 | + |
| 87 | +When the button is clicked, the `myClickHandler()` function will be called. The `this` keyword will refer to the button object, so you can use the `this` keyword to access and modify the button's properties and methods. |
| 88 | + |
| 89 | +Now, let's consider annther example: |
| 90 | + |
| 91 | +```js |
| 92 | +function cookFood(dish) { |
| 93 | + this.dish = dish; |
| 94 | + this.time = time; |
| 95 | + |
| 96 | + function setTime(sec) { |
| 97 | + setTimeout(function () { |
| 98 | + console.log(this.dish + " cooked for " + sec + " seconds"); |
| 99 | + }, sec * 1000); |
| 100 | + } |
| 101 | +} |
| 102 | + |
| 103 | +let curry = new cookFood("Curry"); // this.dish = "Curry" |
| 104 | +curry.setTime(3); // Output: undefined cooked for 2 seconds |
| 105 | +``` |
| 106 | + |
| 107 | +So, what happened here? We have set `this.dish="Curry"` when constructing the `cookFood` object but we get "undefined cooked for 2 seconds" when we expected the output to be "Curry cooked for 2 seconds". |
| 108 | + |
| 109 | +Here, when we call the `setTimeout` function, it creates a nre execution context for the anonymous function that we pass as an argument, which has its own `this` context. Hence, when the anonymous function executes, `this.dish` will be `undefined` because `this` refers to the global object, not the `cookFood` object we created. |
| 110 | + |
| 111 | +This is where arrow functions comes into rescue. |
| 112 | + |
| 113 | +## `this` in arrow functions |
| 114 | + |
| 115 | +Arrow functions help us write more concise and efficient code. Arrow functions does not create it's own execution context but inherits `this` from outside function where the function is defined. |
| 116 | + |
| 117 | +Therefore, the problem in the above code can be fixed just br replacing the regular function for the anonymous function with arrow function. |
| 118 | + |
| 119 | +```js |
| 120 | +function cookFood(dish) { |
| 121 | + this.dish = dish; |
| 122 | + this.time = time; |
| 123 | + |
| 124 | + function setTime(sec) { |
| 125 | + setTimeout(() => { |
| 126 | + console.log(this.dish + " cooked for " + sec + " seconds"); |
| 127 | + }, sec * 1000); |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +let curry = new cookFood("Curry"); // this.dish = "Curry" |
| 132 | +curry.setTime(3); // Output: Curry cooked for 2 seconds |
| 133 | +``` |
| 134 | + |
| 135 | +Since, arrow functions do not create their own `this` context. the `this` will refer to the `cookFood` object we created. |
| 136 | + |
| 137 | +# Function Borrowing |
| 138 | + |
| 139 | +Function borrowing is a concept that allows us to reuse the methods of one object on another object. Simply put, we can borrow the methods of one object and use them on other object. |
| 140 | + |
| 141 | +## Implicit Binding |
| 142 | + |
| 143 | +Implicit binding is the default way that `this` is determined in a function call. Whenever we invoke a method of an object using the dot notation, the `this` keyword will point to the object with which it was invoked. In simple terms, when a function is called as a method of an object, `this` is set to the object itself. |
| 144 | + |
| 145 | +```js |
| 146 | +const user = { |
| 147 | + name: "John", |
| 148 | + greetUser: function () { |
| 149 | + console.log("Welcome to JavaScript " + this.name); |
| 150 | + }, |
| 151 | +}; |
| 152 | + |
| 153 | +user.greetUser(); // Output: "Welcome to JavaScript John" |
| 154 | +``` |
| 155 | + |
| 156 | +In this example, `greetUser()` is called as a method of `user`, so `this` is set to `user`. |
| 157 | + |
| 158 | +## Explicit Binding |
| 159 | + |
| 160 | +Explicit binding in JavaScript refers to the technique of manually setting the `this` keyword inisde a function using the `call()`, `apply()` or `bind()` methods. We use explicit binding when we want to access the methods of an object with the values provided by another object. |
| 161 | + |
| 162 | +### π€ bind() |
| 163 | + |
| 164 | +The `bind()` method creates and returns a new function, whose `this` keyword is set to object which was passed to it. It _binds_ a method to an object and returns to be invoked later. |
| 165 | + |
| 166 | +```js |
| 167 | +function greet() { |
| 168 | + console.log("Welcome " + this.name); |
| 169 | +} |
| 170 | + |
| 171 | +const user1 = { |
| 172 | + name: "Prabesh", |
| 173 | +}; |
| 174 | + |
| 175 | +const user2 = { |
| 176 | + name: "Laxmi", |
| 177 | +}; |
| 178 | + |
| 179 | +const greetUser1 = greet.bind(user1); |
| 180 | +const greetUser2 = greet.bind(user2); |
| 181 | + |
| 182 | +greetUser1(); // Output: "Welcome Prabesh" |
| 183 | +greetUser2(); // Output: "Welcome Laxmi" |
| 184 | +``` |
| 185 | + |
| 186 | +In the above example, we have function `greet()` and objects `user1` and `user2` with `name` properties. We are using the `bind()` method to create two new functions `greetUser1()` and `greetUser2()` which |
| 187 | +have their `this` value bound to `user1` and `user2` respectively. |
| 188 | + |
| 189 | +Let's have a look at another example: |
| 190 | + |
| 191 | +```js |
| 192 | +const user = { |
| 193 | + firstName: "Super", |
| 194 | + lastName: "Mario", |
| 195 | + greet: function () { |
| 196 | + console.log("Hello, I am " + this.firstName + " " + this.lastName + "."); |
| 197 | + }, |
| 198 | +}; |
| 199 | + |
| 200 | +user.greet(); // Output: "Hello, I am Super Mario." |
| 201 | + |
| 202 | +const user2 = { |
| 203 | + firstName: "Bob", |
| 204 | + lastName: "Builder", |
| 205 | +}; |
| 206 | + |
| 207 | +const intro = user.greet.bind(user2); |
| 208 | +intro(); // Output: Hello, I am Bob Builder. |
| 209 | +``` |
| 210 | + |
| 211 | +Here, we defined an object call `user` that has three properties: `firstName`, `lastName` and `greet`. The `greet` property is a function that logs a message including `firstName` and `lastName`. When we call `user.greet()`, we are invoking the `greet` function with `this` referring to `user` object. Hence, we get a output of "Hello, I am Super Mario". This concept is referred as `impilicit binding`. |
| 212 | + |
| 213 | +Next, we define another object called `user2` that has two properties: `firstName` and `lastName`. We then create a function called `intro` by using the `bind()` method to bind the `this` keyword of the `greet` function to the `user2` object. This means when we call `intro()` function, `this` inside the `greet` function will refer to the `user2` object and the output will be "Hello, I am Bob Builder." |
| 214 | + |
| 215 | +To summarize, by using `bind()`, we are essentially creating a new function that has its `this` value set to `user2` object. This allows us to resue the `greet` function with a different object. This concept is referred as `explicit binding`. |
| 216 | + |
| 217 | +### π€ call() |
| 218 | + |
| 219 | +The `call()` method works similar to `bind()` but has one major difference. As we know, `bind()` method returns a function that can be invoked later but the `call()` method invokes a function and returns the result. |
| 220 | + |
| 221 | +```js |
| 222 | +function greet() { |
| 223 | + console.log("Hello, my name is " + this.name); |
| 224 | +} |
| 225 | + |
| 226 | +const person = { |
| 227 | + name: "Ninja", |
| 228 | +}; |
| 229 | + |
| 230 | +greet.call(person); // Output: "Hello, my name is Ninja" |
| 231 | +``` |
| 232 | + |
| 233 | +Here, we hava a `greet` function and an object `person` with a `name` property. We are using the `call()` method to call `greet()` function with `person` as the context to `this`. As a result, we get an output of "Hello, my name is Ninja". |
| 234 | + |
| 235 | +let us consider another example: |
| 236 | + |
| 237 | +```js |
| 238 | +const user = { |
| 239 | + firstName: "Sonic", |
| 240 | + lastName: "Hedgehog", |
| 241 | + greet: function (place) { |
| 242 | + console.log(`I am ${this.firstName} ${this.lastName} from ${place}`); |
| 243 | + }, |
| 244 | +}; |
| 245 | + |
| 246 | +user.greet("Colorado"); // Output: I am Sonic Hedgehog from Colorado |
| 247 | + |
| 248 | +const user2 = { |
| 249 | + firstName: "Ninja", |
| 250 | + lastName: "Turtle", |
| 251 | +}; |
| 252 | + |
| 253 | +user.greet.call(user2, "Texas"); // Output: I am Ninja Turtle from Texas |
| 254 | +``` |
| 255 | + |
| 256 | +In this example, we have two objects: `user` and `user2`. We are borrowing the `greet()` function from `user` object using `call()` method abnd binding it with `user2` object. |
| 257 | + |
| 258 | +By using the `call()` method, we are invoking the `greet` function in the context of `user2` object. The first argument to `call()` is the object that `this` should refer to inside the fucntion, and any subsequent arguments are passed as arguments to the function itself. |
| 259 | + |
| 260 | +### π apply() |
| 261 | + |
| 262 | +The `apply()` method is similar to `call()` method, but takes an array of arguments instead of inidividual arguments. In the `call()` method, we can pass multiple parameters, but in `apply()` method, we have to pass in single array that would contain all the parameters. |
| 263 | + |
| 264 | +```js |
| 265 | +const user = { |
| 266 | + firstName: "Cristiano", |
| 267 | + lastName: "Ronaldo", |
| 268 | + greet: function (club, country) { |
| 269 | + console.log( |
| 270 | + `I am ${this.firstName} ${this.lastName} from ${country} playing for ${club}.` |
| 271 | + ); |
| 272 | + }, |
| 273 | +}; |
| 274 | + |
| 275 | +user.greet("Al-Nassr", "Portugal"); // Output: I am Cristiano Ronaldo from Portugal playing for Al-Nassr. |
| 276 | + |
| 277 | +const user2 = { |
| 278 | + firstName: "Lionel", |
| 279 | + lastName: "Messi", |
| 280 | +}; |
| 281 | + |
| 282 | +user.greet.apply(user2, ["PSG", "Argentina"]); // Output: I am Lionel Messi from Argentina playing for PSG. |
| 283 | +``` |
| 284 | + |
| 285 | +By using `apply()`, we're essentially invoking the `greet` function in the context of the `user2` object. The first argument to `apply()` is the object that `this` should refer to inside the function, and the second argument is an array of arguments to pass to the function itself. In this case, we're passing an array of two elements, "PSG" and "Argentina", which are used as the `club` and `country` parameters in the `greet` function, respectively. |
| 286 | + |
| 287 | +**_Summary:_** |
| 288 | + |
| 289 | +- By default, the methods of an object are implicitly bounded to the object itself, and we can access them using the dot (.) operator. |
| 290 | +- To access the methods of other objects, we need to explicitly bind them to the object using the `bind()`, `call()`, or `apply()` methods, with each one of them having its own use cases. |
0 commit comments