Wang Fupeng bosses tutorial transparent, portal: www.cnblogs.com/wangfupeng1…

This article is also in the previous study to read more leaders of the article after the summary, some code examples are also directly used, delete ~~~

Execution context

What is an execution context (also called an “execution context”)? Leaving definitions aside, let’s look at some code:

console.log(a) //ReferenceError: a is not defined

console.log(a) //undefined
var a

console.log(a) //undefined
var a = 100
Copy the code

A is not defined. The second and third sentences are undefined, indicating that the browser knows that a is undefined when console.log(a) is executed, but does not know that a is 10.

Before a piece of JS code can be taken and actually run sentence by sentence, the browser has done some preparatory work, which is what we call variable promotion.

Not only that, notice the two nouns in the code comments — function expressions and function declarations. Although both are commonly used, they are two different things when it comes to preparation.

console.log(f1)  //function f1(){}
function f1(){} 
Copy the code

The above example is a function declaration, which is read first in the process of parsing the file. That is, the browser knows that the function is called

This is fine even if the function call comes first and the function declaration comes last in the JS file

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

The above example is a function expression, which is actually a var a function process

When parsing functions, browsers will put the var stuff first

Var f1 = undefined, so what does that function do?

So something must have gone wrong with the execution

In a script file, at the beginning of the file or function execution, the browser reads the function/variable declaration first, and takes out some of the assigned variables.

We need to know what has been done in preparation:

  • Variable, function expression — variable declaration, default assigned to undefined;
  • This – the assignment;
  • Function declaration — assignment;

These three data preparation situations are called “execution context” or “execution context”.

To prove our point, write the following code on the console, as you will see

So we can understand the following two different cases case 1

fn('zhangsan'); function fn(name){ alert(name); } // There is no error after running, and the web page pop-up warning boxCopy the code

Case 2

f1('zty');
var f1 = function(name ){
  alert(name)
}
Copy the code

Functions in function declarations can be anonymous, but function assignments cannot.

Let’s take a look at a classic question:

function test() { console.log(1); } (function () { if (false) { function test() { console.log(2); } } test(); }) (); //test is not a functionCopy the code

In early browsers, the result would have been 2 (function test() {} would have been pushed to the top of the function scope because the variable was raised) but if it had been 2, our if statement would have made no sense at all. Var test() is not a function. If (test() is not a function, test() is not a function. It’s said that in addition to if, the same goes for while, switch, and for.

About this

www.ruanyifeng.com/blog/2018/0… The above is Teacher Ruan Yifeng’s understanding of some internal mechanisms of this

Until the arrow function comes out, we just know that this doesn’t know what it refers to until the function executes, okay

The main scene

  1. Execute as a constructor

    function Foo(name){ this.name = name; alert(name); console.log(this.name); } var f1 = Foo('zhangsan'); // Zhangsan var f2 = Foo('hello'); // Prints helloCopy the code

Here, the existence of this is more meaningful, many objects call the same method, this refers to many objects, can be used flexibly, there is no need to write so many of the same function

  1. Execute as an object property

     var obj = {
         name:'a',
         fn: function(){
             console.log(this.name);
       }
     }
      obj.fn();   //a
    Copy the code

This refers to the object that executes this property, so it’s a. What if it’s not called as a property of obj?

    var obj = {
        name:'a',
        fn: function(){
            console.log(this.name);  
            console.log('kkk');
      }
    }
    var fn1 =  obj.fn;  
    fn1()//undefined
Copy the code

As in the above code: if fn is assigned to another variable and is not called as a property of obj, then this is the window and this.x is undefined.

  1. Execute as a normal function

    function fn(){ console.log(this); } fn();   //  This points to the global objectCopy the code
  2. call bind apply

    Function fn1(name, age){console.log(name); function fn1(name, age){console.log(name); console.log(this); } fn1. Call (100} {x: 'zhangsan, 20). // Execution result: Tom {x:100)} // This is the name of the call. This is the name of the call. This is the name of the call. Call ({x:200},['zhangsan',20]); var f2 = function(name){ console.log(name); console.log(this); }.bind({y:100},'lisi')Copy the code

In fact, this in JS has always been an enduring topic in the front end, many questions are on this.

So let’s stop there and do a couple of homework problems.

var obj = { x: 10, fn: function(){ function f(){ console.log(this); //Window } f() } }; obj.fn()Copy the code

What? The answer is obj, which means that when executed as an object property it points to an object.

F is defined inside obj.fn, but it is still a normal function, and this still refers to window. The statement that refers to an object when executed as an object property refers to fn in this function, not f, which is defined in obj, but f in obj.fn. I don’t know. Do you understand this explanation?

var x = 3; var y = 4; var obj = { x: 1, y: 6, getX: function() { var x =5; return function() { return this.x; } (); }, getY: function() { var y =7; return this.y; } } console.log(obj.getX()) console.log(obj.getY())Copy the code

Give yourself two minutes to think about this problem and the answer should be: 3, 6

Let’s explore! Obj. getX is a function defined in obj.getX. This refers to the window. From the perspective of scope, it will look for its own this.name in the first layer. The execution environment of an anonymous function is global and points to the window at this point because it is a normal function. So it’s going to be 3. GetY is a property of obj. When executed as a property of obj, it points to obj, and the x in the scope of obj is equal to 6.

Let’s move on.

var length = 10;
function fn(){
console.log(this.length);
}
var obj = {
    length:5,
    method:function(fn){
    fn();
    }
}
obj.method(fn);
Copy the code

The answer is 10. Same idea. Function fn refers to global.

This pointing is also an important feature of the ES6 arrow function, which will be covered in a separate article next time.

About scope

Speaking of one of JS’s big mountains, is our well-known closure. But before we get to closures, we need to talk about scopes!

First, it should be clear that Js has no block-level scope

    #include <stdio.h> 
    void main() { 
      int i=2; 
      i--; 
      if(i) { 
        int j=3; 
     } 
      printf("%d/n",j); 
    }
Copy the code

In C, for example, this will pop up an error because j is defined in the if block, and if it jumps out of that block, it will not be accessible. However, in Js, different blocks are still accessible.

if(true){ var i = 7; }console.log(i); / / 7Copy the code

Parent scope

var a = 100;
    function fn1(){
        var b = 200;
        console.log(a);
        console.log(b);
    }
Copy the code

The parent scope of fn1 is Window

The number defined inside the function cannot be changed from the outside, and the outside cannot pollute the number inside. Look at the following questions:

var a = 100; function f1(){ var b = 200; console.log(a); console.log(b); } var a = 10000; var b = 20000; f1(); / / 10000 200;Copy the code

In javascript, in addition to global scopes, only functions can create scopes.

So, when we declare variables, global code should be declared at the front of the code, and functions should be declared at the beginning of the function body.

I stepped in a hole before!

let person = {
    a:2,
    say:()=>{
        console.log(this.a)
    }
}
Copy the code

Because I’m writing the method inside the object, and the object’s parentheses don’t close the scope. So this is still pointing to the global object, so it’s not pointing to person.

Let’s move on to scope:

As shown in the figure above: global, FN, and bar all have their own scopes, and scopes have the relationship between the upper and lower levels. The relationship between the upper and lower levels depends on which scope the function is created in. For example, if a bar function is created under the fn scope, the “fn scope” is the parent of the “bar scope”.

The purpose of a scope is to isolate variables, so that variables with the same name in different scopes do not conflict. If bar needs a, take a below bar, if fn needs a, take a below fn, and if global needs a, take global A.

ES6 lets combine with what we call blocks to achieve the desired block-level scope effect.

Scope and context

closure

www.ruanyifeng.com/blog/2009/0…

Closure usage scenarios:
  1. The function returns a value

    function f1(){ var a = 100; return function(){ console.log(a); } } var f2 = f1(); //f2(){console.log(a)}, var a = 200; // The a here is different from the a above. / / 100,Copy the code
  2. The function is passed as an argument

    function f1(){ var a = 100; return function(){ console.log(a); } } var f2 = f1(); function f3(fn){ var a = 200; fn(); } f3(f2); / / 100Copy the code
What’s special about closures

Take a look at this code:

function compare(value1,value2){ if(value1<value2){ return -1; }else if(value1>value2){ return 1; }else{ return 0; }} var result = compare(5,10)Copy the code

Compare () is defined and then called in the global scope.

When the first call to compare (), creates a contain this, the arguments. Value1, value2 active objects, (in the compare () function scope of the first chain). The global execution environment variable object (this,result,compare) is second in the scope of compare().

The background environment has a variable object — a variable object. The global environment variable object is always present, like compare(), which is local and only exists during the execution of the function. When you create compare, you create a scope chain containing the global variable object, stored in the internal scope property. When you perform compare, you create an execution environment for the function, and then copy the objects from the scope property to build your scope chain. Then some variable objects inside functions are pushed to the front of the scope chain. In compare(), the scope includes global and local variables. A scope chain is essentially a list of Pointers to a variable object.

In general, local variables (live objects) are destroyed after the function completes execution. Only global scope chains are held in memory, but closures are different.

Closure of the case, on the outside of the function of the definition of internal function will function of the activities of the object to add to its scope, in the code below, from anonymous function f1 () is returned, his scope chain is initialized to f1 function activity object of global variables, anonymous functions can access all all the variables defined in formula one, More importantly, f1’s live object is not destroyed when the function is finished, because the anonymous function’s scope chain still references the live object, and f1’s live object is not destroyed until the anonymous function is destroyed.

function f1(){ var a = 100; return function(){ console.log(a); Var result = f2()// call f2 = null // unreference the anonymous function.Copy the code

Related Interview questions

Let me give you an understanding of variable promotion

Declarations of functions and variables in Js are always raised by the parser to the top of the method body. (Note that function expressions do not.) Look at the scope section above for the simplest example:

console.log(f); var f = 100; Var e = 100; var e = 100; console.log(e); //100 fn('zhangsan'); function fn(name){ this.name = name; alert(this.name); }// this function is called f5('xiaosan'); var f5=function(name){ this.name = name; alert(this.name); } // An error occurs because this is an assignment of a function rather than a declarationCopy the code
Closure block-level scope

This code is usually given to show you the possible results

Please write the output value of the click below and print the correct numbers in Li in three ways.

<ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> </ul> <script type="text/javascript"> var list_li  = document.getElementsByTagName("li"); for (var i = 0; i < list_li.length; i++) { list_li[i].onclick = function() { console.log(i); } } </script>Copy the code

It is a very classic problem really !!!! You can close your eyes and say that no matter which li you click on, it will only output 6. (Related to the single thread and asynchronous queue of Js)

There are three solutions:

  • The ES6 let

    for (let i = 0; i < list_li.length; i++) { list_li[i].onclick = function() { console.log(i+1); }}Copy the code

For more information about let, see temporary dead zones.

  • Closure, execute the function immediately

    for(var i = 0; i < list_li.length; i++){
      (function(i){
          list_li[i].onclick = function(){
              console.log(i+1)
          }
      })(i)
    }
    Copy the code

The closure holds this variable.

  • The last way is when Jia Ge said it, suddenly realized, this is the most in solution ah damn. This is not a closure or scope.

    for (var i = 0; i < list_li.length; i++) { list_li[i].onclick = function() { console.log(this.innerHTML); }}Copy the code
Supplement knowledge

Executing the function immediately creates a separate scope for each loop

(function(){})(I)

var a = 2; (function foo(){ var a = 3; console.log(a); / / 3}) (); console.log(a); / / 2Copy the code

The function expression is executed immediately after a parenthesis. A very common advanced usage is to call them as functions and pass them in

var a = 2; (function IIFE(global){ var a = 3; console.log(a); //3 })(window); console.log(a); / / 2Copy the code
How to understand scope
  1. Free variables

  2. Scope chains, that is, lookups of free variables

  3. Two scenarios for closures

    var a = 100; function f1(){ console.log(a); // we can't find a in the function scope, we call a free variable}Copy the code

If we can’t find a in f1, we go up the scope chain, and the parent scope of A is window(see where f1 is defined) and we find var a = 100; Scenario 1: The function is passed as an argument Scenario 2: the function is returned as a value. A closure is generated when a function returns a value from another function that calls a value inside its parent function and is executed outside the parent function. Closures are functions that have access to variables in the scope of another function. A common way of creating a closure, it is inside a function to create another function The following example, for example, external performed this function, can be access to the variable a, are able to access this variable, mainly because of the internal function scope contains outside the scope of the function f1.

function f1(){ var a = 100; return function(){ console.log(a); } } var f2 = f1(); //f2(){console.log(a)}, var a = 200; // The a here is different from the a above. / / 100Copy the code

Essentially, whenever and wherever you pass functions around as first-level value types, you’ll see closures applied to those functions. Whenever you use callbacks in timers, event listeners, Ajax requests, cross-window communication, Web workers, or other tasks, you’re actually using closures.

Here is a pitfall you may encounter if you do not understand closures and scopes

for(var i = 1; i <=5; i++){ setTimeout(function timer(){ console.log(i); }, I *1000)}// Pop up 5 6'sCopy the code

Contrary to our expectation, we expected 1,2,3,4,5 to pop up in sequence. Based on how scopes work, the reality is that although the five functions in the loop are defined separately in each iteration, as in the code above, they are all enclosed in a shared global scope. Each time through the loop, Each function holds active objects in the global scope, including, that is, the scope chain each function are preserved in the global scope of I, when the main thread of i++ after these operations, I have become a 6, the closure function contains only last a value of any variable is 6, When the timer function is executed, the setTimeout encountered during JS parsing will pull out the callback and put it in a separate queue. When the current processor is idle, the callback in that queue will be executed. The processor completes the for loop before executing SetTimeout. This I in the global variable is 6, so it’s not going to work.

The solution

for(var i = 1; i<=5; i++) (function(i){ setTimeout(function timer(){ console.log(i); // I is a free variable, and we find the immediate function passing in I (the immediate function is executed after each for loop). That is, the function opens a separate block scope for each loop. In ES6, {} is a block scope. Don't be afraid to be covered}, I *1000)})(I);Copy the code

In fact, we can get the desired effect by changing the var in front of I to let, because let also declares a block-level scope.

(function(I))(j) passes the value of j to I, creating a block-level scope. (function(I))

The role of closures in real-world development
Closures are often used in modularity
function f1(){ var something = "something"; return function(){ console.log(something); } } var foo = f1(); foo(); // The outside can't touch the inside of the functionCopy the code
Practical application 2

Used to encapsulate variables, convergent permissions.

function firstLoad(){ var _list = []; Return function(id){if(_list.indexof (id) >= 0){return false; }else{_list.push(Id); Return true; } } } var load = firstLoad(); load(10); //true, first load. Load (10); //false because it is already loadedCopy the code

The main thing is that only functions returned by firstLoad can operate on __list. It is impossible to operate on __list outside of firstLoad. Because __list is not directly exposed.

By understanding the results of these two pieces of code, you probably understand what closures are.

var name = "The Window"; var object = { name:"my object", getNameFunc:function(){ return function(){ return this.name; }; }}; console.log(object.getNameFunc()()); //var name = "The Window"; var name = "The Window"; var object = { name:"my object", getNameFunc:function(){ var that = this; return function(){ return that.name; }; }}; object.getNameFunc()(); // "my object"Copy the code

My own interpretation, because first we need to see that both functions actually generate closures, but in return function() return this.name, we need to notice that this is an anonymous function, and from a scope perspective, He looks for his this.name in the first layer, and finds that the execution environment of the anonymous function is global and points to the window because it is a normal function.

In the second code, we give the closure access to this object by saving this in the getNameFunc scope in a variable that the closure can access. (Argument is the same.)

(that is, execute as a function object as described above)

var obj={name:'hhhh', func:function(){ return this.name; }} obj.func()//"hhhh"Copy the code

GetNameFunc ()();

var object = { name:"my object", getNameFunc:function(){ return function(){ return this.name; }; }}; console.log(object.getNameFunc()); ƒ (){return this.name; }Copy the code

So a parenthesis is the return value of that function and then a parenthesis is the return value of that function

Why do closures cause memory leaks

Let’s talk about counting garbage collection. If zero references point to it, then the object is ready to be garbage collected. One serious problem with counting garbage collection, however, is that objects referenced in a loop cannot be recycled, which was implemented by the javascript engine prior to IE9 using the tag clearance strategy. Circular references (references to each other of two objects) are useless and can be released after a function call, but the counting algorithm considers that since each object is referenced at least once, neither object can be garbage collected. For example:

    function problem(){
        var ObjectA = new Object();
        var ObjectB = new Object();

        ObjectA.someOtherObject = ObjectB;
        ObjectB.someOtherObject = ObjectA;
        }
Copy the code

Before IE9, BOM and DOM objects were realized in the form of COM objects using C++, and the garbage collection mechanism of COM objects adopted the counting strategy. Therefore, as long as BOM and DOM objects were involved in IE, there would be the problem of circular reference. Something like this:

      var element = document.getElementById("some_element");
      var myObject = new Object();
      myObject.element = element;
      element.someObject = myObject;
Copy the code

Closures can actually easily create hidden circular references to JavaScript objects and DOM objects. That is, as long as the closure holds an HTML element in its scope chain, it means that the element cannot be destroyed.

      function assignHandler(){
        var element = document.getElementById("someElement");
        element.onclick = function(){
            alert (element.id);
       }; 
    }
Copy the code

Note that the concept of a closure is that one function returns a value from another function that calls other values inside its parent function. This is a closure for the event handling of an Element. This closure creates a circular reference.

  1. The JavaScript object Element references a DOM object (whose ID is “someElement”); JS(element) —-> DOM(someElemet)
  2. The DOM object’s onclick attribute refers to an anonymous function closure that can refer to the entire live object, including Element, of the external assignHandler function. DOM(someelement.onclick) —->JS(Element) The anonymous function always holds a reference to the assginHandler() active object, and its memory is never reclaimed.

So it can be improved slightly:

        function assignHandler(){
        var element = document.getElementById("someElement");
        var id = element.id;
        element.onclick = function(){
            alert (id);
       }; 
       element = null;
    }
Copy the code

Circular references can be eliminated first by referring to a copy variable, but the closure contains all the live objects of the external function, so anonymous functions always contain references to Element even without direct references. Finally, you need to manually set the Element variable to NULL.

One great thing about closures is that they can read variables inside functions, and another is that they keep their values in memory at all times.

Whenever a variable is used by any closure, it is appended to the lexical environment and shared by all closures in that scope.

On function promotion
    alert(a)    1
    a();   2
    var a=3;
    function a(){
        alert(10)
    } 
    alert(a)    3
    a=6;
    a();   4
Copy the code

Write the pop-up value and explain why? Because of the promotion, the whole thing is going to look like this

Var a; function a(){ alert(10) } alert(a) 1 a(); 2 a=3; alert(a); 3 a=6; a(); 4Copy the code

F a(){alert(10)} // The variable is promoted, and the function takes precedence over the variable, which will be overwritten if the same name is used. Little knowledge:

Log.console.log (c)//ƒ c(){console.log('hahha') function c(){console.log('hahha')} var c = 1; Log.console. log(c) //ƒ c(){console.log('hahha') var c = 1; function c(){ console.log('hahha') }Copy the code
  1. A (){alert(10)}
  2. 3 // Assigns a value of 3
  3. A is not a function; a is not a function
Some special cases of function variable promotion
function yideng() { console.log(1); } (function () { if (false) { function yideng() { console.log(2); } } yideng(); }) (); // function yideng() {} is not a function in early browsers, the result should be 2, (because the variable is raised, the whole function yideng() {} is promoted to the top of the function scope), but if it is 2, Our if statement makes no sense at all. Error: yideng() is not a function when the function 'yideng' is raised to the top of the function scope. Instead of having the whole body of the function come up front like we did in the first problem.Copy the code

It’s said that in addition to if, the same goes for while, switch, and for. Supplementary knowledge:

    if(false){
        var a = 1;
    }
    console.log(a)//undefined
Copy the code

##### write the following output values for this, explain why?

this.a = 20; var test = { a: 40, init:()=> { console.log(this.a); function go() { console.log(this.a); } go.prototype.a = 50; return go; }}; new(test.init())(); / / 20 to 50Copy the code

The arrow function’s this is bound to the lexical scope of its parent, so its this.a is fixed to 20. Go itself does not have the property A, so the new object does not have this property, it will look up the prototype chain. So it’s 50.

this.a = 20; var test = { a: 40, init:()=> { console.log(this.a); function go() { this.a = 60; console.log(this.a); } go.prototype.a = 50; return go; }}; var p = test.init(); //20 p(); // 60 new(test.init())(); / / 60, 60Copy the code

Var p = test.init(); Test.init () is executed once. A = 20 p() = go(), ** note: Var p = test.init (), so this refers to Windows. This is the outermost this.a = 20. This. a=60. new(test.init())() : Test.init () takes this from the already bound parent lexical scope, but this.a has been changed to 60, so it prints 60. Go ahead and print 60.

So let’s think about what would be printed if this were the case?

Execute 20 because this is var not this.a

Another tidbit:

    function test(){
        console.log(this)
    }
    var obj = {
        f:test
    }
    (obj.f)()  //Cannot read property 'f' of undefined
Copy the code

????? What the hell? It turned out that there was no addition; Var obj = {f:test}(obj.f)() That must be an error!

With the semicolon, let’s see what the code looks like when it runs.

function test(){ console.log(this) } var obj = { f:test }; (obj) f () / / {f: ƒ}Copy the code

Obviously, the object is obj, because obj calls test(). So if we change it like this:

    function test(){
        console.log(this)
    }
    var obj = {
        f:test
    };
    (false || obj.f)()
Copy the code

Guess what? Yes Window ????? What the hell is this?? The result of short circuit statement is obj. F, and the result of operation is obj. Var xx = obj.f, then xx(), then this must refer to the global variable window**

About closure block-level scope

Please write the output value of the click below and print the correct numbers in Li in three ways.

<ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> </ul> <script type="text/javascript"> var list_li  = document.getElementsByTagName("li"); for (var i = 0; i < list_li.length; i++) { list_li[i].onclick = function() { console.log(i); } } </script>Copy the code

It is a very classic problem really !!!! You can close your eyes and say that no matter which li you click on, it will only output 6. There are three solutions to this problem:

  • The ES6 let

    for (let i = 0; i < list_li.length; i++) { list_li[i].onclick = function() { console.log(i+1); }}Copy the code

For more information about let, see temporary dead zones.

  • Closure, execute the function immediately

    for(var i = 0; i < list_li.length; i++){
      (function(i){
          list_li[i].onclick = function(){
              console.log(i+1)
          }
      })(i)
    }
    Copy the code

The closure holds this variable. How to understand the closure to save the variable, that is to say, every time this function is executed immediately, will pass an I (argument) in the scope of the function level, the level in the function scope inside onclick callback function using the I, also is the closure, we often say to note is that I pass by value here, So each of these I’s is independent and doesn’t matter.

  • The last way is when Jia Ge said it, suddenly realized, this is the most in solution ah damn. This is not a closure or scope.

    for (var i = 0; i < list_li.length; i++) { list_li[i].onclick = function() { console.log(this.innerHTML); }}Copy the code

A memory leak

www.ruanyifeng.com/blog/2017/0…