class: left, middle background-image: url(../../images/master-folie.png) .title[ ## Let, const, ## temporal dead zone
### Oleg Varaksin #### ovaraksin@googlemail.com ] --- # Variables declared with var (1/3). *Variables declared with __var__ are __hoisted__ to the top.* What does it mean? Hoisting is a JavaScript's default behavior of moving all declarations to the top of the current scope. The current scope can be the current script or the current function. Example: ```js var a = 1; console.log(a); // 1 ``` The variable `a` is declared as global (as expected). ```js if (a === 1) { var b = 2; } console.log(b); // 2 ``` Variable `b` is inside an `if` block, but in JavaScript it doesn't create a new scope. It's declared as global too. --- # Variables declared with var (2/3). ```js for (var c = 0; c < 3; c++) { ... } console.log(c); // 3 ``` The variable `c` is declared in a `for` loop, but it is also lies in the global scope. ```js function doSomething() { var d = 4; } console.log(d); // ReferenceError: d is not defined ``` The variable `d` is declared in his own scope. It's inside a function and only function creates new scopes. --- # Variables declared with var (3/3). Another example of __hoisting__: ```js function doSomething (name) { if (name === 'nico') { var awesome = true; } return awesome; } doSomething('nico') // <- true doSomething('marc') // <- undefined ``` Behind the scenes, the JavaScript engine changes the function to look like this ```js function doSomething (name) { var awesome; if (name === 'nico') { awesome = true; } return awesome; } ``` --- # Let declaration (1/2). The `let` declaration syntax is the same as the syntax for `var`, but it limits the variable's scope to only the current code block. Previous examples, but with `let`: ```js let a = 1; if (a === 1) { let b = 2; } for (let c = 0; c < 3; c++) { ... } function doSomething() { let d = 4; } console.log(a); // 1 console.log(b); // ReferenceError: b is not defined console.log(c); // ReferenceError: c is not defined console.log(d); // ReferenceError: d is not defined ``` --- # Let declaration (2/2). Quick quiz. ```js let a = 2; if (a > 1) { let b = a * 3; console.log(b); // 6 for (let i = a; i <= b; i++) { let j = i + 10; ... } let c = a + b; console.log(c); // 8 } ``` Which variable(s) exist only inside the `if` statement, and which variable(s) exist only inside the `for` loop? -- __Answer__: the `if` statement contains `b` and `c` block-scoped variables, and the `for` loop contains `i` and `j` block-scoped variables. --- # Let declarations in loops (1/2). ```js var funcs = []; for (let i = 0; i < 5; i++) { funcs.push(function(){ console.log(i); }); } funcs[3](); // 3 ``` The `let i` in the `for` header redeclares a new `i` for each iteration of the loop. In other words, on each iteration, the loop creates a new variable `i` and the closures are created inside the loop iteration. The same snippet, but with `var i` in the `for` loop header, would get `5` instead of `3`, because there would only be one `i` in the outer scope which had the last assigned value `i`. __Note:__ `let` also works the same way with `for..in` and `for..of` loops. --- # Let declarations in loops (2/2). Example for `for..in` loop: ```js var funcs = [], object = {a: true, b: true, c: true}; for (let key in object) { funcs.push(function() { console.log(key); }); } funcs.forEach(function(func) { func(); // outputs "a", then "b", then "c" }); ``` Each time through the loop, a new `key` binding is created, and so each function has its own copy of the `key` variable. The result is that each function outputs a different value. If `var` were used to declare `key`, all functions would output `c`. --- # Const declaration (1/3). `const` declaration creates constants. It is a variable that is read-only after its initial value is set. ```js const a = 2; console.log(a); // 2 a = 3; // SyntaxError: "a" is read-only ``` Values of variables declared using `const` cannot be changed once set. For this reason, every `const` variable must be initialized on declaration: ```js // valid constant const maxItems = 30; // SyntaxError: missing initialization const name; ``` --- # Const declaration (2/3). Constants are not a restriction on the value itself, but on the variable's assignment of that value. In other words, the value is not frozen or immutable because of `const`, just the assignment of it. If the value is complex, such as an object or array, the contents of the value can still be modified. Example: ```js const a = [1,2,3]; a.push(4); console.log(a); // [1,2,3,4] a = 42; // SyntaxError ``` Constants, like `let` declarations, are block-level declarations. ```js if (condition) { const maxItems = 5; ... } // maxItems isn't accessible here ``` --- # Const declaration (3/3). Constans can be used in loops too. ```js var funcs = [], object = {a: true, b: true, c: true}; // doesn't cause an error due to a new binding on each iteration for (const key in object) { funcs.push(function() { console.log(key); }); } funcs.forEach(function(func) { func(); // outputs "a", then "b", then "c" }); ``` __Attention:__ But the value of `key` cannot be changed inside the loop. --- # Redeclaration pitfall. If an identifier has already been defined in a scope, then using the identifier in a `let` or `const` declaration inside that scope causes an error to be thrown. ```js var count = 30; // SyntaxError is thrown, // because count is declared twice within the same block let count = 40; ``` But the next `let` declaration doesn't throw an error. ```js var count = 30; if (condition) { let count = 40; } ``` Why? Because it creates a new variable `count` within the `if` statement block, instead of creating `count` in the surrounding block. Inside the `if` block, this new variable shadows the global `count`, preventing access to it until execution leaves the block. --- # The Temporal Dead Zone (1/2). The `let` and `const` declarations are not hoisted to the top of the enclosing block. A variable declared with either cannot be accessed before the declaration. Attempting to do so results in a reference error. ```js if (condition) { console.log(typeof value); // ReferenceError! let value = "blue"; // TDZ } ``` The line with `let value = "blue"` is never executed because the previous line throws an error. But the next code snippet works without errors because `typeof` check occurs outside of the block in which `value` is declared. ```js console.log(typeof value); // "undefined" if (condition) { let value = "blue"; } ``` There is no `value` binding ouside of the block, and `typeof` simply returns `undefined`. --- # The Temporal Dead Zone (2/2). __Memorize again and notice the difference__: Accessing a `let`-declared variable earlier than its `let` declaration / initialization causes an error, whereas with `var` declarations the ordering doesn't matter. ```js { console.log(a); // undefined console.log(b); // ReferenceError! var a; let b; } ``` --- class: center, middle, inverse # That's all folks, thanks for your attention! .back[[Back to the homepage](http://ova2.github.io/es6-presentations/)] Slideshow created with [remark](https://github.com/gnab/remark)