# Getting Started with ES6: let, const, and Arrow Functions

> An introduction to the most impactful ES6 features — let, const, arrow functions, and template literals — with practical examples.

- Published: 2015-07-05
- Category: Languages
- Tags: JavaScript
- Reading time: 3 min read
- Source: https://www.muhammetsafak.com.tr/en/blog/getting-started-with-es6-let-const-and-arrow-functions/
- Language: en-US
- Author: Muhammet Şafak

---
In June, **ES6 (ECMAScript 2015)** was officially finalized. The ECMAScript specification, which defines the core standard for JavaScript, just received its first major release since ES5 back in 2009. Six years in the making. For most of those six years I wrote JavaScript through jQuery; [the post on sending data without a page reload](/en/blog/sending-data-without-page-reload-jquery-ajax/) is a typical example of that era.

Browser support is not yet complete — older browsers in particular have issues. But by using **Babel** (a transpiler), you can convert ES6 code down to ES5, which means you can write modern JavaScript and still run it in older browsers. In this post I'll cover the three additions that make the biggest day-to-day difference: `let`/`const`, arrow functions, and template literals.

## The problem with var, and let/const

For a long time in JavaScript, `var` was the only way to declare a variable. It has two notable characteristics: it is function-scoped, and it is hoisted. These traits can lead to surprising behavior:

```javascript
for (var i = 0; i < 3; i++) {
    setTimeout(function() {
        console.log(i); // Prints 3 every time
    }, 100);
}
```

Because `var i` belongs to the surrounding function rather than the loop block, by the time `setTimeout` fires, `i` is already 3.

**`let`** is block-scoped: it lives only within the `{}` block where it is declared.

```javascript
for (let i = 0; i < 3; i++) {
    setTimeout(function() {
        console.log(i); // 0, 1, 2 — the expected behavior
    }, 100);
}
```

**`const`** is also block-scoped, but with one extra constraint: once assigned, it cannot be reassigned. Using it for values that should not change makes the intent of the code explicit.

```javascript
const API_URL = 'https://api.ornek.com';
// API_URL = 'something else'; // TypeError

const kullanici = { isim: 'Ali' };
kullanici.soyisim = 'Veli'; // Valid — the object's contents can change
// kullanici = {};           // Invalid — you cannot reassign the reference
```

The important distinction is that `const` freezes the *reference*, not the object itself. The object's properties can still be mutated; what you cannot do is point `kullanici` at something else entirely.

The rule I use in practice: declare everything with `const` first, and switch to `let` only when I genuinely need to reassign. Never go back to `var`. This habit lets anyone reading the code answer the question "does this value change later?" at a glance.

## Arrow Functions

One of the most useful additions in ES6. The syntax is more concise, and `this` binding works differently.

Old syntax:

```javascript
var kareler = [1, 2, 3, 4].map(function(sayi) {
    return sayi * sayi;
});
```

With an arrow function:

```javascript
const kareler = [1, 2, 3, 4].map(sayi => sayi * sayi);
```

In the concise form, if there is a single expression, you can omit `return` and the curly braces. Multiple parameters require parentheses:

```javascript
const topla = (a, b) => a + b;
const selamla = isim => `Merhaba, ${isim}!`;
```

On the topic of `this` binding — arrow functions do not define their own `this`. They inherit it from the enclosing scope. This solves a very common problem when accessing object properties inside a callback:

```javascript
function Zamanlayici() {
    this.saniye = 0;

    setInterval(() => {
        this.saniye++; // Correct `this` — the Zamanlayici instance
        console.log(this.saniye);
    }, 1000);
}
```

With the old approach, you would have written a workaround like `var self = this;`. Arrow functions make that unnecessary.

That said, arrow functions are not appropriate everywhere. When used as a method on an object, `this` does not behave as expected — because the arrow function does not bind `this`; it inherits it from the outer scope. In those cases, a traditional `function` expression is still the right choice.

## Template Literals

String concatenation with `+` often produced hard-to-read code:

```javascript
var mesaj = 'Merhaba ' + isim + ', hesabınızda ' + bakiye + ' TL var.';
```

With ES6 template literals, you embed variables directly inside backtick strings:

```javascript
const mesaj = `Merhaba ${isim}, hesabınızda ${bakiye} TL var.`;
```

Multi-line strings are now natural as well:

```javascript
const html = `
  <div class="kart">
    <h2>${baslik}</h2>
    <p>${aciklama}</p>
  </div>
`;
```

Previously you would have dealt with a mess of `\n` and `+` operators.

The `${}` placeholder accepts any JavaScript expression, not just variables — things like `${sayi * 2}` or `${kullanici ? kullanici.isim : 'Misafir'}`. That flexibility is especially useful when generating dynamic content.

## Where to start?

Using ES6 without Babel today is still risky due to inconsistent browser support. But if you have Babel set up with a build step (via Gulp or Webpack), writing ES6 is completely viable. I started using `let`/`const` and arrow functions with Babel and it took only a few days to feel natural.

Letting go of old JavaScript habits takes a little time. Start with `const`, fall back to `let` when you actually need reassignment — once I committed to that rule, I found myself asking "does this value change?" far less often when reading code.

ES6 is not something you learn all at once. Beginning with `let`/`const` and arrow functions, then adding template literals — these three features have an immediate impact on everyday code. The rest — destructuring, spread, modules — comes with time. There is no rush; even just these three features noticeably improve code readability.
