Function closure scope understanding and problem sets

Basic knowledge

1. Create functions

  • Create a space in the heap with a hexadecimal address
  • Store something into space
  • Scope [[scope]] In which context is the scope created
  • The function body code is stored as a string
  • Functions are also objects and store key-value pairs
    • For example,
    • The name key
    • Length Length of a variable
  • Associates the heap memory address to the corresponding variable

Function performs

  • Generates a “brand new” “private context” EC(?) , there is an AO(?) in the context. Private variable object, used to store private variables declared in the current context [AO is a branch of VO]

  • Initialize scope-chain

    < own current context, function scope {parent context}>

  • Initialize THIS

  • To initialize the ARGUMENTS

  • Parameter assignment -> Parameters are also private variables stored in the AO

  • Variable promotion -> Private variables declared in the context are private variables stored in the AO

  • Code execution

The scope chain

When executing code in a function, if we encounter a variable, we first check if it is a variable in our own private context (AO).

[protect] + If the variable is not private: look in the parent context, if not, continue to look in the parent context... Until the EC(G) global context is foundCopy the code

If global (including GO) does not have it

XXX is not defined + If the variable value is set, it is equivalent to setting the corresponding attribute in GOCopy the code

Function closure scope problem

var x = [12.23];
function fn(y) {
    y[0] = 100;
    y = [100];
    y[1] = 200;
    console.log(y);
}
fn(x);
console.log(x); 

/* [100, 200] [100, 23] */
Copy the code
let x = 5;
function fn(x) {
    return function (y) {
        console.log(y + (++x)); }}let f = fn(6);
f(7);
fn(8) (9);
f(10);
console.log(x);

/* 14 18 18 5 */
Copy the code
let a = 0,
    b = 0;
function A(a) {
    A = function (b) {
        alert(a + b++);
    };
    alert(a++);
}
A(1);
A(2); 

/*
	1
	4
*/
Copy the code
var arr = [11.22];
function f(ary) {
    console.log(ary);
    ary[0] = 100;
    ary = [Awesome!];
    ary[0] = 0;
    console.log(ary);
}
f(arr);
console.log(arr)

/* [11,22] [0] [100,22] */
Copy the code
var a = 1;
var b = 1;
function f() {
    console.log(a, b)
    var a = b = 2;
    console.log(a, b)
}
f();
console.log(a, b)

/* undefined 1 2 2 1 2 */

If a variable is not defined in GO, then it is defined in GO */
var a = 1;
var b = 1;
function f() {
    var a;
    console.log(a, b)
    a = b = 2;
    console.log(a, b)
}
f();
console.log(a, b)
Copy the code
var i=0;
function A() {
    var i=10;
    function x() {
        console.log(i);
    }
    return x;
}
var y = A();
y();
function B() {
    var i=20;
    y();
}
B();

/*
	10
	10
*/
Copy the code
/* 1. All function fn(){} will raise the variable by 2. Function execution 3. Duplicate definitions of a function override previous definitions */
fn();
function fn() { console.log(1); }
fn();
function fn() { console.log(2); }
fn();
var fn = function () { console.log(3); }
fn();
function fn() { console.log(4); }
fn();
function fn() { console.log(5); }
fn();

/* 5, 5, 3, 3, 3 */
Copy the code
var b = 0; 
function fn(a){
    console.log(a,b);
    var a = b= 2;	
}
fn(1)

/*
	1 0 
*/
/ / equivalent to
var b = 0; 
function fn(a){
    var a;
    console.log(a,b);	/ / 1, 0
   	a = b= 2;	
}
fn(1)   
Copy the code
console.log(a, b, c);   //=>undefined undefined undefined var a = b= 1; The variable written like this increases by a but the comma increases both variables
var a = 12,
    b = 13,
    c = 14;
function fn(a) {
        console.log(a, b, c);	/ / = > 10 13 14
        a = 100;
        c = 200;
        console.log(a, b, c);	/ / = > 100 to 13, 200
}
b = fn(10);
console.log(a, b, c); //=>12 undefined 200
Copy the code
var a = 1;
function fn(a) {
    console.log(a)	ƒ a() {}
    var a = 2;
    function a() { }
}
fn(a);
Var a; Function fn(a){} 2. Function fn(a){} */
Copy the code

Function is a big cheating man who plays with women’s feelings

1. Variables are declared but not defined 2, except in {} braces for functions and objects. If in braces let/const/function creates a block-level private context, var creates a global variable. Global initialization declaration (only declared but not defined) 2. Block-level scope variable declaration plus variable definition (var declaration,function declaration plus definition) 3. Function foo() {} returns the result of previous execution to function variable 4 in the global context. All subsequent operations are on block-level scopes */
{
    function foo() {}
    foo = 1;
    console.log(foo)	/ / = > 1
}
console.log(foo);  / / = > function

In the {} braces except for functions and objects, variables are only declared and not defined, so there is a value of undefined 2 in the whole field. Function foo() {} returns the previous operation to global foo(function foo() {}) + foo = Log (foo) //=> 1 + global console.log(foo); // => function */

{
    function foo() { }
    foo = 1;
    function foo() {}}console.log(foo);	/ / = > 1

/ * -- -- -- -- -- - * /
{
    function foo() {}
    foo = 1;
    function foo() {}
    foo = 2;
}
console.log(foo);	/ / = > 1
Copy the code

Pit for the es6 parameter assignment

/ * conditions: @1 uses the parameter assignment default value whether any type of value is assigned & Whether the argument is passed or not, the default value is valid.] @2 In the function body, Let /var/const specifies that a variable is declared based on let/var/const. If both of the above conditions are met, the function generates two private contexts: Private context + + function block level private context And block-level private context "superior context", is the function of private context If block-level private variables and functions in the context of a private in the context of a variable name, the same is in the block before "code", first of all, the function in the context of this variable's value, Synchronize this variable */ in the block-level context
var x = 1;
function func(x, y = function () { x = 2 }) {
    x = 3;
    y();
    console.log(x); / / = > 2
}
func(5);
console.log(x);	/ / = > 1

/ * - * /
var x = 1;
function func(x, y = function(){x = 2}){
    var x = 3; 
    y();
    console.log(x);	/ / = > 3
}
func(5);
console.log(x);		/ / = > 1

/ * - * /
var x = 1;
function func(x, y = function () { x = 2 }) {
    var x;
    var y = function () { x = 4 };
    y();
    console.log(x);		/ / = > 4
}
func(5);
console.log(x);			/ / = > 1
Copy the code

Mapping between function parameters and arguments

/* 1. In non-strict mode, parameters and arguments generate one-to-one mapping 2. In strict mode the mapping is cancelled

var a = 4;
function b(x, y, a) {
    console.log(a);		/ / = > 3
    arguments[2] = 10;	
    console.log(a);		/ / = > 10
}
a = b(1.2.3);
console.log(a);			//=>undefined

/*-- in strict mode --*/
"use strict"
var a = 4;
function b(x, y, a) {
    console.log(a);		/ / = > 3
    arguments[2] = 10;	
    console.log(a);		/ / = > 3
}
a = b(1.2.3);
console.log(a);			//=>undefined
Copy the code
Function (){alert(I *= 2); function(){alert(I *= 2); function(){alert(I *= 2); } 3.test(5) executes the accepted function and does not find I to the parent scope */
var test = (function (i) {
    return function () {
        alert(i *= 2);
    }
})(2);
test(5);	/ / = > 4
Copy the code
function fun(n, o) {
    console.log(o);
    return {
        fun: function (m) {
            return fun(m, n);	
        	Function (m){} returns fun(m, n) */}}; }var c = fun(0).fun(1);	
c.fun(2);
c.fun(3);

/* undefined 0 1 1 */
Copy the code

Perform function nomenclature quickly

/* Special 1. Variables in the immediate function cannot be accessed externally. 2. Quick line function namespaces are very low weight, when encountered var can change the variable */
var b = 10;
(function b() {
    b = 20;
    console.log(b);/ / = > function; The change to B is invalid}) ();console.log(b);	/ / = > 10;

/ * - weight - * /
var b = 10;
(function b() {
    var b = 20;
    console.log(b);/ / = > 20}) ();console.log(b);	/ / = > 10;
Copy the code

The use of closures in functions

Closure application: Closure handling scheme in loop

  • Circular event binding
    • User-defined attribute data-index
      • N methods in closures (LET handling mechanism)
      • Event delegation
  • Timer in loop
    • Closure handling scheme
    • Processing scheme of the timer itself

Problem with loop event binding using var

/* Code error: no matter which control is clicked, the output is 3. BTNS [I].adDeventListener ("click",function(){console.log(I)}; We just bind the method to the control, and call the bound function when the control is triggered and I is still looking for the global I = 3}) */
let btns = Array.from(document.querySelectorAll(".btn"));
for(var i = 0; i<btns.length; i++){ btns[i].addEventListener("click".function(){
        console.log(i)
    })
}
Copy the code

Solution one closure

Closure solutions that leverage the “save” mechanism of closures

/* For each round of the loop, a closure is created that stores its own private variable I and its value is the index of each round of the loop; When you click the button, execute the corresponding function, and encounter a variable I, do not look globally, but instead look in the closure to which it belongs... * /
let btns = Array.from(document.querySelectorAll(".btn"));
for(var i = 0; i<btns.length; i++){ (function(i){
        btns[i].addEventListener("click".function(){
        console.log(i)
    })})(i)
}

/ * - * /
let btns = Array.from(document.querySelectorAll(".btn"));
for(var i = 0; i<btns.length; i++){// The next line of I to K does not generate a closure because there is nothing external to the current context
    btns[i].addEventListener("click", (function(i){
        return function (){
            console.log(i)
        }
    })(i))
}

/*--forEach is still a closure --*/
let btns = Array.from(document.querySelectorAll(".btn"));
btns.forEach((item,i) = > {
    btns[i].addEventListener("click".function(){
        console.log(i)
    })
});
Copy the code

1. A parent private scope is first generated to control the five child private scopes in the loop */
let btns = Array.from(document.querySelectorAll(".btn"));
for(let i = 0; i<btns.length; i++){ btns[i].addEventListener("click".function(){
        console.log(i)
    })
}
Copy the code

Solution 2: Customize attributes

let btnList = document.querySelectorAll('.btn');
let i = 0;
for (; i < btnList.length; i++) {
    // At the beginning of each loop, each button object is assigned a custom property myIndex, which stores its index
    btnList[i].myIndex = i;
    btnList[i].onclick = function () {
        // Every time you click, get the value of the previously stored custom attribute based on THIS(the current action element)
        console.log('Index of currently clicked button:The ${this.myIndex}`);
    };
}
// Performance is better than closures, but there are some performance costs {element objects & node collections & binding methods are open heap memory} */
Copy the code

Solution 3: The ultimate solution event delegate

Solution 3: The ultimate solution event delegate// Click each button. In addition to triggering the button's click event, according to the bubble propagation mechanism, it will also trigger the body's click event
document.body.onclick = function (ev) {
    let target = ev.target;
    if (target.tagName === 'BUTTON' && target.className === "btn") {
        // The event source for the click is the button
        let index = target.getAttribute('data-index');
        console.log('Index of currently clicked button:${index}`); }};Copy the code

Timer problem

Is it possible to output 0, 1, 2 every one second?

/* a: can output every second but cannot output 0,1,2 cause: when the timer is a macro task output, the global I becomes 3 */
for (var i = 0; i < 3; i++) {
    setTimeout(function () {
        console.log(i);	/ / = > 3 3 3
    }, i * 1000); // wait time 0ms(5-7 ms) 1000ms 2000ms
}
Copy the code

The solution

A parent private scope is generated to control the three child private scopes in the loop */
for (let i = 0; i < 3; i++) {
	setTimeout(function () {
   		 console.log(i);	/ / = > 0 1 2
	}, i * 1000);
} 

/* @2 immediately executes the function to generate the private scope so I don't have to look up */ in the global scope
var i = 0;
for (; i < 3; i++) {
    (function (i) {
        setTimeout(function () {
            console.log(i);
        }, i * 1000);
    })(i);
}

/* @3 is almost the same closure as method 2 */
const fn = i= > {
    return function () {
        console.log(i);
    };
};
let i = 0;
for (; i < 3; i++) {
    setTimeout(fn(i), i * 1000);
}

/ * * / @ 4
let i = 0;
for (; i < 3; i++) {
    // Set timer:
    // Parameter 1: callback function to execute the scheme at time
    // Parameter 2: wait time
    // Argument 3: the argument to the callback function is "pre-passed".
    setTimeout(function (i) {
        console.log(i);
    }, i * 1000, i);
} 
Copy the code