<h1 id="javascript-let-declaring-block-scoped-variables">JavaScript let: Declaring Block-Scoped Variables</h1>
<p><strong>Summary</strong>: in this tutorial, you will learn how to use the JavaScript <code>let</code> keyword to declare block-scoped variables.</p>
<h2 id="introduction-to-the-javascript-let-keyword">Introduction to the JavaScript let keyword</h2>
<p>In ES5, when you <a href="https://www.javascripttutorial.net/javascript-variables/">declare a variable</a> using the <code>var</code> keyword, the scope of the variable is either global or local. If you declare a variable outside of a function, the scope of the variable is global. When you declare a variable inside a function, the scope of the variable is local.</p>
<p>ES6 provides a new way of declaring a variable by using the <code>let</code> keyword. The <code>let</code> keyword is similar to the <code>var</code> keyword, except that these variables are blocked-scope. For example:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">let</span> variable_name;<span class="hljs-title class_">Code</span> <span class="hljs-attr">language</span>: <span class="hljs-title class_">JavaScript</span> (javascript)
</code></pre>
<p>In JavaScript, blocks are denoted by curly braces <code>{}</code> , for example, the <code>if else</code>, <code>for</code>, <code>do while</code>, <code>while</code>, <code>try catch</code> and so on:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">if</span>(condition) {
<span class="hljs-comment">// inside a block</span>
}<span class="hljs-title class_">Code</span> <span class="hljs-attr">language</span>: <span class="hljs-title class_">JavaScript</span> (javascript)
</code></pre>
<p>See the following example:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">let</span> x = <span class="hljs-number">10</span>;
<span class="hljs-keyword">if</span> (x == <span class="hljs-number">10</span>) {
<span class="hljs-keyword">let</span> x = <span class="hljs-number">20</span>;
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(x); <span class="hljs-comment">// 20: reference x inside the block</span>
}
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(x); <span class="hljs-comment">// 10: reference at the begining of the scriptCode language: JavaScript (javascript)</span>
</code></pre>
<p>How the script works:</p>
<ul>
<li>First, declare a variable <code>x</code> and initialize its value to 10.</li>
<li>Second, declare a new variable with the same name <code>x</code> inside the <code>if</code> block but with an initial value of 20.</li>
<li>Third, output the value of the variable <code>x</code> inside and after the <a href="https://www.javascripttutorial.net/javascript-if-else/"><code>if</code></a> block.</li>
</ul>
<p>Because the <code>let</code> keyword declares a block-scoped variable, the <code>x</code> variable inside the <code>if</code> block is a <strong>new variable</strong> and it shadows the <code>x</code> variable declared at the top of the script. Therefore, the value of <code>x</code> in the console is <code>20</code>.</p>
<p>When the JavaScript engine completes executing the <code>if</code> block, the <code>x</code> variable inside the <code>if</code> block is out of scope. Therefore, the value of the <code>x</code> variable that following the <code>if </code>block is 10.</p>
<h2 id="javascript-let-and-global-object">JavaScript let and global object</h2>
<p>When you declare a global variable using the <code>var</code> keyword, you add that variable to the property list of the <a href="https://www.javascripttutorial.net/es-next/javascript-globalthis/">global object</a>. In the case of the web browser, the global object is the <code>window</code>. For example:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">var</span> a = <span class="hljs-number">10</span>;
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-variable language_">window</span>.<span class="hljs-property">a</span>); <span class="hljs-comment">// 10Code language: JavaScript (javascript)</span>
</code></pre>
<p>However, when you use the <code>let</code> keyword to declare a variable, that variable is <strong>not</strong> attached to the global object as a property. For example:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">let</span> b = <span class="hljs-number">20</span>;
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-variable language_">window</span>.<span class="hljs-property">b</span>); <span class="hljs-comment">// undefinedCode language: JavaScript (javascript)</span>
</code></pre>
<h2 id="javascript-let-and-callback-function-in-a-for-loop">JavaScript let and callback function in a for loop</h2>
<p>See the following example.</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; i++) {
<span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(i);
}, <span class="hljs-number">1000</span>);
}<span class="hljs-title class_">Code</span> <span class="hljs-attr">language</span>: <span class="hljs-title class_">JavaScript</span> (javascript)
</code></pre>
<p>The intention of the code is to output numbers from 0 to 4 to the console every second. However, it outputs the number <code>5</code> five times:</p>
<pre><code class="hljs language-js"><span class="hljs-number">5</span>
<span class="hljs-number">5</span>
<span class="hljs-number">5</span>
<span class="hljs-number">5</span>
<span class="hljs-number">5</span>
</code></pre>
<p>In this example, the variable <code>i</code> is a global variable. After the loop, its value is 5. When the callback functions are passed to the <code>setTimeout()</code> function executes, they reference the same variable <code>i</code> with the value 5.</p>
<p>In ES5, you can fix this issue by creating another scope so that each callback function references a new variable. And to create a new scope, you need to create a function. Typically, you use the <a href="https://www.javascripttutorial.net/javascript-immediately-invoked-function-expression-iife/">IIFE</a> pattern as follows:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; i++) {
(<span class="hljs-keyword">function</span> (<span class="hljs-params">j</span>) {
<span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(j);
}, <span class="hljs-number">1000</span>);
})(i);
}<span class="hljs-title class_">Code</span> <span class="hljs-attr">language</span>: <span class="hljs-title class_">JavaScript</span> (javascript)
</code></pre>
<p>Output:</p>
<pre><code class="hljs language-js"><span class="hljs-number">0</span>
<span class="hljs-number">1</span>
<span class="hljs-number">2</span>
<span class="hljs-number">3</span>
<span class="hljs-number">4</span>
</code></pre>
<p>In ES6, the <code>let</code> keyword declares a new variable in each loop iteration. Therefore, you just need to replace the <code>var</code> keyword with the <code>let</code> keyword to fix the issue:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; i++) {
<span class="hljs-built_in">setTimeout</span>(<span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(i);
}, <span class="hljs-number">1000</span>);
}<span class="hljs-title class_">Code</span> <span class="hljs-attr">language</span>: <span class="hljs-title class_">JavaScript</span> (javascript)
</code></pre>
<p>To make the code completely ES6 style, you can use an <a href="https://www.javascripttutorial.net/es6/javascript-arrow-function/">arrow function</a> as follows:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; i++) {
<span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(i), <span class="hljs-number">1000</span>);
}<span class="hljs-title class_">Code</span> <span class="hljs-attr">language</span>: <span class="hljs-title class_">JavaScript</span> (javascript)
</code></pre>
<p>Note that you’ll learn more about the <a href="https://www.javascripttutorial.net/es6/javascript-arrow-function/">arrow functions in the later tutorial</a>.</p>
<h2 id="redeclaration">Redeclaration</h2>
<p>The <code>var</code> keyword allows you to redeclare a variable without any issue:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">var</span> counter = <span class="hljs-number">0</span>;
<span class="hljs-keyword">var</span> counter;
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(counter); <span class="hljs-comment">// 0Code language: JavaScript (javascript)</span>
</code></pre>
<p>However, redeclaring a variable using the <code>let</code> keyword will result in an error:</p>
<pre><code class="hljs language-js"><span class="hljs-keyword">let</span> counter = <span class="hljs-number">0</span>;
<span class="hljs-keyword">let</span> counter;
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(counter);<span class="hljs-title class_">Code</span> <span class="hljs-attr">language</span>: <span class="hljs-title class_">JavaScript</span> (javascript)
</code></pre>
<p>Here’s the error message:</p>
<pre><code class="hljs language-js"><span class="hljs-title class_">Uncaught</span> <span class="hljs-title class_">SyntaxError</span>: <span class="hljs-title class_">Identifier</span> <span class="hljs-string">'counter'</span> has already been declaredCode <span class="hljs-attr">language</span>: <span class="hljs-title class_">JavaScript</span> (javascript)
</code></pre>
<h2 id="javascript-let-variables-and-hoisting">JavaScript let variables and hoisting</h2>
<p>Let’s examine the following example:</p>
<pre><code class="hljs language-js">{
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(counter); <span class="hljs-comment">// </span>
<span class="hljs-keyword">let</span> counter = <span class="hljs-number">10</span>;
}<span class="hljs-title class_">Code</span> <span class="hljs-attr">language</span>: <span class="hljs-title class_">JavaScript</span> (javascript)
</code></pre>
<p>This code causes an error:</p>
<pre><code class="hljs language-js"><span class="hljs-title class_">Uncaught</span> <span class="hljs-title class_">ReferenceError</span>: <span class="hljs-title class_">Cannot</span> access <span class="hljs-string">'counter'</span> before initializationCode <span class="hljs-attr">language</span>: <span class="hljs-title class_">JavaScript</span> (javascript)
</code></pre>
<p>In this example, accessing the <code>counter</code> variable before declaring it causes a <code>ReferenceError</code>. You may think that a variable declaration using the <code>let</code> keyword does not <strong>hoist,</strong> but it does**.**</p>
<p>In fact, the JavaScript engine will hoist a variable declared by the <code>let</code> keyword to the top of the block. However, the JavaScript engine does not initialize the variable. Therefore, when you reference an uninitialized variable, you’ll get a <code>ReferenceError</code>.</p>
<h2 id="temporal-death-zone-tdz">Temporal death zone (TDZ)</h2>
<p>A variable declared by the <code>let</code> keyword has a so-called temporal dead zone (TDZ). The TDZ is the time from the start of the block until the variable declaration is processed.</p>
<p>The following example illustrates that the temporal dead zone is time-based, not location-based*.*</p>
<pre><code class="hljs language-js">{ <span class="hljs-comment">// enter new scope, TDZ starts</span>
<span class="hljs-keyword">let</span> log = <span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) {
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(message); <span class="hljs-comment">// messagedeclared later</span>
};
<span class="hljs-comment">// This is the TDZ and accessing log</span>
<span class="hljs-comment">// would cause a ReferenceError</span>
<span class="hljs-keyword">let</span> message= <span class="hljs-string">'Hello'</span>; <span class="hljs-comment">// TDZ ends</span>
<span class="hljs-title function_">log</span>(); <span class="hljs-comment">// called outside TDZ</span>
}<span class="hljs-title class_">Code</span> <span class="hljs-attr">language</span>: <span class="hljs-title class_">JavaScript</span> (javascript)
</code></pre>
<p>In this example:</p>
<p>First, the curly brace starts a new block scope, therefore, the TDZ starts.</p>
<p>Second, the <code>log()</code> function expression accesses the <code>message</code> variable. However, the <code>log()</code> function has not been executed yet.</p>
<p>Third, declare the <code>message</code> variable and initialize its value to 10. The time from the start of the block scope to the time that the <code>message</code> variable is accessed is called a <em>temporal death zone</em>. When the JavaScript engine processes the declaration, the TDZ ends.</p>
<p>Finally, call the <code>log()</code> function that accesses the <code>message</code> variable outside of the TDZ.</p>
<p>Note that if you access a variable declared by the <code>let</code> keyword in the TDZ, you’ll get a <code>ReferenceError</code> as illustrated in the following example.</p>
<pre><code class="hljs language-js">{ <span class="hljs-comment">// TDZ starts</span>
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-keyword">typeof</span> myVar); <span class="hljs-comment">// undefined</span>
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-keyword">typeof</span> message); <span class="hljs-comment">// ReferenceError</span>
<span class="hljs-keyword">let</span> message; <span class="hljs-comment">// TDZ ends</span>
}<span class="hljs-title class_">Code</span> <span class="hljs-attr">language</span>: <span class="hljs-title class_">JavaScript</span> (javascript)
</code></pre>
<p>Notice that <code>myVar</code> variable is a non-existing variable, therefore, its type is <a href="https://www.javascripttutorial.net/javascript-data-types/#undefined">undefined</a>.</p>
<p>The temporal death zone prevents you from accidentally referencing a variable before its declaration.</p>
<h3 id="summary">Summary</h3>
<ul>
<li>Variables are declared using the <code>let</code> keyword are block-scoped, are not initialized to any value, and are not attached to the global object.</li>
<li>Redeclaring a variable using the <code>let</code> keyword will cause an error.</li>
<li>A temporal dead zone of a variable declared using the <code>let</code> keyword starts from the block until the initialization is evaluated.</li>
</ul>