Block-level scope

What is block-level scope

Block-level scopes have been added in ES6. Block scopes are included by {}. The {} in the if and for statements are also block scopes.

Why do you need block-level scopes

Scenario 1: Internal variables overwrite external variables

var time = new Date()
function fx () {
    console.log(time) // undefined
    if (false) {
        var time = 'hello'
    }
}
fx()
Copy the code

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); / / 5Copy the code

Implement block-level scope (ES6 provides let variables to implement block-level scope)

Function fxFn () {// this is a block scope let fx = 'fx is a great girl' if (true) {// this is a block scope let fx = 'fx is 18 years old'} Console.log (fx) // fx is a great girl} fxFn() // block-level scopes do not affect each otherCopy the code

ES6 allows for arbitrary nesting of block-level scopes.

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

The code above uses a five level block-level scope, with each level being a separate scope. The fourth scope cannot read the internal variables of the fifth scope.

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 advent of block-level scopes actually makes it possible to obtain widely used, anonymous, instant-execution function expressions(Anonymous IIFE)No longer necessary.

// IIFE; (function () {var TMP = 'a' console.log(TMP)}()) ; . }Copy the code

Block-level scopes and function declarations

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

If (true) {function f() {}} try {function f() {}} catch (e) {// if (true) {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.

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.

function f() { console.log('I am outside! '); } (function () {if (false) {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.

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

The ES6 is completely different, theoretically getting “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?

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

The above code will always report an error in an ES6 browser.

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 stipulates that browser implementations can behave in their own way, regardless of the above rules

  • Allows functions to be declared in block-level scopes.
  • The function declaration looks something likevarIs promoted to the head of the global scope or function scope.
  • Function declarations are also promoted to the head of their block-level scope.

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, block-level scoped functions in the browser’s ES6 environment behave like variables declared by var. The above example actually runs the following code.

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 functionCopy the code

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.

{let a = 'secret'; function f() { return a; }} // Inside the block-level scope, the function expression {let a = 'secret'; let f = function () { return a; }; }Copy the code

ES6 block-level scopes must have braces

If there are no braces, the JavaScript engine assumes that there is no block-level scope.

If (true) let x = 1; 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.

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

Variable promotion/function promotion

What is variable/function promotion

  • All declarations, including variables and functions, are processed first before any code is executed, a phenomenon known as promotion.
  • But only the declaration itself is promoted; assignment or other running logic stays put
  • javascriptIt’s not strictly top-down language

Variable promotion details

  • JavaScriptThe variable promotion is directed againstvar, andletandconstThere is no variable promotion feature
  • throughvarA variable that can be accessed before the statement is defined:undefined
  • Variable promotion means that a variable is promoted to the top of the scope, no matter where it is declared in the scope.
  • When you seevar a = 2; “, may think this is a statement. butJavaScriptIt is actually thought of as two declarations:var a; anda = 2; .
  • The JS interpreter will find the variables and functions that need to be promoted, and give them space in memory ahead of time. For functions, the entire function is stored in memory, and the variables are only declared and assigned toundefined.

The first definition declaration is made at compile time. The second assignment declaration is left in place for the execution phase. The first snippet is processed as follows:

var a;
a = 2;
console.log(a); 
Copy the code

The first part is compilation, while the second part is execution.

Similarly, our second code snippet actually follows the following flow:

var a;
console.log(a); 
a = 2; 
Copy the code

For example, this process is as if variable and function declarations are “moved” to the top from where they appear in the code. This process is called ascension.

JavaScript only raises variable declarations, but not initialization

But if the variable has never been declared, a ReferenceError is raised, such as a direct output:

console.log(b) // Uncaught ReferenceError: b is not defined
Copy the code

It is also worth noting that each scope is promoted

var a = 100

function fn () {
    console.log(a)
    var a = 200
    console.log(a)
}

fn()
console.log(a)
var a
console.log(a)
var a = 300
console.log(a)
Copy the code

So this code is going to say 200, 100, 100, 300

In fn() var a = 200 is declared, so var a is promoted to the top of fn’s scope, and the first output is undefined

Before ES6, JS had no block-level scope, so a variable declared in if is treated as a global variable

var a = 1
if (true) {
    var a = 2
}
console.log(a) // 2
Copy the code

Compare the following two pieces of code

var a = 10                        
function fx () {
    console.log(a) // undefined
    var a = 20
    console.log(a) // 20
}
fx()
console.log(a) // 10
Copy the code

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

var a = 10
function fx () {
    console.log(a) // 10
    a = 20
    console.log(a) // 20
}
fx()
console.log(a) // 20
Copy the code

In the second code fx(), a does not use var definition, so there is no variable declaration in the FX function. Therefore, the variable a accessed in FX is actually the global variable A, and a = 20 is equivalent to reassigning 20 to the global variable A

Function declaration promotion

Functions declared by function can be called directly before

Function fx() {console.log('fx is a great girl')} fx() // fx is a great girl It can be called before or afterCopy the code

But if you write it this way: function expression declares the function

console.log(fx) // undefined
var fx = function () {
    console.log('fx is a great girl')
}
Copy the code

= = = = = = = = = = = = = = = = = = = = = = = = = =

Fx () // ReferenceError, TypeErr var fx = function () {console.log('fx is a great girl')}Copy the code

The variable identifier FX () in this program is promoted and assigned to its scope (global scope in this case), so fx() does not cause ReferenceError. But FX does not assign at this point (as it would if it were a function declaration rather than a function expression). Fx () raises TypeError because a function call on the value of undefined causes an illegal operation.

When the current function declaration and variable declaration use the same variable name, the function takes precedence over the variable

Log ('fx is a great girl')} var fx = 'fx' console.log(fx) // fx  console.log(fx()) // TypeError: fx is not a functionCopy the code

When there are multiple function declarations with the same name, the last function declaration replaces the previous one

    fx() // fx is a great girl
    function fx () {
        console.log('fx')
    }

    function fx () {
        console.log('fx is a great girl')
    }
Copy the code

List a few questions below, after understanding it is very easy to understand

function fx () {
    console.log(a) // undefined
    if (false) {
        var a = 1
    }
    console.log(a) // undefined
    console.log(b) // Uncaught ReferenceError: b is not defined
}
fx()
Copy the code

If the condition of the if statement is false, it does not affect the promotion of the A variable

    function fx () {
        console.log('fx is a great girl')
    }
    var fx
    console.log(typeof fx) // function
Copy the code

= = = = = = = = = = = = = = = = = = = = = = = = = = =

    function fx () {
        console.log('fx is a great girl')
    }
    var fx = 'good girl'
    console.log(typeof fx) // string
Copy the code

= = = = = = = = = = = = = = = = = = = = = = = = = = =

console.log(typeof fx) // function
var fx = 'good girl'
function fx () {
    console.log('fx is a great girl')
}
console.log(typeof fx) // string
Copy the code

= = = = = = = = = = = = = = = = = = = = = = = = = = =

console.log(typeof fx) // function var fx function fx () { console.log('fx is a great girl') } console.log(typeof fx) //  function if(! (fx in window)) { var fx = 1 } console.log(fx) // undefinedCopy the code
Var c = 1 function c(c) {console.log(c)} c(2) // c is not a function. Functions are promoted, variables are defined, and variables are definedCopy the code

= = = = = = = =

c(2) // 2
var c = 1
function c(c) {
    console.log(c)
}
console.log(c) // 1
Copy the code

= = = = = = = =

var c = function(c) {
    console.log(c)
}
c(2) // 2
var c = 1
Copy the code

= = = = = = = =

var c = function(c) {
    console.log(c)
}
var c = 1
c(2) // Uncaught TypeError: c is not a function
Copy the code

= = = = = = = =

console.log(c) // undefined
c(2) // Uncaught TypeError: c is not a function
var c = function(c) {
    console.log(c)
}
var c = 1
Copy the code

Anonymous functions

What is an anonymous function: a function with no real name

Anonymous functions:

Closures can be implemented through anonymous functions

2. Simulate block-level scope and reduce global variables. After the anonymous function is executed, the corresponding variable stored in memory is destroyed. Using block-level scope greatly reduces the problem of naming conflicts without worrying about messing up the global scope.

Anonymous functions:

Declare a common function:

function fx () {
    console.log('good girl')
}
Copy the code

Remove the function name

Console. log('good girl')} function () {console.log('good girl')}Copy the code

Properly defined anonymous functions

(function () {// Statements in the body of an anonymous function are not executed because the anonymous function is not executed. console.log('fx') })Copy the code

A function with its name removed by parentheses is an anonymous function:

What the parentheses do:

The parentheses break up our expression combination into chunks, and each chunk, that is, each pair of parentheses, has a return value. The return value is actually the return value of the expression in the parentheses. So, when we enclose an anonymous Function in parentheses, the parentheses actually return the Function object of an anonymous Function. Therefore, the parentheses add an anonymous function as if it were a named function and we get its reference position. So if you add a list of arguments to the reference variable, you get the normal function call form. In layman’s terms, when you add the parentheses, you have the same form as a named function.

Anonymous function self-execution, also known as immediate function expression (IIFE)

  • Methods a
/ / no anonymous functions of parameters (the function () {the console. The log (" fx ")}) (); Function (a, b, c) {console.log(' console.log ', b) {console.log(' console.log ', b) // console.log(' console.log ', b) // console.log(' console.log ', b) // console.log(' console.log ', b) // Console. log(' console.log ', b) // Console. log(' console.log ', b) // Console. log(' console.log ', b) I am parameters two console. The log (' three parameters: 'c) / / three parameters: fx}) (' this is the place where the ordinary function and', 'I am parameters of 2', 'fx)Copy the code
  • Way 2
(function () {console.log('fx')}())Copy the code

Methods three

! function (fx) { console.log(fx) }('fx')Copy the code

Methods four

let fx = function (fx) {
    console.log(fx)
}('fx')
Copy the code

IIFE common usage

Another very common advanced use of IIFE is to call them as functions and pass in arguments. var a = 2; (function IIFE (global) { var a = 3 console.log(a) // 3 console.log(global.a) // 2 })(window) console.log(a) // 2 IIFE Another variation is to invert the running order of the code, putting the function that needs to be run in the second place and passing it as an argument after IIFE execution. (function IIFE (def) { def(window) })(function def (global) { var a = 3 console.log(a) // 3 console.log(global.a) // 2 })Copy the code

Application scenarios of anonymous functions

$('#fx').onclick = function () {console.log(' add button click event ')} 2. Var obj = {name: 'fx', fx: function () { return this.name + ' is' + ' good girl' } } console.log(obj.fx()) // fx is good girl 3. Var fx = function () {return 'fx is good girl'} console.log(fx()) // fx is good girl 4. SetInterval (function () {console.log('fx is good girl')}, 1000) 5 Return function () {return 'fx'}} console.log(fx()()) // fxCopy the code

Anonymous functions mimic block-level scopes

If (true) {var a = 12 // a is global} console.log(a) // 12 for (var I = 0; i < 3; I ++) {// console.log(I)} console.log(I) // 3 for has no scope of its own, so when the loop ends I becomes a global variable if () {}for () {} etc has no scope of its own. If it does, the declared variable will be destroyed immediately if it goes out of its scope. Function fn () {(function () {var fx = 'good girl! '// This variable is not externally defined console.log(fx) // good girl! })() console.log(fx) Uncaught ReferenceError: fx is not defined} fn()Copy the code

Problem sets a

function test(a, b, c, d){ console.log(a + b + c + d); } (1, 2, 3, 4); / / not perform error = = = = = = = = = = = = = = function test () {the console. The log (a + b + c + d); } (); // Uncaught SyntaxError: Unexpected token)Copy the code

Problem set 2

function fxFn (){
    var arr = [];
    for(var i = 0; i < 10; i ++){
        arr[i] = function (){
            console.log(i);
        }
    }
    return arr;
}
var fx = fxFn();
for(var j = 0; j < 10; j++){
    fx[j]();
}
Copy the code

Break down

In fxFn, since for is not block-level scoped, var I becomes a local variable for fxFn, and each new I overwrites the old one until I =10. So it prints 10 tens

Problem sets three

function fxFn(){
    var arr = [];
    for(var i = 0; i < 10; i ++){
        (function(j){
            arr[i] = function (){
            console.log(j + " ");
        }
        }(i))
    }
    return arr;
}
var fx= fxFn();
for(var j = 0; j < 10; j++){
    fx[j]();
}
Copy the code

A:

This uses the execute now function, passing the I in fxFn as an argument to the j of the anonymous function, so the status of the j is updated every time it executes, so it prints 0, 1, 2, 3, 4, 5, 6, 7, 8, 9

Disadvantages of anonymous functions

1. Anonymous functions do not show meaningful function names in the stack trace, making debugging difficult.

2. Without a function name, only the expired arguments.callee reference can be used when the function needs to reference itself, such as in recursion. Another example of a function needing to reference itself is when the event listener needs to unbind itself after the event is fired.

3. Anonymous functions omit function names that are important to code readability/understandability. A descriptive name lets the code speak for itself.

let

The characteristics of

  • Variables declared by lets are valid only in their own block-level scope and have temporary dead zones
  • Cannot duplicate declaration (error message already defined)
  • No preprocessing, no variable promotion

application

  • Loop through the listener
  • Using let instead of var is the trend
  • It mainly addresses the need for block-level scope
  • Prevent using (variables) before declaring (variables)

A simple example

{let a = 10; var b = 1; } a // ReferenceError: a is not defined. b // 1 ============ for (var i = 0; i < 5; I++) {the console. The log (I) / / 0 1 2 3 4}. The console log (I) / / 5 I became a global variable = = = = = = = = = = = = = = the for (let j = 0; j < 5; J ++) {console.log(j) // 0 1 2 3 4} // Uncaught ReferenceError: console.log(j) {console.log(j) // 0 1 2 3 4} j is not definedCopy the code
var a = []
for (let i = 0; i < 10; i++) {
    a[i] = function () {
        console.log(i)
    }
}
console.log(a[6]()) // 6
console.log(i) // Uncaught ReferenceError: i is not defined
 
===
var a = []
for (var i = 0; i < 10; i++) {
    a[i] = function () {
        console.log(i)
    }
}
a[6]() // 10 
a[1]() // 10
console.log(i) // 10
Copy the code
  • In the code above, variablesiisvarCommand declaration is valid globally, so there is only one variable globallyi. Each time through the loop, the variableiIs changed, and the loop is assigned to the arrayaInside the function ofconsole.log(i)The inside of the,iIt’s going to be globali. In other words, all arraysaOf the members of theiThey all point to the same thingi, resulting in the run-time output being the last roundiThat’s 10.
  • If you are usinglet, the declared variable is valid only in block-level scope, and the final output is 6.

forAnother special feature of the loop is that the part that sets the variables of the loop is a parent scope, while the inside of the loop body is a separate child scope.

for (let i = 0; i < 3; i++) {
    let i = 'abc'
    console.log(i)
}
Copy the code

The above code works correctly, printing three ABCs. This indicates that the variable I inside the function and the loop variable I are not in the same scope and have separate scopes.

There is no variable promotion

// var case console.log(foo); Var foo = 2; // let's case console.log(bar); // ReferenceError let bar = 2;Copy the code

Temporary dead zone

  • When you declare a variable in a block using let, a temporary dead zone occurs between the beginning of the block and the declaration of the variable. You cannot use the variable in this zone until you encounter its let statement
  • As long as a let command exists in the same scope, its declared variable is “bound” in that scope, regardless of whether it is declared externally
  • ES6 clearly states that if a block existsletandconstCommands, the variables that this block declares for these commands, form a closed scope from the start. Any time these variables are used before declaration, an error is reported.
  • In short, within the code block, useletThe variable is not available until the command declares it. This is grammatically called a “temporary dead zone.”
  • 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.
var tmp = 123; if (true) { tmp = 'abc'; // ReferenceError let tmp; } =============== var i = 5; (function () { console.log(i) // undefined var i = 10 })() =============== let j = 55; (function () { console.log(j) // ReferenceError: J is not defined let j = 77})() =============== (function HHH () {console.log(j) // j Uncaught ReferenceError: J is not defined let j = 77 // from the beginning of the function to here, are new j temporary dead zone})()Copy the code

Calling bar returns an error (some implementations may not) because the default value of argument x is equal to another argument y, which is undeclared and “dead zone.”

function bar(x = y, y = 2) { return [x, y]; } bar(); / / an errorCopy the code

The following code also reports an error and behaves differently from var.

Var x = x; // let x = x; // ReferenceError: x is not definedCopy the code

The above code error is also due to temporary dead zone. When declaring a variable with let, an error is reported whenever the variable is used before the declaration is complete. The above line is an example of this situation, where the value of x is fetched before the declaration of variable x is completed, causing an error “x undefined”.

Cannot duplicate declaration

let fx = 'fx is a great girl' let fx = 'fx is 18 years old' // Uncaught SyntaxError: Identifier 'fx' has already been declared ================= let fx3 = 'fx is a great girl' var fx3 = 'fx is 18 years old' // Uncaught SyntaxError: Identifier 'fx3' has already been declared ============== { let fx3 = 'fx is a great girl' var fx4 = 'fx is 18 years Old '} {let fx3 = 'fx is a good girl' var fx4 = 'fx is 8 years old'Copy the code

Arguments cannot be redeclared inside functions.

function func(arg) { let arg; Function func(arg) {{let arg; }} func(Copy the code

const

  • Declare a constant, which has most of the same characteristics as let
  • Only valid within the block-level scope of the declaration.
  • Declared constants are also non-promoted and have temporary dead zones that can only be used after the declared position.
  • Non-repeatable declaration

The declaration must be assigned, otherwise an error will be reported

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

For basic types like number,string, Boolean, and so on, it’s true that it declares a constant that doesn’t change, and whenever you change it, you get an error

const fx = 'fx is a great girl'
fx = 'NO' // Uncaught TypeError: Assignment to constant variable.
Copy the code

However, in the case of a reference type, it does not mean that the object’s content is unchanged, but that the object’s address is unchanged. That is, you can change the internal member of the object, but you cannot change the address of the variable.

const fx = { age: 18, name: 'fx' } fx ==> {age: 18, name: "fx"} fx.age = 8 {age: 8, name: "fx"} fx.weight = 90 {age: 8, name: "fx", weight: 90} fx = {} // Uncaught TypeError: Assignment to constant variableCopy the code

Since objects are referential, fx holds only Pointers to objects, which means that const only guarantees that the pointer does not change. Modifying an object’s properties does not change the object’s pointer, so it is allowed. That is, a reference type defined by const is allowed to change no matter how long the pointer does not change.

Here’s another example.

const a = []; a.push('Hello'); // execute a.length = 0; A = ['Dave']; / / an errorCopy 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.

Const object freezing method declared by const

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.

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

Properties of the top-level object

In the browser context it refers to the Window object and in Node it refers to the global object. 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.

According to ES6, for 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; A // 1 let b = 1; // This. A window. A // 1 let b = 1; window.b // undefinedCopy 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.

closure

A function that has access to a variable in the scope of another function

In simple terms, Javascript allows the use of internal functions – that is, function definitions and function expressions that reside inside a function of another function. Furthermore, these inner functions have access to all local variables, parameters, and other inner functions declared in the outer function they are in. A closure is formed when one of these inner functions is called outside the outer function that contains them.

  1. Closures are nested internal functions
  2. Closures exist in nested internal functions

The main functions of closures:

1. You can read variables inside a function

2. Keep the values of these variables always in memory and the variables or parameters are not collected by the garbage collection mechanism

Conditions for generating closures:

  1. Nested function
  2. An inner function references data from an external function (variable/function).

Advantages of closures:

  1. Variables reside in memory for a long time
  2. Avoid contamination of global variables

Disadvantages of closures:

  1. Resident memory increases memory usage
  2. Improper use may cause memory leaks
  3. Closures change the values of variables inside the parent function, outside the parent function

Closure use:

When it comes to getting local variables of f1 functions, normally this is not possible and can only be achieved through workarounds. That is, inside the function, define another function

function f1() { var n = 999; function f2() { console.log(n); / / 999}}Copy the code

In the code above, the function f2 is inside the function f1, and all local variables inside f1 are visible to F2. But the other way around, local variables inside F2 are invisible to F1. This is the JavaScript language’s unique “chain scope” structure, in which child objects look up, level by level, to all of the parent objects’ variables. Therefore, all variables of the parent object are visible to the child object, and vice versa. Since F2 can read local variables of F1, if f2 is returned, we can read internal variables outside of F1.

function f1() { var n = 999; function f2() { console.log(n); } return f2; } var result = f1(); result(); / / 999Copy the code

In the above code, the return value of function f1 is function f2. Since F2 can read f1’s internal variables, it can obtain F1’s internal variables externally. The closure is the function F2, which reads variables inside other functions. Because in the JavaScript language, only child functions inside a function can read internal variables, closures can be understood simply as “functions defined inside a function.” In essence, a closure is a bridge between the inside and outside of a function.

Closures are useful for reading variables inside functions and for keeping them in memory at all times

In the example below, closures cause internal variables to remember the result of the last call.

function fxs(fx) {
    return function () {
        return fx++;
    };
}
var inc = fxs(5);
inc() // 5
inc() // 6
inc() // 7
Copy the code

In the above code, fx is the internal variable of the function FXS. With closures, the state of FX is preserved, and each call is evaluated on the basis of the previous call. As you can see, the closure Inc makes the internal environment of the function FXS always exist. So, a closure can be thought of as an interface to a function’s internal scope. Why is that? The reason is that Inc is always in memory, and its existence depends on FXS, so it is always in memory and will not be collected by the garbage collection mechanism after the call ends.

Another use of closures is to encapsulate the private properties and methods of objects:

function Person(name) {
    var age
    function setAge(n) {
        age = n
    }
    function getAge() {
        return age
    }
    return {name: name, getAge: getAge, setAge: setAge}
}
var p1 = Person('fx')
p1.setAge(18)
p1.getAge() // 18
Copy the code

In the code above, the internal variable age of the function Person, via the closures getAge and setAge, becomes the private variable of the return object P1.

Note that each time the outer function is run, a new closure is generated, which in turn preserves the inner variables of the outer function, so memory consumption is high. Therefore, you should not abuse closures, which can cause performance problems for your web pages.

After the function is executed, the local variables in the function are not released, which will occupy the memory for a long time and easily cause memory leakage (memory is occupied but not used).

function fn1() { var arr = new Array[1000] function fn2() { console.log(arr.length) } return fn2 } var f = fn1() // Closure f() f = null has been generated to make internal functions garbage objects --> reclaim closureCopy the code

Problem sets 1

var name = 'The Window' var object = { name: 'My Object', getNameFunc: function () { return function () { return this.name } } } alert(object.getNameFunc()()) //The Window Var name = 'The window 'var object = {name: var name = 'The window' var name = {name: 'My Object', getNameFunc: function () { var that = this return function () { return that.name } } } alert(object.getNameFunc()()) // My ObjectCopy the code

Problem set 2

function outerFun() { var a = 0; function innerFun() { a++; alert(a); } return innerFun; } var obj = outerFun(); obj(); // result is 1 obj(); Var obj2 = outerFun(); obj2(); // result is 1 obj2(); // The result is 2Copy the code

Problem set 3

function foo(x) { var tmp = 3 function bar(y) { console.log(x + y + (++tmp)) } bar(10) } foo(2) // 16 foo(2) // 16 Foo (2) // 16 foo(2) // 16 this is just a function call, Not closure = = = = = = = = = = = = = = = = = = = = = = = = function foo (x) {var TMP = 3 return function (y) {the console. The log (x + y + + TMP) (+)}} var Bar = foo(2) bar(10) // 16 bar(10) // 17 bar(10) // 18 bar(10) // 19 When you return an internal function, it is a closure. A function that accesses its external variables is a closureCopy the code

A classic closure interview question

function fun(n, o) { console.log(o); return { fun: function (m) { return fun(m, n); } } } var a = fun(0); // undefined a.fun(1); // 0 a.fun(2); // 0 a.fun(3); // 0 var b = fun(0).fun(1).fun(2).fun(3); //undefined,0,1 var c = fun(0).fun(1); //undefined,0 c.fun(2); // 1 c.fun(3); / / 1Copy the code

A:

The fun property of the object returned by return corresponds to a newly created function object. This function object forms a closure scope that allows it to access the outer function variable N and the outer function fun. In order not to confuse fun with fun, Function fun(n,o){console.log(o); function fun(n,o){console.log(o); return { fun:function(m){ return fun(m,n); } }} var a = fun(0); // undefined generates closure n = 0a.fun(1); // 0 creates a closure, but since there is no binding variable, the closure disappears immediately. // 0 creates a closure, but since there is no binding variable, the closure disappears immediately. Var b = fun(0).fun(1).fun(2).fun(3); var b = fun(0).fun(2).fun(3); Var b1 = b.foo (1); // undefined,0,1; var b2 = b1.fun(2); var b3 = b2.fun(3); var c = fun(0).fun(1); // undefined,0,c.fun(2); // 1 The current closure of c is 1c.fun(3); // 1 The current closure of C is 1. Although c.sun (2) and c.sun (3) both generate new closures, the closure then disappears because no new variables are assigned

Stack (stack) is short for stack memory. The stack automatically allocates a relatively fixed size of memory space and is automatically released by the system. Heap is short for heap memory. The heap is dynamically allocated memory, which varies in size and is not automatically freed.

Basic data types:

  • They are stored directly on the stack by value and can be accessed directly by value
  • The amount of memory occupied by each type of data is determined and automatically allocated and released by the system.
  • The advantage of this is that memory can be reclaimed in a timely manner
  • Memory space is easier to manage than a heap.
var a = 10
var b = ab = 20
console.log(a) // 10
console.log(b) // 20
Copy the code

Reference type data:

  • They are copied and new, and such data is stored in the heap.
  • The size of the memory space is uncertain.
  • When we want to access a value of a reference type, we need to get the object’s address pointer from the stack, and then use the address pointer to find the data in the heap.
var obj1 = new Object()
var obj2 = obj1
obj2.name = 'fx'
console.log(obj1.name) // fx
Copy the code

The two reference data types refer to the same heap memory object. Obj1 is assigned to obj2, and the heap object is actually copied to obj2 at the stack reference address, but in fact they both refer to the same heap object, so modifying obj2 is actually modifying that object, so it can be accessed through obj1.

Copy a pointer stored in the stack into the space allocated by the new variable on the stack. The copy of the pointer points to the same object stored in the heap as the original pointer. After the copy operation is complete, both variables will actually refer to the same object. Therefore, when used, changing the value of one of these variables affects the other variable.

var b = a;
Copy the code

b.sex = 'boy';
Copy the code

The biggest difference between base types and reference types is really the difference between value and address

Value passing: Base types use value passing. Address pass: The reference type is address pass, assigning the address stored in stack memory to the variable received.

Value and address transmission

var arr1 = [1, 2, 5, 8]; var arr2 = arr1; var str1 = arr1[2]; console.log(arr2); // 1, 2, 5, 8 console.log(str1); // 5 arr2[4] = 99; Arr1 console.log(arr2); arr1 console.log(arr2); //1, 2, 5, 8, 99 console.log(arr1); //1, 2, 5, 8, 99 arr1[1] = 3 // The same changes to arr1 will affect arr2 console.log(arr1); //1, 3, 5, 8, 99 console.log(arr2); //1, 3, 5, 8, 99 str1 = 6; // str1 is a basic type of data and changes do not affect console.log(arr2); //1, 3, 5, 8, 99 console.log(arr1); //1, 3, 5, 8, 99 console.log(arr1[2]); / / 5Copy the code

The above example shows that when I change the data in ARR2, the data in ARR1 also changes, but when I change the data in STR1, ARR1 does not change. Why is that? That’s the difference between value and address.

Because ARr1 is an array and a reference, it is assigned to ARR2 with the address in the stack (equivalent to creating a new “pointer” with a different name) rather than the value of the object in the heap. Str1 gets an assignment of a primitive type, so str1 simply fetches a value from arR1 heap memory and stores it directly on the stack. Arr1 and ARR2 both point to the same heap memory. When ARR2 changes the heap memory, arR1 will be affected. Str1 is directly modified in the stack, and the data in ARR1 heap memory will not be affected.

Why do base data types stay on the stack and reference data types in the heap?

  • The heap is larger than the stack, and the stack is faster than the heap;
  • Basic data types are relatively stable and use relatively little memory;
  • Reference data type size is dynamic, and it is infinite, the size of the reference values will change, can’t put it on the stack, otherwise it will reduce the speed of the variable to find, so the value of the variables on the stack space is the address of the object is stored in the heap, address size is fixed, so it is stored in the stack on the variable performance without any negative effects;
  • Heap memory is unordered storage and can be retrieved directly by reference;

Problems with variables stored in closures

The stack is destroyed when the function is finished executing.

function count () { let num = -1; return function () { num++; return num; } } let numCount = count(); numCount(); // 0 numCount(); / / 1Copy the code

In conclusion, the num variable is created when the count function is called and popped off the stack when the return function is called. Given this logic, how do we get 0 by calling numCount? Num is destroyed in memory when the function returns. Therefore, the underlying JavaScript type in this case is not stored on the stack, but should be stored in the heap for use by the numCount function.

Dump the stack and store data only in the heap

function test() { let num = 1; let string = 'string'; let bool = true; let obj = {attr1: 1, attr2: 'string', attr3: true, attr4: 'other'} return function log() { console.log(num, string, bool, obj); }}Copy the code

With the call of test, to ensure that the variable is not destroyed, create an object called Scope in the heap, and store the variable as an attribute of Scope. The data structure in the heap looks roughly like this:

Since the Scope object is stored in the heap, the returned log function has full access to the Scope object. Here’s what this code looks like in Chrome:

The JavaScript variables in the example are not in the stack, but in the heap, stored using a special object (Scopes).

How exactly is a variableJavaScriptStored in the

There are three types of variables in JavaScript:

  1. A local variable
  2. Captured variable
  3. The global variable

A local variable

An object declared in a function that is not used by other scopes after the function returns. All fx* in the following code are local variables.

function fxFn() {
    let fx1 = 1
    var fx2 = 'str'
    const fx3 = true
    let fx4 = {name: 'fx'}
    return
}
Copy the code

Captured variable

A captured variable is the opposite of a local variable: declared in a function but used by an unexecuted scope (function or class) after the function returns. Fx * in the following code are captured variables.

function fxFn() {
    let fx1 = 1
    var fx2 = 'str'
    const fx3 = true
    let fx4 = {name: 'fx'}
    return function () {
        console.log(fx1, fx2, fx3, fx4)
    }
}
let fx = fxFn()
console.dir(fx)
Copy the code

function fxFn() {
    let fx1 = 1
    var fx2 = 'str'
    const fx3 = true
    let fx4 = {name: 'fx'}
    return class {
        constructor() {
            console.log(fx1, fx2, fx3, fx4)
        }
    }
}
let fx = fxFn()
console.dir(fx)

Copy the code

Copy code toChromeYou can view the output object under[[Scopes]]I have the correspondingScope.

The global variable

The global variable is called global, which is window in the browser and global in node. Global variables are added by default at the bottom of the function scope chain, which is the last one in the [[Scopes]] function above.

Global variables need special attention: var and let/const are different.

Var: globalvarThe variable is just thetaglobalObject adds a property.

var name = 'fx'; Windows. name = 'fx';Copy the code

Let/const: globallet/constVariables don’t changewindowsObject, but put the variable declaration under a special object (andScopeSimilar).

var pwd = 123
Copy the code

Variable assignment

In fact, whether a variable is stored in the stack or in the heap (in memory anyway), its structure and the way of storing value is similar, there are the following structure:

Assign a constant

What is a constant? Constants are declarative values, such as 1, “string”, true, {a: 1}, which are constants

Suppose we now have the following code:

let foo = 1
Copy the code

JavaScript declares a variable foo and gives it a value of 1, and the following changes occur in memory

If we now declare a bar variable:

let bar = 2
Copy the code

Then memory would look like this:

For object types

let obj = {    
	foo: 1,    bar: 2
}
Copy the code

The memory model is as follows:

If we set obj.foo = ‘foo’ in the memory region 0x1021, the memory address obj points to does not change. Therefore, the object is constant!

Assign to a variable

What is a variable? Foo, bar, and obj in the above procedure are all variables, and variables represent a reference relationship whose value is not determined.

So what happens if I assign the value of one variable to another variable?

let x = foo

As shown above, simply referring x to the same address value as foo does not use any new memory space.

OK assignment ends here, and modification is next.

Variable changes

Like variable assignment, variable modification needs to be done in one of two ways, depending on the type of variable to the right of the = sign:

Change to constant

foo = ‘foo’

As shown in the figure above, ‘foo’ is saved in memory and the reference address of foo is changed to 0x0204.

Change to a variable

foo = bar

As you can see above, you just changed the address referenced by Foo.

How const works

Const is a way of declaring a variable new to ES6. Variables decorated with const cannot be changed.

In JavaScript’s variable store diagram, the memory address that a variable points to cannot change. That arrow can’t be changed.

For example:

const foo = 'foo';
foo = 'bar'; // Error
Copy the code

As the diagram above shows, foo cannot refer to other address values. Ok, now can you solve your confusion about the following code:

const obj = {    
	foo: 1,    
	bar: 2
};
obj.foo = 2;
Copy the code

The address referenced by obJ has not changed, and the changed part is another area. As shown in the figure below

Object modification

OK entering an interview question that’s extremely easy to ask:

let obj1 = {foo: 'foo', bar: 'bar'}
let obj2 = obj1;
let obj3 = {foo: 'foo', bar: 'bar'}
console.log(obj1 === obj2);
console.log(obj1 === obj3);
obj2.foo = 'foofoo';
console.log(obj1.foo === 'foofoo');
Copy the code

Say the results of console in turn.

Instead of talking about the results, let’s look at the structure in memory. So the result is true false true