|
| 1 | +# What is 'this'? |
| 2 | + |
| 3 | +At any point during the execution of a JavaScript program there is a context-dependent value that you can access through the keyword `this`. In many cases the value of `this` is `undefined` and as such not of significance for use in your own code. |
| 4 | + |
| 5 | +Note: The value of `this` is only useful if used inside a function. |
| 6 | + |
| 7 | +> When accessed outside of any function, the value of `this` is different depending on whether you run your program in the browser or in Node. In the case of the browser, the value of `this` refers to the global `window` object. In the case of Node, the value of `this` outside any function is an empty object (`{}`). |
| 8 | +
|
| 9 | +## Regular functions and `this` |
| 10 | + |
| 11 | +Let's look at the value of `this` when used in a regular function: |
| 12 | + |
| 13 | +```js |
| 14 | +'use strict'; |
| 15 | + |
| 16 | +function whatIsThis(arg) { |
| 17 | + console.log(arg, this); |
| 18 | +} |
| 19 | + |
| 20 | +whatIsThis('Hello'); // --> Hello undefined |
| 21 | +``` |
| 22 | + |
| 23 | +As mentioned, the value of `this` in this case is `undefined`. Note however that this is only the case if we start our file with the string literal `'use strict'`. In versions of JavaScript prior to ES5 the `'use strict'` option did not exist. If you leave out `'use strict'` then `this` refers to the 'global context' (in the browser this is the `window` object, in Node it is the `global` object). |
| 24 | + |
| 25 | +In the example below you can see the effect (the shown output is for the browser). |
| 26 | + |
| 27 | +```js |
| 28 | +// left out: 'use strict'; |
| 29 | + |
| 30 | +function whatIsThis(arg) { |
| 31 | + console.log(arg, this); |
| 32 | +} |
| 33 | + |
| 34 | +whatIsThis('Hello'); // --> Hello |
| 35 | + // ▶︎ Window {postMessage: f, ...} |
| 36 | +``` |
| 37 | + |
| 38 | +Accessing the global context through `this` (accidentally on or on purpose) is never a good idea, especially when it comes to forgetting to declare a variable. The designers of JavaScript recognised this as an issue and provided the `'use strict'` option in ES5 to remedy the issue. |
| 39 | + |
| 40 | +More info on MDN: [Strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) |
| 41 | + |
| 42 | +## Function invocation through the `call` method |
| 43 | + |
| 44 | +When you call a regular function by specifying its name followed by zero or more arguments enclosed within parentheses, the JavaScript engine actually invokes the `call` method that exists on every function (yes, functions are actually a special type of JavaScript objects). The code snippet show how a regular function is invoked behind the scenes by the JavaScript engine, setting the `this` value (the first argument of the `call` method) to `undefined`. |
| 45 | + |
| 46 | +```js |
| 47 | +'use strict'; |
| 48 | + |
| 49 | +function whatIsThis(arg) { |
| 50 | + console.log(arg, this); |
| 51 | +} |
| 52 | + |
| 53 | +whatIsThis.call(undefined, 'Hello'); // --> Hello undefined |
| 54 | +``` |
| 55 | + |
| 56 | +We can use the `call` method ourselves and pass something else in place of `undefined`, as show in the next snippet: |
| 57 | + |
| 58 | +```js |
| 59 | +whatIsThis.call('world!', 'Hello'); // --> Hello world! |
| 60 | +``` |
| 61 | + |
| 62 | +However, in practice there are not many occasions where we would need to use the `call` method ourselves to set the value of `this`. |
| 63 | + |
| 64 | +Next, we will see how `this` becomes relevant when used in combination with JavaScript objects. |
| 65 | + |
| 66 | +More info on `this` and JavaScript function properties and methods: |
| 67 | + |
| 68 | +- [Understanding JavaScript Function Invocation and "this"](http://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/), by Yehuda Katz. |
| 69 | +- MDN: [Function prototype object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function#Function_prototype_object) |
| 70 | + |
| 71 | +## JavaScript objects and 'this' |
| 72 | + |
| 73 | +When used in conjunction with JavaScript object methods (including those from ES6 classes) the `this` keyword gets overriding importance. |
| 74 | + |
| 75 | +**What is a method?** A method is a regular JavaScript function that is 'called on' an object, using dot notation. In almost all cases this function will be defined as a property of the object (or it's prototype) it is called upon. In ES6 classes, methods are directly defined as member functions on the class. |
| 76 | + |
| 77 | +The example below shows a simple object with a data property `myData` and a method property `myMethod`. When `myMethod` is called using dot notation such as shown in the example below, the `this` value inside the method is set to the object itself. Hence, `myMethod` has access to the `myData` property through the `this` keyword. |
| 78 | + |
| 79 | +```js |
| 80 | +const myObj = { |
| 81 | + myData: 'Hello world!', |
| 82 | + myMethod: function () { |
| 83 | + console.log(this.myData); |
| 84 | + } |
| 85 | +}; |
| 86 | + |
| 87 | +myObj.myMethod(); // --> Hello world! |
| 88 | +``` |
| 89 | + |
| 90 | +## Function.prototype.bind |
| 91 | + |
| 92 | +There is yet another way to set the `this` value, this time fixing it's value. This can be done through the `bind` method that is avaiable on every function (although it has no effect on fat arrow functions). |
| 93 | + |
| 94 | +To just fix the value of `this` you call the `bind` method with a single parameter, passing the value to be assigned to `this`. (The `bind` method accepts additional parameters, but their use is beyond the scope of this article. See the reference below for more information). |
| 95 | + |
| 96 | +The `bind` method returns a new function for which the `this` value is fixed to the value specified in the `bind` parameter, as shown below. |
| 97 | + |
| 98 | +```js |
| 99 | +const myObj = { |
| 100 | + myData: 'Hello world!' |
| 101 | +}; |
| 102 | + |
| 103 | +function printMyData() { |
| 104 | + console.log(this.myData); |
| 105 | +} |
| 106 | + |
| 107 | +const newFunction = printMyData.bind(myObj); |
| 108 | +newFunction(); // --> Hello world! |
| 109 | +``` |
| 110 | + |
| 111 | +Using the `printMyData` function as a basis, the `bind` method fixes the `this` value to `myObj` and returns a new function assigned here to the variable `newFunction`. When we call `newFunction` the `this` value will be `myObj`, and therefore we can console.log `myData` through the `this` keyword. |
| 112 | + |
| 113 | +You will come across the `bind` method extensively in the HYF React module. |
| 114 | + |
| 115 | +MDN: [Function.prototype.bind()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind) |
| 116 | + |
| 117 | +## Fat arrow functions and 'this' |
| 118 | + |
| 119 | +In fat arrow functions the value of `this` is unchanged from its enclosing scope. This makes fat arrow functions specifically useful as event handlers and callbacks when used in objects and ES classes where `this` is used. |
| 120 | + |
| 121 | +In the example below we have defined a simple ES6 class which uses a `setTimeout` to console.log the `myData` value after one second. The callback passed as the first parameter of `setTimeout` is written as a fat arrow function. Hence, the `this` value inside the fat arrow function refers to the `this` value of the `sayDelayed` method. Because `sayDelayed` is called using object notation, the `this` value is set to the object itself. Thus, the expected result is printed. |
| 122 | + |
| 123 | +Had we written the callback as a regular (anonymous) function, using the `function` keyword, this would have failed as its `this` value would have been set to `undefined`. Try it! |
| 124 | + |
| 125 | +```js |
| 126 | +class MyClass { |
| 127 | + |
| 128 | + constructor() { |
| 129 | + this.myData = 'Hello world' |
| 130 | + } |
| 131 | + |
| 132 | + sayDelayed() { |
| 133 | + setTimeout(() => { |
| 134 | + console.log(this.myData); |
| 135 | + }, 1000); |
| 136 | + } |
| 137 | +} |
| 138 | + |
| 139 | +const myClass = new MyClass(); |
| 140 | +myClass.sayDelayed(); |
| 141 | +``` |
0 commit comments