What is the difference between 'let' and 'const' ECMAScript 2015 (ES6)?

I'm wondering what is the difference between let and const in ES6. Both of them are block scoped, as the example in the following code:

const PI = 3.14;
console.log(PI);

PI = 3;
console.log(PI);

const PI = 4;
console.log(PI);

var PI = 5;
console.log(PI);

In ES5 the output will be:

3.14
3.14
3.14
3.14

But in ES6 it will be:

3.14
3
4
5

I'm wondering why ES6 allows the change of const value, the question is why should we use 'const' now? we can use 'let' instead?

Note: jsbin can be used for testing, choose JavaScript to run ES5 code and Traceur to run it with ES6 capabilities.


The difference between let and const is that once you bind a value/object to a variable using const, you can't reassign to that variable. In other words Example:

const something = {};
something = 10; // Error.

let somethingElse = {};
somethingElse = 1000; // This is fine.

The question details claim that this is a change from ES5 — this is actually a misunderstanding. Using const in a browser that only supports ECMAScript 5 will always throw an error. The const statement did not exist in ECMAScript 5. The behaviour in is either JS Bin being misleading as to what type of JavaScript is being run, or it’s a browser bug.

In practice, browsers didn't just go from 0% support for ECMAScript 2015 (ECMAScript 6) to 100% in one go — features are added bit-by-bit until the browser is fully compliant. What JS Bin calls ‘JavaScript’ just means whatever ECMAScript features your browser currently supports — it doesn’t mean ‘ES5’ or ‘ES6’ or anything else. Many browsers supported const and let before they fully supported ES6, but some (like Firefox) treated const like let for some time. It is likely that the question asker’s browser supported let and const but did not implement them correctly.

Secondly, tools like Babel and Traceur do not make ES6 ‘run’ in an older browser — they instead turn ES6 code into ES5 that does approximately the same thing. Traceur is likely turning const statements into var statements, but I doubt it is always enforcing that the semantics of a const statement are exactly replicated in ES5. Using JS Bin to run ES6 using Traceur is not going to give exactly the same results as running ES6 in a fully ES6 specification-compliant browser.


It is important to note that const does not make a value or object immutable.

const myArray = [];
myArray.push(1); // Works fine.
myArray[1] = 2; // Also works fine.
console.log(myArray); // [1, 2]
myArray = [1, 2, 3] // This will throw.

Probably the best way to make an object (shallowly) immutable at the moment is to use Object.freeze() on it. However, this only makes the object itself read-only; the values of the object’s properties can still be mutated.


What you're seeing is just an implementation mistake. According to the ES6 spec wiki on const, const is:

A initialize-once, read-only thereafter binding form is useful and has precedent in existing implementations, in the form of const declarations.

It's meant to be read-only, just like it currently is. The ES6 implementation of const in Traceur and Continuum are buggy (they probably just overlooked it)

Here's a Github issue regarding Traceur not implementing const


let

  • Use block scope in programming.
  • for every block let create its own new scope which you cannot access in outside of that block.
  • value can be changed as many times as you want.
  • let is extremely useful to have for the vast majority of code. It can greatly enhance your code readability and decrease the chance of a programming error.

    let abc = 0;
    
    if(true)
     abc = 5 //fine
    
    if(true){
      let def = 5
    }
    console.log(def)
    

const

  • It allows you to be immutable with variables.
  • const is a good practice for both readability and maintainability and avoids using magic literals e.g.

    // Low readability
    if (x > 10) {
    }
    
    //Better!
    const maxRows = 10;
    if (x > maxRows) {
     }
    
  • const declarations must be initialized

     const foo; // ERROR: const declarations must be initialized
    
  • A const is block scoped like we saw with let:+
const foo = 123;
if (true) {
    const foo = 456; // Allowed as its a new variable limited to this `if` block
}

Summary:

Both the let and the const keyword are ways to declare block scoped variables. There is one big difference though:

  • Variables declared with let can be reassigned.
  • Variables declared with const have to be initialized when declared and can't be reassigned.

If you try to reassign variables with declared with the const keyword you will get the following error (chrome devtools):

const reassignment error

Why should we use this?

If we know that we want to assign a variable once and that we don't want to reassign the variable, using the const keywords offers the following advantages:

  • We communicate in our code that we don't want to reassign the variable. This way if other programmmers look to your code (or even you to your own code you wrote a while ago) you know that the variables which are declared with const should not be reassigned. This way our code becomes more declarative and easier to work with.
  • We force the principle of not being able to reassign a variable (JS engine throws error). This way if you accidentally try to reassign a variable which is not meant to be reassigned you can detect this at an earlier stage (because it's logged to the console).

Caveat:

Although a variable declared with const can't be reassigned this doesn't mean that an assigned object isn't mutable. For example:

const obj = {prop1: 1}

// we can still mutate the object assigned to the 
// variable declared with the const keyword
obj.prop1 = 10;
obj.prop2 = 2;

console.log(obj);

If you also want your object to be non mutable you can use Object.freeze() in order to achieve this.