I am small and live in Wuhan, do two years of new media, ready to use 6 months time to switch to the front end of the industry.

Lesson Objective for today

Yesterday, based on some page search, I learned 3.6 statements in chapter 3 of JavaScirpt Advanced Programming (3rd edition). Therefore, today is mainly based on the search to carefully learn the pre-knowledge of variable declaration, another suitable learning day, go, small and !!!!


Today’s Lesson summary

Variable declarations can use var,let, and const, so why are there three ways to declare variables?

  • Block-level scope
    • Why?
    • ES6 block-level scope
    • Related to function declarations
  • Scope lifting
    • Variable ascension
    • Function increase
  • Temporary dead zone
  • ES6 declares variables
  • The top-level object
  • Properties of the top-level object

Block-level scope

Why?

ES5 has only global and functional scopes, not block-level scopes, which leads to a lot of irrational scenarios.

The first scenario

Inner variables may override 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.


The second 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 block-level scope

Let actually adds block-level scope to JavaScript.


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.

ES6 allows for arbitrary nesting of block-level scopes

The code above uses a five-level block-level scope.


{{{{{let insane = 'Hello World'}}}}};
Copy the code

The outer scope cannot read variables from the inner scope.

{{let insane = 'Hello World'}
  console.log(insane); / / an error
};
Copy the code

The inner scope can define variables of the same name for the outer scope


{
  let insane = 'Hello World';
  {let insane = 'Hello World'}};Copy the code

The emergence of block-level scopes virtually eliminates the need for the widely used immediate-execution function expressions (IIFE).


/ / IIFE
(function () {
  var tmp = ...;
  ...
}());

// block level scope
{
  lettmp = ... ; . }Copy the code

Related to function declarations

Function declaration in ES5

Can functions be declared in block-level scopes? This is a rather confusing question.

ES5 states that functions can only be declared in top-level scopes and function scopes, not block-level scopes.


/ / a
if (true) {
  function f() {}}
/ / 2
try {
  function f() {}}catch(e) {
  // ...
}
Copy the code

Both of the above function declarations are illegal under ES5.

However, browsers do not comply with this rule and, for compatibility with older code, still support declaring functions in block-level scopes, so both cases actually work without error.


function f() { console.log('I am outside! '); }

(function () {
  if (false) {
    // declare the function f again
    function f() { console.log('I am inside! '); }}f(); } ());Copy the code

Running the above code on ES5, you get I am Inside! Because f declared inside if is promoted to the function header, the actual code to run is as follows.


/ / ES5 environment
function f() { console.log('I am outside! '); }

(function () {
  function f() { console.log('I am inside! '); }
  if (false) { } f(); } ());Copy the code

Function declaration in ES6

ES6 introduces block-level scopes, explicitly allowing functions to be declared in block-level scopes. ES6 states that a function declaration statement in a block-level scope behaves like a LET and cannot be referenced outside the block-level scope.


// ES6 environment of the browser
function f() { console.log('I am outside! '); }

(function () {
  if (false) {
    // declare the function f again
    function f() { console.log('I am inside! '); }}f(); } ());// Uncaught TypeError: f is not a function
Copy the code

Run in an ES6-compliant browser and you’ll theoretically get I am Outside! . Because functions declared inside the block-level scope are like lets, they have no effect outside the scope.

However, if you actually run the above code in an ES6 browser, you will get an error. Why?

Because what actually runs is the following code.


// ES6 environment of the browser
function f() { console.log('I am outside! '); }
(function () {
  var f = undefined;
  if (false) {
    function f() { console.log('I am inside! '); }}f(); } ());// Uncaught TypeError: f is not a function
Copy the code

It turns out that if you change the processing rules for functions declared in block-level scopes, you can obviously have a big impact on older code.

To mitigate the resulting incompatibilities, ES6 provides in Appendix B that browser implementations can behave in their own way.


In ES6, function rules are declared in block-level scope

  • Allows functions to be declared in block-level scopes.
  • Function declarationSimilar to thevar, will be promoted toGlobal scopeorFunction scopeIn the head.
  • At the same time,Function declarationWill also be promoted to whereBlock-level scopetheThe head.

Note that the above three rules apply only to browser implementations of ES6. Implementations of other environments do not follow these rules, and block-scoped function declarations are treated as lets.

According to these three rules, in the browser’s ES6 environment, block-level scoped functions behave like variables declared by var.

You should avoid declaring functions in block-level scopes because of the wide variation in behavior caused by the environment. If necessary, write it as a function expression rather than a function declaration statement.


Function declarations in block-level scopes require curly braces

ES6’s block-level scope allows you to declare rules for functions only if braces are used, and an error is reported if braces are not used.

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

Scope lifting

We know that before ES6 there was no block-level scope, only global scope and function scope.

JS before executing scripts parsing code, at the time of parsing will create a global execution context, and will be one of the variables and functions are out first, and give them early create good space in the memory, a temporary assignment for undefined variable, the function will be declared in advance, the stored in memory, this step is done to the formal execution procedures.

Function in the execution of the same, will first parse the code, create a function execution context, the variables, the function prepared in advance.

console.log(a); // undefined
var a = 1;
test(); // test is running
function test(){
  console.log('test is running')
}
b=2;
Copy the code

So, when console.log(a) is executed, the JS parser has defined a in advance and assigned it to undefined. It can be called before a function is defined.

Variable ascension

When we use a variable or function, it is important to understand when the value is initialized.

Variable promotion means that a variable is used before it is declared. In a global scope, only variables declared with the var keyword are promoted. When a variable is promoted, the browser knows that only one variable exists.

But the value you define below has not been assigned to this variable yet. The value of · is undefined until the browser executes the code below.

So there is no initialization value when the variable is promoted. Using var to declare a variable adds a property of the same name to the window, so you can also get the value of the variable by using the property name. Assigning a value to a variable without any keyword declaration is equivalent to adding a property of the same name to the window.

Function increase

A function can be defined using a function declaration and a function expression. The two methods are also different when promoted. The function declaration is promoted to the top of the scope.

So you can execute a function before you define it, and the way you define it improves. Function expressions, on the other hand, are just declarations and don’t assign values to them.


// Function declaration statement
{
  let a = 'secret';
  function f() {
    returna; }}
// Function expression
{
  let a = 'secret';
  let f = function () {
    return a;
  };
}
Copy the code

Temporary dead zone

Temporary dead zones (TDZ), ES6 explicitly states that if there are let and const commands in a block, the block is closed scoped from the start to the variables declared by those commands. Any time these variables are used before declaration, an error is reported.

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.

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.

ES6 declares 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.


The top-level object

The top-level object of ES5 is itself a problem because it is not uniform across implementations.

In browsers, the top-level object is window, but Nodes and Web workers don’t have Windows.

In browsers and Web workers, self also points to the top-level object, but Node has no self.

In Node, the top-level object is global, which is not supported in any other environment.

In order for the same code to be able to fetch the top-level object in any environment, it is now common to use this variable, but there are limitations.

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

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

In strict 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 work.

In summary, it’s hard to find a way to get to the top-level object in all cases. Here are two methods that can be used reluctantly.

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

/ / method 2
var getGlobal = function () {
  if (typeofself ! = ='undefined') { return self; }
  if (typeof window! = ='undefined') { return window; }
  if (typeofglobal ! = ='undefined') { return global; }
  throw new Error('unable to locate global object');
};
Copy the code

There is now a proposal to introduce Global as a top-level object at the level of language standards. That is, in all circumstances, global exists and can be retrieved from it.

The shim library system. Global emulates this proposal and is available in all environments.

// CommonJS
require('system.global/shim') ();
// ES6 module
import shim from 'system.global/shim'; shim();
Copy the code

The code above ensures that the global object exists in any environment.


// CommonJS
var global = require('system.global') ();
// ES6 module
import getGlobal from 'system.global';
const global = getGlobal();
Copy the code

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


Properties of the top-level object

The top-level object refers to the Window object in the browser environment and the Global object in Node. In ES5, attributes of top-level objects are equivalent to global variables.

window.a = 1;
a / / 1

a = 2;
window.a / / 2
Copy the code

In the code above, the assignment of attributes to top-level objects is the same thing as the assignment of global variables.

The attributes of top-level objects are linked to global variables, which is considered one of the biggest design failures of the JavaScript language.

This design poses several big problems

  • First, there is no compile-time error with undeclared variables, which is only known at runtime (because global variables can be created by attributes of top-level objects, and attribute creation is dynamic).

  • Second, it is easy for programmers to unknowingly create global variables (such as typos). Finally, the properties of the top-level object are read and written everywhere, which makes modular programming very difficult.

  • On the other hand, the window object has an entity meaning, which refers to the browser window object, and the top-level object is an entity meaning object, which is also inappropriate.

In order to change this, ON the one hand, ES6 stipulates that, in order to maintain compatibility, global variables declared by var and function commands are still attributes of top-level objects.

On the other hand, global variables declared by let, const, and class are not attributes of the top-level object.

That is, starting with ES6, global variables will gradually be decoupled from the properties of top-level objects.

var a = 1;
// If in the REPL environment of Node, it can be written as global.a
// Or, in general, write this.a
window.a / / 1

let b = 1;
window.b // undefined
Copy the code

In the code above, the global variable A is declared by the var command, so it is a property 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.


Summary of today’s lesson



Today the mood

Today is mainly based on the search to carefully learn the related pre-knowledge of variable declaration, part of the content from the ES6 introductory tutorial, feel the attention of variable declaration, feel very good, hope to learn more tomorrow ~~~~


This article is formatted using MDNICE