This post was originally posted on lijing0906.github. IO

I’ve been on the job for two months, and the summary is two months late. Due to the epidemic, most of the interviews this year were conducted remotely, so I took corresponding notes for every interview. From this blog, I will summarize the questions I have been asked in the interview. First, I will review the questions I have been asked in the interview, and second, I will prepare for future interviews. Refer to the nguyen let and const commands

scope

In ES5, with only global and function scopes, the inner variables may override the outer variables.

var tmp = new Date(a);function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined
Copy the code

The if block uses the TMP variable in the outer layer and the TMP variable in the inner layer. The TMP variable in the inner layer overwrites the TMP variable in the outer layer. In another scenario, loop variables used to count are leaked as global variables.

var s = 'hello';

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

console.log(i); / / 5
Copy the code

In the code above, the variable I is used only to control the loop, but when the loop ends, it does not disappear and leaks out as a global variable. ES6 began to add block-level scope, declaring variables that are only valid within the code block in which the let/const command is located.

{
  let a = 10;
  var b = 1;
}

a // ReferenceError: a is not defined.
b / / 1
Copy the code

The above code declares two variables, let and var, respectively, in the code block. These two variables are then called outside of the code block, with the let variable reporting an error and the var variable returning the correct value. This indicates that the variable declared by the LET is valid only in the code block in which it is located.

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); / / 5
}
Copy the code

The above function has two code blocks that declare the variable n and print 5. This means that the outer code block is not affected by the inner code block. If you use var to define n both times, the output is 10. A counter for a loop is suitable for the let command.

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6] ();/ / 6
Copy the code

In the above code, the variable I is declared by let, and the current I is only valid for this loop, so each loop I is actually a new variable, so the final output is 6. You might ask, if the variable I for each cycle is redeclared, how does it know the value of the previous cycle and thus calculate the value of the current cycle? This is because the JavaScript engine internally remembers the value of the previous cycle, and when it initializes the variable I of this cycle, it evaluates on the basis of the previous cycle.

Variable ascension

The “variable promotion” phenomenon occurs when the var command is used, that is, the variable can be used before the declaration and its value is undefined. To correct this, the let/const command changes the syntactic behavior so that the variable it declares must be used after the declaration or an error will be reported.

// var case
console.log(foo); / / output is undefined
var foo = 2;

// let
console.log(bar); / / error ReferenceError
let bar = 2;
Copy the code

In the above code, the variable foo is declared with the var command, and the variable promotion occurs. That is, when the script starts running, the variable foo already exists, but has no value, so undefined is printed. The variable bar is declared with the let command, and no variable promotion occurs. This means that the variable bar does not exist until it is declared, and an error will be thrown if it is used.

ES6 temporary dead zone

As long as the let/const command exists in the block-level scope, the variable declared by it is “binding” to the region, no longer subject to external influence.

var tmp = 123;
if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}
Copy the code

In the above code, there is a global variable TMP, but in the block-level scope, let also declared a local variable TMP, causing the latter to bind the block-level scope. Therefore, before let declared the variable, TMP assignment will report an error. ES6 explicitly states that if there are let and const commands in a block, the variables declared by the block to those commands form a closed scope from the start. Any time these variables are used before declaration, an error is reported. In short, the variable is not available within the code block until it is declared using the let command. This is grammatically known as a “temporal dead zone” (TDZ).

if (true) {
  / / start TDZ
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; / / end of TDZ
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); / / 123
}
Copy the code

In the above code, the TMP variable is in the “dead zone” of TMP until the let command declares it. A “temporary dead zone” also means that Typeof is no longer a 100 percent secure operation.

typeof x; // ReferenceError
let x;
Copy the code

In the above code, variable X is declared using let command, so before declaration, it belongs to the “dead zone” of X, and an error will be reported whenever this variable is used. Therefore, the Typeof runtime throws a ReferenceError. By comparison, typeof does not generate an error if a variable is not declared at all.

typeof undeclared_variable // "undefined"
Copy the code

In the above code, undeclared_variable is a nonexistent variable name and returns “undefined”. So, before let, typeof operators are 100 percent safe and never report errors. That’s no longer true. This is designed to encourage good programming practice. Variables must be used after declaration, otherwise an error will be reported.

ES6 makes it clear that temporary dead zones and let and const statements do not promote variables. The main purpose of ES6 is to reduce runtime errors and prevent the variable from being used before it is declared, resulting in unexpected behavior. Such errors are common in ES5, and with this provision, it’s easy to avoid them. In short, the essence of a temporary dead zone is that the variable you want to use already exists as soon as you enter the current scope, but you can’t get it until the line of code that declared the variable appears.

Duplicate declarations are not allowed

Let does not allow the same variable to be declared twice in the same scope.

/ / an error
function func() {
  let a = 10;
  var a = 1;
}
/ / an error
function func() {
  let a = 10;
  let a = 1;
}
Copy the code

Therefore, arguments cannot be redeclared inside functions.

/ / an error
function func(arg) {
  let arg;
}
func()
function func(arg) {{letarg; }}/ / is not an error
func()
Copy the code

Block-level scope and function declarations see this jump to see ruan Da Da’s blog

Let and function declarations can only appear at the top of the scope

ES6 block-level scopes must have braces, and if there are no braces, the JavaScript engine assumes that there is no block-level scope.

// This is not the case
if (true) let x = 1;

// The second way is not error
if (true) {
  let x = 1;
}
Copy the code

In the above code, the first method has no braces, so there is no block-level scope, and the let can only appear at the top level of the current scope, so an error is reported. The second version has braces, so the block-level scope holds. The same is true for function declarations, which in strict mode can only be declared at the top level of the current scope.

'use strict';
if (true) {
  function f() {}}/ / an error
'use strict';
if (true)
  function f() {}
Copy the code

const

Const declares a read-only constant. Once declared, the value of a constant cannot be changed.

const PI = 3.1415;
PI / / 3.1415

PI = 3;
// TypeError: Assignment to constant variable.
Copy the code

The above code shows that changing the value of a constant causes an error. A variable declared by const may not change its value, which means that a const, once declared, must be initialized immediately and cannot be left for later assignment.

const foo;
// SyntaxError: Missing initializer in const declaration
Copy the code

The code above shows that const is declared without assignment, and an error is reported.

Const has the same scope as the let command: it is only valid in the block-level scope in which the declaration is made.

if (true) {
  const MAX = 5;
}

MAX // Uncaught ReferenceError: MAX is not defined
Copy the code

Constants declared by the const command are also non-promoted and have temporary dead zones that can only be used after the declared position.

if (true) {
  console.log(MAX); // ReferenceError
  const MAX = 5;
}
Copy the code

The above code is called before the constant MAX declaration, and the result is an error. A constant declared by const, like let, cannot be declared repeatedly.

var message = "Hello!";
let age = 25;

// The following two lines will return an error
const message = "Goodbye!";
const age = 30;
Copy the code

What const actually guarantees is not that the value of the variable is fixed, but that the memory address to which the variable refers is fixed. For data of simple types (values, strings, booleans), the value is stored at the memory address to which the variable points, and is therefore equivalent to a constant. But for complex type data (mainly objects and arrays), variable pointing to the memory address, save only a pointer to the actual data, const can guarantee the pointer is fixed (i.e., always points to the other a fixed address), as far as it is pointing to the data structure of variable, is completely unable to control. Therefore, declaring an object as a constant must be done with great care.

const foo = {};
// Add an attribute to foo
foo.prop = 123;
foo.prop / / 123
// If foo points to another object, an error is reported
foo = {}; // TypeError: "foo" is read-only
Copy the code

In the code above, the constant foo stores an address that points to an object. Only the address is immutable, that is, you can’t point foo to another address, but the object itself is mutable, so you can still add new attributes to it. Here’s another example.

const a = [];
a.push('Hello'); / / the executable
a.length = 0;    / / the executable
a = ['Dave'];    / / an error
Copy the code

In the above code, the constant A is an array. The array itself is writable, but an error is reported if another array is assigned to A. If you really want to freeze an Object, you should use the object. freeze method.

const foo = Object.freeze({});
// In normal mode, the following line does not work;
// In strict mode, this line will report an error
foo.prop = 123;
Copy the code

In the code above, the constant foo refers to a frozen object, so adding new attributes does not work and will cause an error in strict mode. In addition to freezing the object itself, properties of the object should also be frozen. Here is a function that freezes an object completely.

/ / is not an error
var constantize = (obj) = > {
  Object.freeze(obj);
  Object.keys(obj).forEach( (key, i) = > {
    if ( typeof obj[key] === 'object') { constantize( obj[key] ); }}); };Copy the code

ES6 six ways to declare variables

ES5 has only two methods for declaring variables: the var command and the function command. In addition to the let and const commands, ES6 has two other ways to declare variables, the import command and the class command, as discussed in a later section. So there are six ways to declare variables in ES6.

Properties of the top-level object click here to view the globalThis object