[front end development] talk about the let and const commands in the core foundation of ES6

Posted by complex05 on Thu, 17 Feb 2022 14:32:10 +0100

ECMAScript 6 Introduction

ECMAScript 6.0 (ES6 for short) is the next generation standard of JavaScript language, which has been officially released in June 2015. Its goal is to make JavaScript language can be used to write complex large-scale applications and become an enterprise development language. Over the past few years, the latest versions of major browsers have increasingly supported ES6, and most of the features of ES6 have been realized.

And we all know Node JS is the server running environment of JavaScript language, Node JS has higher support for ES6 than browsers. Through Node, we can experience more features of ES6. Use the version management tool nvm to install Node. The advantage is that you can switch versions freely.

Babel converter

Babel is a widely used ES6 transcoder, which can convert ES6 code into ES5 code, that is, you can program in the way of ES6 and execute in your existing environment without worrying about whether the existing environment supports it. example:

// Before conversion
input.map(number => number + 1);

// After conversion
input.map(function (number) {
  return number + 1;
});

Principle: the above original ES6 code uses the arrow function. This feature has not been widely supported. The Babel converter can convert it into an ordinary function and execute it in the existing JavaScript environment.

Configuration file babelrc

Babel's configuration file is babelrc, which is stored in the root directory of the project. The first step in using Babel is to configure this file.
This file is used to set transcoding rules and plug-ins. The basic format is as follows:

{
  "presets": [],
  "plugins": []
}

The presets field is used to set transcoding rules. The following rule sets are officially provided and can be installed as needed:

# ES2015 transcoding rules
$ npm install --save-dev babel-preset-es2015

# react transcoding rules
$ npm install --save-dev babel-preset-react

# ES7 transcoding rules for syntax proposals in different stages (there are 4 stages in total), one optional
$ npm install --save-dev babel-preset-stage-0
$ npm install --save-dev babel-preset-stage-1
$ npm install --save-dev babel-preset-stage-2
$ npm install --save-dev babel-preset-stage-3

Then add the installed rules to the configuration file: babelrc:

  {
    "presets": [
      "es2015",
      "react",
      "stage-1"
    ],
    "plugins": []
  }

ES6 let and Const commands

let command

ES6 adds the let command to declare variables. Its usage is similar to that of var in es5, but its declared variables make it valid only in the code where the let command is located, that is, the block level scope, which also makes up for the defect of var in es5. If this code block is exceeded, an error will be reported.
For example:

The above code declares two variables with let and var respectively in the code block. Then call these two variables outside the code block. As a result, the variable declared by let reports an error, and the variable declared by var returns the correct value. This shows that the variables declared by the let are valid only in the code block in which it is located.

Circular scope

Use let

for (let i = 0; i < 10; i++) {}

console.log(i);  //Error reporting: ReferenceError: i is not defined

The counter i is only valid in the for circulatory system. If it is referenced outside the circulatory system, an error will be reported.

Use var

  for(var i = 0;i<10;i++){
    console.log(i);  // 0,1,2,3,4,5,6,7,8,9
  }

  console.log(i);  // 0,1,2,3,4,5,6,7,8,9,10

In the above code, the variable i is declared by var and is valid in the global scope. Therefore, in each cycle, the new i value will overwrite the old value, resulting in the final output of the last round of i value.

There is no variable promotion

In JavaScript, variables defined by var keyword can be declared after use, that is, variables can be used first and then declared, which is the so-called "variable promotion". Unlike var, let does not have the phenomenon of "variable promotion" and cannot be declared after use. Therefore, variables must be used after declaration, otherwise an error will be reported.

console.log(info);  // Output undefined
var info = "var Keyword defined variables can be declared after use";


console.log(info2);  //Error: cannot access' info2 'before initialization
let info2 = "let Keyword defined variables cannot be declared after use";

Duplicate declarations are not allowed

let cannot declare the same variable repeatedly within the same scope:

  • In the same scope or block level scope, let keyword cannot be used to reset the variables declared by var keyword;
// report errors
function(){
  var x = 10;
  let x= 20;
}

  • In the same scope or block level scope, the let keyword cannot be used to reset the variables declared by the let keyword;
// report errors
function(){
  let x = 10;
  let x = 20;
}


  • In the same scope or block level scope, the var keyword cannot be used to reset the variables declared by the let keyword.
// report errors
function(){
  let x = 10;
  var x = 20;
}

let keyword can be redeclared and assigned in different scopes or different block level scopes.

// No error reporting
let x = 10;       

function(){
  let x = 20
}


function func(arg){
  {
    let arg
  }
}

Therefore, you cannot redeclare parameters inside a function.

Block level scope

With regard to block level scopes, why do you need block level scopes? Before ES6, there was no concept of block level scope. ES5 only has global scope and function scope, and there is no block level scope, which brings many scenario application defects.

  • Among them, the first scenario: inner variables may override outer variables.
var date = new Date();

function d(){
  console.log(date);
  if (false){
    var date = "2020-11-21 00:15:34";
  }
}

d();  // Output undefined 

In the above code, after the function d() is executed, the output result is undefined. The reason is that the variable is promoted, resulting in the date variable of the inner layer covering the date variable of the outer layer.

  • The second scenario: the loop variable used to count is leaked as a global variable.
var str = "hello world!";

for (var i=0;i<str.length;i++){
  console.log(str[i]);
}

console.log(i);  // 12

In the above code, the variable i is only used to control the loop, but after the loop ends, it does not disappear and is leaked into a global variable.

Block level scope of ES6

The new let in ES6 adds a block level scope to JavaScript.
The variables declared by let are only valid in the code block {} where the let command is located, and cannot be accessed outside {}.

let i = 1;

function f(){
  let i = 2;
  if (true){
    for(let i=1;i<10;i++){
      console.log(i);
    }
  }
}

console.log(i);  // Output 1

The above function has three code blocks, all of which declare variable i and output 1 after running. Due to the block level scope, the outer code block is not affected by the inner code block.

ES6 allows arbitrary nesting of block level scopes:

{{{{let username = "plum"}}}};

The above code uses a four layer block level scope. And the outer scope cannot read the variables of the inner scope.

{{{
{let username = "plum"}
  console.log(username);  // Error: Uncaught ReferenceError: username is not defined
}}};

Inner scopes can define variables with the same name as outer scopes.

{{{
let username = "plum";
{let username = "plum"}
}}};

The emergence of block level scope actually makes the widely used immediate execution function expression (IIFE) unnecessary and more concise.

// IIFE writing method
(function () {
  var info = ...;
  ...
}());

// Block level scope writing
{
  let info = ...;
  ...
}

Block level scope and function declaration

  • In ES5, js functions can only be declared in the top-level scope and function scope, not in the block level scope. Especially in "strict mode", if it is declared in the block level scope, an error will be reported.
// ES5 strict mode
'use strict';

// report errors
if (true) {
  function f() {}
}

// report errors
try {
  function f() {}
} catch(e) {
}
  • ES6 adds a block level scope, which explicitly allows functions to be declared in the block level scope:
// ES6 strict mode
'use strict';
if (true) {
  function f() {}
}
// No error reporting
  • ES6 stipulates that in the block level scope, the behavior of the function declaration statement is similar to that of the let and cannot be referenced outside the block level scope.
        function f(){
            console.log("I'm outside the block level scope");
        }

         (function(){
             while(true){
                 function f(){
                     console.log("I'm inside the block level scope");
                 }
             }
             f();
         }());  // I'm inside the block level scope

When the above code is run in ES5, you will get "I'm inside the block level scope", because the function f declared in if will be promoted to the function header.

If you use ES6 Texin to run, the result is completely different. The result is still: "I'm outside the block level scope". Because the functions declared in the block level scope are similar to lets and have no impact outside the scope, the actual running code is as follows:

        function f(){
            console.log("I'm outside the block level scope");
        }

         (function(){
             f();
         }());  
  • The block level scope of ES6 allows the rule of declaring functions only when braces are used. If braces are not used, an error will be reported.
    1. Not using {}:
// report errors
'use strict';
if (true)
  function f() {}


2. Use {}:

//Do not report errors. Note that in es6 browser, if in es5 browser, errors will still be reported in strict mode
'use strict';
if (true) {
  function f() {}
}

Summary of block level scope and function declarations:

  • Allows functions to be declared within a block level scope.
  • Function declarations are similar to var, that is, they are promoted to the head of the global scope or function scope.
  • At the same time, the function declaration is also promoted to the head of the block level scope.
    The above is only valid for browsers that support ES6. In other environments, the function declaration of block level scope can be treated as let.

Due to the difference of environment, there may be many errors in declaring a function in the block level scope. Therefore, we try our best to avoid declaring a function in the block level scope. If it is necessary to declare a function in the block level scope, we can use function expression instead of function declaration statement:

         // Function declaration statement
        {
          let name = "plum";
          function info(){
              return name;
          }
        }
        
        // Function expression
        {
            let name = "plum";
            let info = function(){
                return name;
            };
        }

do expression

In essence, a block level scope is a statement that encapsulates multiple operations without a return value.

{
  let num = f();
  num = num + 1;
}

In the above code, the block level scope encapsulates the two statements together. However, there is no way to get the value of num outside the block level scope, because the block level scope does not return a value unless num is a global variable.

If you can change the block level scope into an expression, that is, you can return a value, then the problem is solved. So how? The way is to add do before the block level scope to make it a do expression.

let x = do {
  let num = f();
  num = num + 1;
};

const command

const declares a read-only constant. Once declared, the value of the constant cannot be changed.
Once const declares a variable, it must be initialized immediately and cannot be assigned later.

// Once declared, the value of the constant cannot be changed:
const name = "plum"; 
name = "Li Mao er"   //Error reporting: TypeError: Assignment to constant variable

// Once const declares a variable, it must be initialized immediately and cannot be assigned later:
const name;  // Error: Missing initializer in const declaration

For const, once declared, the value of the constant cannot be changed; And if you only declare that there is no assignment, an error will be reported.

  • const has the same scope as the let command: it is valid only within the block level scope where the declaration is located.
if (true) {
  const name = "plum";
}

console.log(name);  //  Uncaught ReferenceError: name is not defined
  • The constant declared by const command is not promoted, and there is also a temporary dead zone, which can only be used after the declared position.
    Call before the constant name is declared, and an error will be reported as follows:
if (true) {
  console.log(name);  //  ReferenceError: Cannot access 'name' before initialization
  const name = "plum";
}
  • const is a constant declared. Like let, it cannot be declared repeatedly.
var name = "plum";
let age = 20;

const name = "plum";  // eferenceError: Cannot access 'name' before initialization
const age = 20;
  • For compound type variables, the variable name does not point to the data, but to the address where the data is located. The const command only ensures that the address pointed to by the variable name remains unchanged, and does not guarantee that the data of the address remains unchanged. Therefore, you must be very careful when declaring an object as a constant.
const info = {};
info.prop = 678;

info = {}  // TypeError: Assignment to constant variable.

In the above code, the constant info stores an address that points to an object. Only this address is immutable, that is, you cannot point info to another address, but the object itself is changeable, so you can still add new attributes to it:

const info = [];
info.push('plum');
info.push(20);

info = ['Li Mao er'] // TypeError: Assignment to constant variable.

In the above code, the constant info is an array. This array itself is writable, but if another array is assigned to info, an error will be reported.

ES5 has only two ways to declare variables: var command and function command. In addition to adding let and const commands, ES6 also has two methods to declare variables: import command and class command. Therefore, ES6 has six methods to declare variables.

Properties of the top-level object

Top level objects refer to window objects in browser environment and global objects in Node environment. In ES5, the attributes of top-level objects are equivalent to global variables.

window.a = 1;
a  // 1

a = 2;
window.a // 2

The attribute of the top-level object is linked to the global variable, which is considered to be one of the biggest design failures of JavaScript language. This design brings several big problems. First, it is impossible to report the undeclared error of variables at compile time, which can only be known at run time (because the global variables may be created by the attributes of the top-level object, and the creation of attributes is dynamic); Secondly, programmers can easily unknowingly create global variables (such as typing errors); Finally, the properties of top-level objects can be read and written everywhere, which is very unfavorable to modular programming. On the other hand, the window object has entity meaning, which refers to the window object of the browser. The top-level object is an object with entity meaning, which is also inappropriate.

In order to change this point, ES6 stipulates that, on the one hand, in order to maintain compatibility, the global variables declared by var command and function command are still the attributes of the top-level object; On the other hand, it is stipulated that the global variables declared by let command, const command and class command do not belong to the properties of the top-level object. In other words, starting from ES6, global variables will be gradually decoupled from the properties of the top-level object.

global object

The top-level object of ES5 is also a problem, because it is not unified in various implementations.

  • In the browser, the top-level object is window, but Node and Web Worker do not have window.

  • In browsers and web workers, self also points to the top-level object, but Node does not have self.

  • In Node, the top-level object is global, but other environments do not support it.
    In order to get the top-level object in various environments, the same code now generally uses this variable, but it has limitations.

  • In the global environment, this returns the top-level object. However, in Node module and ES6 module, this returns the current module.

  • This in the function, if the function is not run as a method of the object, but simply as a function, this will point to the top-level object. However, in strict mode, this will return undefined.

  • Whether in strict mode or normal mode, new Function('return this') () always returns global objects. However, if the browser uses CSP (Content Security Policy), eval and new Function may not be available.

To sum up, it is difficult to find a way to get the top-level object in all cases. Here are two barely usable methods.

// Method 1
(typeof window !== 'undefined'
   ? window
   : (typeof process === 'object' &&
      typeof require === 'function' &&
      typeof global === 'object')
     ? global
     : this);

// Method 2
var getGlobal = function () {
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('unable to locate global object');
};

Now there is a proposal to introduce global as the top-level object at the level of language standards. In other words, in all environments, global exists and can get top-level objects from it.

Gasket library system Global has simulated this proposal and can get global in all environments.

// Writing method of CommonJS
require('system.global/shim')();

// Writing method of ES6 module
import shim from 'system.global/shim'; shim();

The above code can ensure that global objects exist in various environments.

// Writing method of CommonJS
var global = require('system.global')();

// Writing method of ES6 module
import getGlobal from 'system.global';
const global = getGlobal();

The above code puts the top-level object into the variable global.

Learning documents: http://caibaojian.com/es6/

Topics: Javascript Front-end ECMAScript