|
| 1 | +# [A walk in JavaScript](/README.md) |
| 2 | + |
| 3 | +## DAY 7 |
| 4 | + |
| 5 | +- `this` Keyword |
| 6 | + - Introduction |
| 7 | + - Resolving `this` |
| 8 | + - Explicitly binding `this` through prototype methods |
| 9 | + - `Function.prototype.bind()` |
| 10 | + - `Function.prototype.apply()` |
| 11 | + - `Function.prototype.call()` |
| 12 | +- Strict mode |
| 13 | + - What happens on strict mode? |
| 14 | + - Semantic Differences |
| 15 | +- Arrow Functions |
| 16 | +- Generators |
| 17 | +- Exercises |
| 18 | + |
| 19 | +## `this` Keyword |
| 20 | + |
| 21 | +Over and over again I see engineers struggling with `this` topic; is so weird!! Long ago I found myself in the same situation, like, being writing code for many years and still ... never took the time to really understand `this` when `this` is one of the most important and powerful features in JavaScript! |
| 22 | +Engineers we feel so frustrated about `this` that there's even a joke for `this`! |
| 23 | + |
| 24 | +> JavaScript makes me want to flip the table and say "F*** this shit", but I can never be sure what **`this`** refers to. |
| 25 | +
|
| 26 | +The good things came when I took the responsibility of `this` and accepted the guilt was entirely mine. |
| 27 | + |
| 28 | +Why this intro? because there are tons of articles regarding `this` but everything about `this` was written by **Kyle Simpson** who dedicated a whole book for `this` topic , so we're gonna read and study it until we breath `this`. |
| 29 | + |
| 30 | +### Resolving `this` |
| 31 | + |
| 32 | +Let's take a look at the following chapters of **You Don't Know JS: this & Object Prototypes - 1st Edition** |
| 33 | + |
| 34 | +- [Chapter 1: this Or That?](https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/this%20%26%20object%20prototypes/ch1.md) |
| 35 | +- [Chapter 2: this All Makes Sense Now!](https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/this%20%26%20object%20prototypes/ch2.md) |
| 36 | +- [Chapter 5: Prototypes](https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/this%20%26%20object%20prototypes/ch5.md) |
| 37 | +- [Chapter 6: Behavior Delegation](https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/this%20%26%20object%20prototypes/ch6.md) |
| 38 | + |
| 39 | +Now let's see how ECMAScript specifies the mechanism to resolve `this`. |
| 40 | + |
| 41 | +- [GetThisEnvironment ( )](https://www.ecma-international.org/ecma-262/6.0/#sec-getthisenvironment) |
| 42 | +- [ResolveThisBinding ( )](https://www.ecma-international.org/ecma-262/6.0/#sec-resolvethisbinding) |
| 43 | + |
| 44 | +In the other hand, MDN describes `this` on the Operators section |
| 45 | + |
| 46 | +- [MDN - *this*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) |
| 47 | + |
| 48 | +### Explicitly binding `this` through prototype methods |
| 49 | + |
| 50 | +Now we've learned that `this` has specific rules and it's resolved at run-time, and we saw that the `function` prototype has 3 methods to explicitly define where to point when `this` needs to be resolved during it's execution. |
| 51 | + |
| 52 | +- [Function.prototype.bind()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) |
| 53 | +- [Function.prototype.apply()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) |
| 54 | +- [Function.prototype.call()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call) |
| 55 | + |
| 56 | +Now, there's a catch!!! it seems that depending on a thing called **mode**, that depending on it's [strictness](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) or [non-strictness](https://developer.mozilla.org/en-US/docs/Glossary/Sloppy_mode) (a.k.a. Sloppy) it'll alter the semantics and behavior of many things including `this`. |
| 57 | + |
| 58 | +--- |
| 59 | + |
| 60 | +## Strict Mode |
| 61 | + |
| 62 | +- [MDN - Strict Mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) |
| 63 | +- [MDN - Transitioning to Strict Mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode/Transitioning_to_strict_mode) |
| 64 | +- [ECMAScript 5.1 - Strict Mode](http://www.ecma-international.org/ecma-262/5.1/#sec-10.1.1) |
| 65 | +[ECMAScript 2015 - Strict Mode](http://www.ecma-international.org/ecma-262/6.0/#sec-strict-mode-code) |
| 66 | +- [The ECMAScript 2016 change you probably don't know](https://humanwhocodes.com/blog/2016/10/the-ecmascript-2016-change-you-probably-dont-know/) |
| 67 | +- [Speaking JavaScript - Chp.7 Strict Mode" - by Dr. Axel Rauschmayer](http://speakingjs.com/es5/ch07.html#strict_mode) |
| 68 | + |
| 69 | +### What happens on strict mode? |
| 70 | + |
| 71 | +#### TL;DR |
| 72 | + |
| 73 | +1. Eliminates some JavaScript `silent errors` by changing them `to throw errors`. |
| 74 | +2. Fixes mistakes that make it difficult for JavaScript engines to perform optimizations: strict mode code can sometimes be made to run faster than identical code that's not strict mode. |
| 75 | +3. Prohibits some syntax likely to be defined in future versions of ECMAScript. |
| 76 | + |
| 77 | +### Semantic differences |
| 78 | + |
| 79 | +- `this` resolution won't propagate to the global scope, thus for a strict mode function, the specified this is not boxed into an object, and if unspecified, this will be `undefined` |
| 80 | +- arguments doesn't alias named function arguments |
| 81 | +- `eval` doesn't create a new variable in the scope from which it was called, `eval` of strict mode code does not introduce new variables into the surrounding scope. |
| 82 | + |
| 83 | +When adding `'use strict';` the following cases will throw an Error: |
| 84 | + |
| 85 | +- SyntaxError |
| 86 | + - Octal syntax `var n = 023;` |
| 87 | + - `with` statement |
| 88 | + - Using delete on a variable name `delete myVariable`; |
| 89 | + - Using `eval` or `arguments` as variable or function argument name |
| 90 | + - Using one of the newly reserved keywords (in prevision for ECMAScript 2015): |
| 91 | + - `implements`, |
| 92 | + - `interface`, |
| 93 | + - `let`, |
| 94 | + - `package`, |
| 95 | + - `private`, |
| 96 | + - `protected`, |
| 97 | + - `public`, |
| 98 | + - `static`, |
| 99 | + - and `yield` |
| 100 | + - Escape characters are not allowed `var y = \010;` |
| 101 | + - Declaring function in blocks `if (a < b) { function f() {} }` |
| 102 | + - Obvious errors |
| 103 | + - Declaring twice the same name for a property name in an object literal `{a: 1, b: 3, a: 7}` This is no longer the case in ECMAScript 2015 (bug 1041128). |
| 104 | + - Declaring two function parameters with the same name function `f(a, b, b) {}` |
| 105 | +- TypeError |
| 106 | + - Writing to a get-only property is not allowed |
| 107 | + - Writing to a read-only property is not allowed |
| 108 | + - Deleting an undeletable property is not allowed `delete Object.prototype` |
| 109 | + - Setting properties on primitive values `false.true = '';` , `(14).sailing = 'home';` , `'with'.you = 'far away';` |
| 110 | +- Runtime errors |
| 111 | + - Setting a value to an undeclared variable |
| 112 | + - Trying to delete a non-configurable property |
| 113 | + - Poisoned arguments and function properties, e.g. accessing `arguments.callee`, `arguments.caller`, `anyFunction.caller`, or `anyFunction.arguments` |
| 114 | +- ReferenceError |
| 115 | + - Using a variable, without declaring it |
| 116 | + |
| 117 | +--- |
| 118 | + |
| 119 | +## Arrow Functions |
| 120 | + |
| 121 | +> An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords. Arrow function expressions are ill suited as methods, and they cannot be used as constructors. |
| 122 | +> |
| 123 | +> Source: [MDN - Arrow Functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) |
| 124 | +
|
| 125 | +### Syntax |
| 126 | + |
| 127 | +```javascript |
| 128 | +(param1, param2, …, paramN) => { statements } |
| 129 | +(param1, param2, …, paramN) => expression |
| 130 | +// equivalent to: => { return expression; } |
| 131 | + |
| 132 | +// Parentheses are optional when there's only one parameter name: |
| 133 | +(singleParam) => { statements } |
| 134 | +singleParam => { statements } |
| 135 | + |
| 136 | +// The parameter list for a function with no parameters should be written with a pair of parentheses. |
| 137 | +() => { statements } |
| 138 | + |
| 139 | +// Parenthesize the body of a function to return an object literal expression: |
| 140 | +params => ({foo: bar}) |
| 141 | + |
| 142 | +// Rest parameters and default parameters are supported |
| 143 | +(param1, param2, ...rest) => { statements } |
| 144 | +(param1 = defaultValue1, param2, …, paramN = defaultValueN) => { |
| 145 | +statements } |
| 146 | + |
| 147 | +// Destructuring within the parameter list is also supported |
| 148 | +var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c; |
| 149 | +f(); // 6 |
| 150 | + |
| 151 | +``` |
| 152 | + |
| 153 | +One of the most expected and misused features of ES6 is the Arrow Function. Undoubtedly powerful it might also derive in a headache if you don't really know how they work and which are the differences between the full body notation and the arrow notation. |
| 154 | + |
| 155 | +Let's take a look at [YDKJS - ES6 & Beyond - chapter 2](https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/es6%20%26%20beyond/ch2.md#arrow-functions) |
| 156 | + |
| 157 | +--- |
| 158 | + |
| 159 | +## Generators |
| 160 | + |
| 161 | +So far we've seen (except for the iterators) only **[run-to-completion](https://en.wikipedia.org/wiki/Run_to_completion_scheduling)** examples of code. It means, "the execution won't stop until it's done or fails". |
| 162 | +What if I tell you there's a feature that let you define a function capable of being paused midway and resumed later? |
| 163 | + |
| 164 | +Together with [`iterators`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterator_protocol) ES6 introduced something called `generators`. |
| 165 | + |
| 166 | +> The Generator object is returned by a [generator function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) and it conforms to both the [iterable protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterable_protocol) and the [iterator protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#The_iterator_protocol). |
| 167 | +
|
| 168 | +There are 2 ways to create a [generator object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) |
| 169 | + |
| 170 | +- [function* expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) |
| 171 | + |
| 172 | + ```javascript |
| 173 | + function* name([param[, param[, ... param]]]) { |
| 174 | + statements |
| 175 | + } |
| 176 | + ``` |
| 177 | + |
| 178 | +- [GeneratorFunction constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/GeneratorFunction) |
| 179 | + |
| 180 | + ```javascript |
| 181 | + let GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor |
| 182 | + let myGenerator = new GeneratorFunction ([arg1[, arg2[, ...argN]],] functionBody) |
| 183 | + ``` |
| 184 | + |
| 185 | +> **Note** that GeneratorFunction is **not a global object**. |
| 186 | +> |
| 187 | +> `generator` function objects created with the `GeneratorFunction` constructor are parsed when the function is created. This is less efficient than declaring a generator function with a `function* expression` and calling it within your code, because such functions are parsed with the rest of the code. |
| 188 | +> |
| 189 | +> Source: [MDN GeneratorFunction](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/GeneratorFunction) |
| 190 | +
|
| 191 | +Since this is a particularly complex topic, with several nuances, let's try to understand them through examples: |
| 192 | + |
| 193 | +### Example of execution sequence |
| 194 | + |
| 195 | +```javascript |
| 196 | +/** |
| 197 | + * |
| 198 | + * @param {number} initialValue |
| 199 | + * @returns {Object} Generator |
| 200 | + */ |
| 201 | +function* bottlesOfBeer (initialValue) { |
| 202 | + let bob = initialValue; |
| 203 | + let lastMessage = `No more bottles of beer on the wall, no more bottles of beer. |
| 204 | +Go to the store and buy some more, ${bob} bottles of beer on the wall.`; |
| 205 | + |
| 206 | + while (true) { |
| 207 | + console.log(`${bob} bottles of beer on the wall, ${bob} bottles of beer.`); |
| 208 | + |
| 209 | + yield bob--; |
| 210 | + |
| 211 | + console.log(`Take one down and pass it around, ${bob} bottles of beer on the wall.`); |
| 212 | + |
| 213 | + if (bob < 1) { |
| 214 | + bob = initialValue; |
| 215 | + console.log(lastMessage); |
| 216 | + } |
| 217 | + } |
| 218 | +} |
| 219 | + |
| 220 | +let bob = bottlesOfBeer(100); |
| 221 | + |
| 222 | +bob.next(); |
| 223 | +// log -> 5 bottles of beer on the wall, 5 bottles of beer. |
| 224 | +// statement completion value -> {value: 5, done: false} |
| 225 | + |
| 226 | +bob.next(); |
| 227 | +// log -> Take one down and pass it around, 4 bottles of beer on the wall. |
| 228 | +// 4 bottles of beer on the wall, 4 bottles of beer. |
| 229 | +// statement completion value -> {value: 4, done: false} |
| 230 | + |
| 231 | +// ... 3 more calls |
| 232 | + |
| 233 | +bob.next(); |
| 234 | +// log -> Take one down and pass it around, 0 bottles of beer on the wall. |
| 235 | +// No more bottles of beer on the wall, no more bottles of beer. |
| 236 | +// Go to the store and buy some more, 5 bottles of beer on the wall. |
| 237 | +// 5 bottles of beer on the wall, 5 bottles of beer. |
| 238 | +// statement completion value -> {value: 5, done: false} |
| 239 | + |
| 240 | +// guess what happens now? |
| 241 | + |
| 242 | +``` |
| 243 | + |
| 244 | +### Passing values through `next` |
| 245 | + |
| 246 | +```javascript |
| 247 | +/** |
| 248 | + * |
| 249 | + * @returns {Object} Generator |
| 250 | + */ |
| 251 | +function* passingValToNext () { |
| 252 | + let val = 10; |
| 253 | + |
| 254 | + while (true) { |
| 255 | + console.log(`UP val=${val}`); |
| 256 | + |
| 257 | + val = yield val + 10; |
| 258 | + |
| 259 | + console.log(`DOWN val=${val}`); |
| 260 | + } |
| 261 | +} |
| 262 | + |
| 263 | +let pvtn = passingValToNext(); |
| 264 | +// statement completion value -> passingValToNext {<suspended>} |
| 265 | + |
| 266 | +pvtn.next(2); |
| 267 | +// log -> UP val=10 |
| 268 | +// statement completion value -> {value: 20, done: false} |
| 269 | + |
| 270 | +pvtn.next(7); |
| 271 | +// log -> DOWN val=7 |
| 272 | +// log -> UP val=7 |
| 273 | +// statement completion value -> {value: 17, done: false} |
| 274 | + |
| 275 | +// WAIT! WHAT??!!!! |
| 276 | +// how does it work? |
| 277 | + |
| 278 | +``` |
| 279 | + |
| 280 | +### Sample combining initial value and passing value to next |
| 281 | + |
| 282 | +```javascript |
| 283 | +/** |
| 284 | + * |
| 285 | + * @param {Number} expectedTotal |
| 286 | + * @returns {Object} Generator |
| 287 | + */ |
| 288 | +function* calculateDownloadProgress (expectedTotal) { |
| 289 | + let totalDownloaded = 0; |
| 290 | + let newItems = 0; |
| 291 | + |
| 292 | + while (true) { |
| 293 | + totalDownloaded += newItems || 0; // lazy verification for the value passed by `next` |
| 294 | + |
| 295 | + let percent = ((totalDownloaded / expectedTotal) * 100).toFixed(2); |
| 296 | + |
| 297 | + newItems = yield `${percent}%`; |
| 298 | + } |
| 299 | +} |
| 300 | + |
| 301 | +let progress = calculateDownloadProgress(1024); |
| 302 | +// statement completion value -> undefined |
| 303 | +progress.next() |
| 304 | +// statement completion value -> {value: "0.00%", done: false} |
| 305 | +progress.next(15) |
| 306 | +// statement completion value -> {value: "1.46%", done: false} |
| 307 | +progress.next(500) |
| 308 | +// statement completion value -> {value: "50.29%", done: false} |
| 309 | +``` |
| 310 | + |
| 311 | +### DIY |
| 312 | + |
| 313 | +```javascript |
| 314 | +/** |
| 315 | + * |
| 316 | + * @returns {Object} Generator |
| 317 | + */ |
| 318 | +function* spinGen() { |
| 319 | + while(true){ |
| 320 | + yield* ['\\', '|', '/', '--']; |
| 321 | + } |
| 322 | +} |
| 323 | + |
| 324 | +// now you add the code to see the output |
| 325 | +``` |
| 326 | + |
| 327 | +Let's take some time to read and discuss: |
| 328 | + |
| 329 | +- [ECMAScript Generator Function](https://www.ecma-international.org/ecma-262/6.0/#sec-generatorfunction) |
| 330 | +- [YDKJS - ES6 & Beyond - CH3 - Generators](https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/es6%20%26%20beyond/ch3.md#generators) - by Kyle Simpson |
| 331 | +- [The Basics Of ES6 Generators](https://davidwalsh.name/es6-generators) - By Kyle Simpson |
| 332 | +- [2ality - ES6 generators in depth](https://2ality.com/2015/03/es6-generators.html) - by Dr. Axel Rauschmayer |
| 333 | + |
| 334 | +--- |
| 335 | + |
| 336 | +## Exercises |
| 337 | + |
| 338 | +Let's open our test files: |
| 339 | + |
| 340 | +- [this](/src/day_07/this.test.js) |
| 341 | +- [strict mode](/src/day_07/strictMode.test.js) |
| 342 | +- [arrow functions](/src/day_07/arrowFunctions.test.js) |
| 343 | +- [generators](/src/day_07/generators.test.js) |
| 344 | + |
| 345 | +Now open your terminal. |
| 346 | + |
| 347 | +1. Make sure you're at the project location |
| 348 | +2. If you didn't install all the packages yet then run `npm i` for a fresh dependency install, or `npm ci` for an installation based on the lock file. |
| 349 | +3. Type `npm run test:watch`, this will start running your tests every time you make a change. |
| 350 | + |
| 351 | +**Our task is to make ALL our DAY 7 tests pass ;)** |
0 commit comments