Keywords: IFFE, scope, closure, asynchrony

If any of these examples have bothered you for a long time, follow this article to find out. If you can answer all the questions correctly, then congratulations, you can skip this article!

For loop + setTimeout

🌰 example 1

for (var i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i);
  }, 1000);
}
Copy the code
  • Output result: after 1s, output 5, 5, 5, 5
  • SetTimeout is asynchronous and executes after the end of the loop. The output is the final value of I at the end of the loop.

Each time the loop executes, the callbacks are put into the delay queue, for a total of five callbacks. Although the five functions are defined separately, they share a global scope, with really only one I, and all functions share a reference to I.

At the end of the for loop, the callback starts, and the value of I is 5, so five 5’s are printed out.

But!!! This is not what we want, in fact we are trying to assume that each iteration in the loop “captures” itself a copy of I at run time.

So the question is, we need more closure scopes, especially in the loop where each iteration needs a closure scope. IIFE comes on.

IIFE creates a scope by declaring and immediately executing a function.

🌰 case 2

for (var i = 0; i < 5; i++) {
  (function () {
    setTimeout(function timer() {
      console.log(i);
    }, 1000); }) (); }Copy the code
  • Output result: after 1s, output 5, 5, 5, 5
  • Analysis: An IIFE is created in each loop, creating its own scope. But the scope does not have its own variable, so simply enclosing it is not enough.

🌰 example 3

for (var i = 0; i < 5; i++) {
  (function () {
    // The scope has its own variable that stores the value of I in each iteration
    var j = i;
    setTimeout(function timer() {
      console.log(j);
    }, 1000); }) (); }// or I could write it this way on this side, passing in the I
for (var i = 0; i < 5; i++) {
  (function (j) {
    setTimeout(function timer() {
      console.log(j);
    }, 1000);
  })(i);
}
Copy the code
  • Output result: after 1s, output 0, 1, 2, 3, 4, finally!

Summary: Inside a loop, using IIFE generates a new scope for each loop, and the I of each scope is different. It allows the callback of the delay function to enclose the new scope within each iteration, and each iteration contains a variable with the correct value for us to access.

🌰 example 4

As in example 3, we use IIFE to create a new scope with each iteration. In other words, we need a block scope for each iteration. The let declaration can be used to hijack the block scope and declare a variable in the block scope.

for (var i = 0; i < 5; i++) {
  let j = i; // Block scope for closures!
  setTimeout(function () {
    console.log(j);
  }, 1000);
}

// Recite ⬇️ ⬇️
for (let i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i);
  }, 1000);
}

// If you want to output every 1s, you can write it like this
for (let i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i);
  }, i * 1000);
}
Copy the code
  • The output is 0, 1, 2, 3, 4 👌

Also, variables are declared not just once during the loop, but every iteration. Each subsequent iteration initializes this variable with the value at the end of the previous iteration. Block scopes and closures together are unbeatable

Here is a thought question 👇

🌰 case 5

var test = function () {
  var arr = [];
  for (var i = 0; i < 5; i++) {
    arr.push(function () {
      return i * i;
    });
  }
  return arr;
};
var test1 = test(); // [function,function...]
console.log(test1[4] ());/ / 25
Copy the code

The test function stores five functions in arR, where I is the global scoped variable I, and returns an array of five functions. When executing any function in the array, the value of the global scoped variable I is 5 at the end of the loop, so 25 is returned.

Execute the IIFE – Immediately Invoked Function Expression

Let’s start with normal functions, which can hide internal variables from external scopes.

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

But what if we just wanted to isolate some variables, didn’t want to declare functions (extra declared functions would pollute the global scope), didn’t need function names, and wanted to do it automatically?

// Function expressions instead of function declarations
(function foo1() {
  var a = 3;
  console.log("inner", a); / / 3}) ();Copy the code
  1. About scope differences: Foo is bound to its scope and foo1 is bound to its scopeFunction expression itself in the function, external access is not! It does not contaminate the external scope
  2. Function expressions: can be anonymous, but function declarations are not (illegal in JS)
  3. By passing parameters, you can improve your code style
var a = 2;

(function IIFE(global) {
  var a = 3;
  console.log(a); / / 3
  console.log(global.a); / / 2}) (window);
Copy the code
  1. You can invert the running order of your code. Put the function that needs to be run in the second place and pass it in as an argument after IIFE execution. This pattern is widely used in UMD(Universal Module Definition) projects
var a = 2;
(function IIFE(def) {
  def(window); }) (function def(global) {
  var a = 3;
  console.log(a); / / 3
  console.log(global.a); / / 2
});
Copy the code
  1. Is IIFE a closure?

It depends.. IIFE is not executed outside of its lexical scope, and the values of internal variables are found by ordinary scoping lookups rather than closures. While IIFE itself is not a good example of looking at closures, it does create closures and is the most commonly used tool for creating closures that can be enclosed. So IIFE does have a lot to do with closures, even if you don’t actually use closures themselves. You don’t know JS- Part 1

Finish!!!!! ✿ Remember, (° °) Blue ✿