let command
Basic Usage
{ let a = 10; var b = 1; } a // ReferenceError: a is not defined. b // 1
According to the above results, the variables declared by let are valid only in the code block where it is located.
Example 1:
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 10
Example 2: if let is used, the declared variables are only valid in the block level scope, and the last output is 6.
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6
In the above code, the variable i is declared by let, and the current i is only valid in this cycle, so the i of each cycle is actually a new variable, so the final output is 6. You may ask, if the variable i of each cycle is redeclared, how does it know the value of the previous cycle and calculate the value of this cycle? This is because the JavaScript engine will remember the value of the previous cycle. When initializing the variable i of this cycle, it will calculate based on the previous cycle.
In addition, a special feature of the for loop is that the part where the loop variable is set is a parent scope, while the interior of the loop body is a separate child scope.
for (let i = 0; i < 3; i++) { let i = 'abc'; console.log(i); } // abc // abc // abc
The above code runs correctly and outputs abc three times. This indicates that the variable i inside the function and the loop variable i are not in the same scope and have their own scope.
There is no variable promotion
The var command will have the phenomenon of "variable promotion", that is, the variable can be used before declaration, and the value is undefined. In order to correct this phenomenon, the let command changes the syntax behavior. The variable declared by it must be used after declaration, otherwise an error will be reported.
// var Situation console.log(foo); // output undefined var foo = 2; // let Situation console.log(bar); // report errors ReferenceError let bar = 2;
Temporary dead zone
Within a code block, a variable is not available until it is declared using the let command. Grammatically, this is called "temporary dead zone" (TDZ).
if (true) { // TDZ start tmp = 'abc'; // ReferenceError console.log(tmp); // ReferenceError let tmp; // TDZ end console.log(tmp); // undefined tmp = 123; console.log(tmp); // 123 }
Duplicate declarations are not allowed
let is not allowed to declare the same variable repeatedly within the same scope.
// report errors function func() { let a = 10; var a = 1; } // report errors function func() { let a = 10; let a = 1; }
Therefore, you cannot redeclare parameters inside a function.
function func(arg) { let arg; // report errors } function func(arg) { { let arg; // No error reporting } }
Block level scope
Why do I need a block level scope
ES5 has only global scope and function scope, but no block level scope, which brings many unreasonable scenarios.
In the first scenario, inner variables may override outer variables.
var tmp = new Date(); function f() { console.log(tmp); if (false) { var tmp = 'hello world'; } } f(); // undefined
The original meaning of the above code is that the outer tmp variable is used outside the if code block, and the inner tmp variable is used inside. However, after the function f is executed, the output result is undefined. The reason is that the tmp variable of the inner layer covers the tmp variable of the outer layer due to the variable promotion.
In the second scenario, the loop variable used to count is leaked as a global variable.
var s = 'hello'; for (var i = 0; i < s.length; i++) { console.log(s[i]); } console.log(i); // 5
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
let actually adds a block level scope to JavaScript.
function f1() { let n = 5; if (true) { let n = 10; } console.log(n); // 5 }
ES6 allows arbitrary nesting of block level scopes.
{{{{{let insane = 'Hello World'}}}}};
The above code uses a five layer block level scope. The outer scope cannot read the variables of the inner scope.
{{{{ {let insane = 'Hello World'} console.log(insane); // report errors }}}};
Inner scopes can define variables with the same name as outer scopes.
{{{{ let insane = 'Hello World'; {let insane = 'Hello World'} }}}};
The emergence of block level scope actually makes the widely used immediate execution function expression (IIFE) unnecessary.
// IIFE Writing method (function () { var tmp = ...; ... }()); // Block level scope writing { let tmp = ...; ... }
do expression
In essence, a block level scope is a statement that encapsulates multiple operations without a return value.
{ let t = f(); t = t * t + 1; }
In the above code, the block level scope encapsulates the two statements together. However, there is no way to get the value of T outside the block level scope, because the block level scope does not return a value unless t is a global variable.
Now there is a proposal to make the block level scope become an expression, that is, it can return a value. The way is to add do before the block level scope to make it become a do expression, and then return the value of the last internal expression.
function f() { return 2; } let x = do { let t = f(); t * t + 1; };
In the above code, variable x will get the return value of the whole block level scope (t * t + 1).
const command
Basic Usage
const declares a read-only constant. Once declared, the value of the constant cannot be changed.
const PI = 3.1415; PI // 3.1415 PI = 3; // TypeError: Assignment to constant variable.
The above code indicates that changing the value of the constant will report an error.
The variable declared by const must not change its value, which means that once const declares the variable, it must be initialized immediately and cannot be left for later assignment.
const foo; // SyntaxError: Missing initializer in const declaration
The above code indicates that for const, if it only declares that it does not assign a value, 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 MAX = 5; } MAX // Uncaught ReferenceError: MAX 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.
if (true) { console.log(MAX); // ReferenceError const MAX = 5; }
The above code is called before the constant MAX declaration, and the result is an error.
const is a constant declared. Like let, it cannot be declared repeatedly.
essence
const actually guarantees that the value of the variable cannot be changed, but the memory address pointed to by the variable cannot be changed. For simple types of data (numeric, string, Boolean), the value is stored at the memory address pointed to by the variable, so it is equivalent to a constant. However, for composite data (mainly objects and arrays), the memory address pointed to by the variable is only a pointer. const can only ensure that the pointer is fixed. As for whether the data structure it points to is variable, it can not be controlled at all. Therefore, you must be very careful when declaring an object as a constant.
const foo = {}; // by foo Adding an attribute can succeed foo.prop = 123; foo.prop // 123 // take foo If you point to another object, an error will be reported foo = {}; // TypeError: "foo" is read-only
In the above code, the constant foo stores an address that points to an object. What is immutable is only this address, that is, foo cannot be pointed to another address, but the object itself is changeable, so new attributes can still be added to it.
Here is another example.
const a = []; a.push('Hello'); // Executable a.length = 0; // Executable a = ['Dave']; // report errors
In the above code, the constant a is an array. The array itself is writable, but if another array is assigned to a, an error will be reported. If you really want to freeze an object, you should use object Free method.
const foo = Object.freeze({}); // In normal mode, the following line does not work; // In strict mode, the line will report an error foo.prop = 123;
In the above code, the constant foo points to a frozen object, so adding a new attribute does not work, and an error will be reported in strict mode.
In addition to freezing the object itself, the properties of the object should also be frozen. The following is a function that completely freezes the object.
var constantize = (obj) => { Object.freeze(obj); Object.keys(obj).forEach( (key, i) => { if ( typeof obj[key] === 'object' ) { constantize( obj[key] ); } }); };
ES6 six ways to declare variables
ES5 has only two ways to declare variables: var command and function command. In addition to adding let and const commands in ES6, the following chapters will also mention two other methods of declaring 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
In the above code, the attribute assignment of the top-level object is the same as that of the global variable.
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.
var a = 1; // If in Node of REPL Environment, can be written as global.a // Or use the general method to write this.a window.a // 1 let b = 1; window.b // undefined
In the above code, the global variable a is declared by the var command, so it is an attribute of the top-level object; The global variable b is declared by the let command, so it is not a property of the top-level object and returns undefined.
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 a global object. 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'); };