MDN – Closure MDN – microtasks and javascript runtime environment

A combination of a function bound to (or surrounded by) references to its surrounding state (lexical environment) is a closure. That is, closures allow you to access the scope of an outer function within an inner function. In JavaScript, whenever a function is created, the closure is created at the same time the function is created.

preface

I am ashamed to say that I have been working for more than two years and still have little understanding of closures and execution context. I have always thought that function nesting is a closure. After watching Teacher Winter’s class, I realized my cognitive error, but I still had some confusion and uncertainty about the course, so I consulted the MDN document to get some insights.

closure

1. What are closures

From the MDN specification, a function bound to a lexical environment is simply a closure. Closures are a combination of functions and the lexical environment in which they are declared. When a piece of javascript code is run, it is in the execution context, and the lexical context is part of the execution context, meaning that the function is the closure. (I was wrong to classify the execution context and closure as an association, but that’s fine.)

The word lexical scope, lexical, refers to the lexical scope that determines where a variable is available based on where it is declared in the source code. Nested functions can access variables declared outside their scope.

2. Pros and cons of closures

The pros and cons here are really for function nesting

  • Advantages: Emulates private methods
  • Disadvantages: Affects performance

When creating an object or class, methods should hang on the prototype, not be defined in the object’s internal functions (constructors). Because the method is reassigned every time the inner function is called, that is, for every object created, the method is reassigned. A new execution context is created and destroyed every time the same function is executed, so the method is reassigned.

The MDN example is nice and straightforward

Function MyObject(name, message) {this.name = name.tostring (); this.message = message.toString(); this.getName = function() { return this.name; }; this.getMessage = function() { return this.message; }; } function MyObject(name, message) {this.name = name.tostring ();} function MyObject(name, message) {this.name = name.tostring (); this.message = message.toString(); } MyObject.prototype.getName = function() { return this.name; }; MyObject.prototype.getMessage = function() { return this.message; };Copy the code

With closures, this function is redefined each time a.goetName () is called; With stereotypes, however, stereotypes can be shared by all objects without having to define methods every time an object is created.

Execution context

When a piece of JavaScript code is running, it is actually running in an execution context.

Each execution context is a level of scope. Each code snippet is created to run in a new context at the start of execution and destroyed on exit.

There are three types of code that create a new execution context:

  • Global context: The execution context created to run the body of the code, which is created for code other than the javascript functions (specifically, the global context used when defining functions, whereas the function body is used locally).
  • Local context: Each function creates its own context at execution time.
  • Using the eval() function also creates a new execution context.
let outputElem = document.getElementById("output"); let userLanguages = { "Mike": "en", "Teresa": "es" }; function greetUser(user) { function localGreeting(user) { let greeting; let language = userLanguages[user]; Switch (language) {case "ES ": greeting = '¡ Hola, ${user}! `; break; case "en": default: greeting = `Hello, ${user}! `; break; } return greeting; } outputElem.innerHTML += localGreeting(user) + "<br>\r"; } greetUser("Mike"); greetUser("Teresa"); greetUser("Veronica");Copy the code

This code contains three execution contexts, some of which are created and destroyed multiple times as the program runs. Each context is pushed into the execution context stack as it is created. When exiting, it is removed from the context stack.

  • When the program starts running, the global context is created.
    • When greetUser(‘Mike’) is executed, an execution context is created for the greetUser() function. The execution context is pushed into the execution context stack.
      • When greetUser() calls localGreeting(), an execution context is created for the method. And when the localGreeting() exits, its execution context pops out of the execution stack and is destroyed. The program retrieves the next execution context from the stack and resumes execution, starting with the rest of greetUser().
      • When greetUser() completes execution and exits, its context is also popped from the execution stack and destroyed.
    • When greetUser(‘Teresea’) is executed, a context is created for it and pushed to the top of the stack.
      • When greetUser() calls localGreeting(), another context is created and run, and when localGreeting() exits, its context is popped off the stack and destroyed. GreetUser () gets restored and continues with the rest.
      • GreetUser () completes and exits, and its context pops out of the stack and is destroyed.
    • When greetUser(‘Veronica’) is executed, the program creates a context for it and pushes it to the top of the stack.
      • When executed to localGreeting(), a context is created for it, and when executed, the localGreeting() pops the context off the stack and destroys it. (The program continues by fetching the next context from the stack, i.e. GreetUser ()).)
      • GreetUser () exits after execution, and its context is popped from the stack and destroyed.
  • The main program exits, and the global execution context pops out of the stack. All contexts are popped up and the program is finished.

About recursion: Each call itself creates a new context. This allows the JS runtime to track the level of recursion and the value returned from the recursion, but each recursion consumes memory to create a new context.