Offer to come, dig friends take it! I am participating in the 2022 Spring Recruit Punch card activity. Click here for details.

⭐ ⭐ ⭐

What are the application scenarios for closures?

A:

Implementing modularity is one application scenario for closures.

The most common packaging tool we use, Webpack, is modularity with closures, where the variables of each module are packaged without contaminating each other.

For example, we now have an index.js with two modules, A and B, as follows:

// a.js
module.exports = function funcA() {
  var time = 'funcA! ' + Date.now()
  console.log(time)
}
Copy the code
// b.js
module.exports = function funcB() {
  var time = 'funcB! ' + Date.now()
  console.log(time)
}
Copy the code
// add modules A and b to index.js
const funcA = require('./a.js')
const funcB = require('./b.js')

var time = Date.now()
console.log('index.js' + time)

funcA()
funcB()
Copy the code

In modules A, B, and index.js, define a test variable named time. See how the time variable is isolated by each module after packaging.

Note that the time variable date.now () is used for demonstration purposes, because if you define a normal type of data, the variable is gone after Webpack and becomes a value.

With WebPack packaging, the file directory structure looks something like this:

The packaged code is generated in main.js in the dist directory and looks like this:

// main.js; (() = > {
  var o = {
    85: o= > {
      o.exports = function () {
        var o = 'funcA! ' + Date.now()
        console.log(o)
      }
    },
    326: o= > {
      o.exports = function () {
        var o = 'funcB! ' + Date.now()
        console.log(o)
      }
    }
  },
  n = {}
  function r (e) {
    var t = n[e]
    if (void 0! == t)return t.exports
    var s = (n[e] = { exports: {}})returno[e](s, s.exports, r), s.exports } ; (() = > {
    const o = r(85),
    n = r(326)
    var e = Date.now()
    console.log('index.js' + e), o(), n()
  })()
})()
Copy the code

First of all, the whole code is an immediate function, the code will be executed immediately, the code defined variables O, n and function r, are not globally accessible, do not worry about global contamination.

If you fold this code up, there it is. The folded logic executes immediately:

Then, the logic in the original modules A and B becomes methods 85 and 326, defined on object O.

var o = { // o is a collection of modules
  85: o= > { // module A, internal implementation of a.js logic,
    o.exports = function () {
      var o = 'funcA! ' + Date.now() // Change the variable name from time to o, shorten the code size
      console.log(o)
    }
  },
  326: o= > { // b module, internal implementation of THE logic in B. js
    o.exports = function () {
      var o = 'funcB! ' + Date.now()
      console.log(o)
    }
  }
},
Copy the code

Note that 85 and 326 here, the result of code ugly confusion, serve two purposes:

  • Prevents code logic from being visible
  • Reduce code size

You can think of them as two code names, 85 for the logic of module A and 326 for the logic of module B.

The code block defines an object N, a function r, and immediately executes a piece of logic:

n = {} // Emulate exports objects
function r (e) { // simulate the require method
  var t = n[e]
  if (void 0! == t)return t.exports
  var s = (n[e] = { exports: {}})returno[e](s, s.exports, r), s.exports } ; (() = > { 
  const o = r(85), 
    n = r(326) 
  var e = Date.now() 
  console.log('index.js' + e), o(), n()
})()
Copy the code

Export (n, exports) {export (b, exports) {export (b, exports) {export (b, exports) {export (b, exports) {export (b, exports) {export (b, exports)}}

Function r emulates the require method when introduced.

const funcA = require('./a.js'(funcA(const o = r(85) // 85 is the contents of module A, and the contents are placed in the previous collection O
o()
Copy the code

The last immediate-execute function executes the code logic in index.js immediately:

; (() = > { // Execute the function immediately to execute the code logic in index.js
  const o = r(85), // Method 85, which is the logic in module A
    n = r(326) // Method 326, which is the logic in module B
  var e = Date.now() // the logic in index.js is also confused with the variable time.
  console.log('index.js' + e), o(), n()
})()
Copy the code

Comparing the original code, the obfuscated code is still very intuitive.

const funcA = require('./a.js')
const funcB = require('./b.js')

var time = Date.now()
console.log('index.js' + time)

funcA()
funcB()
Copy the code

Executing the obfuscated function O executes the logic in module A, and executing the obfuscated function N executes the logic in module B.

Print out functions O and n for a look. In fact, the code logic in module A and module B is extracted, packaged into functions, and then put into module set O, corresponding to the confused codes 85 and 326.

Function O and function n are called externally. Because of the closure, the original variable time, that is, the confused variable O in function O and function N, is the “private variable” of the two functions and does not affect each other.

As a result, we have achieved the isolation of variables between modules.

Finally, take a break to look at the variables in the closure. The generated variables O, n, and r are all anonymous private variables in the immediate function closure, with no global impact.

Extension: Understand closures from the way require is written

When a module is introduced, it is usually received with a variable and then executed.

const moduleXXX = require('xxx.js')
moduleXXX()
Copy the code

That’s easy, because the require function returns a function, of course.

A relatively simple function nested function implementation closure is written like this:

function fn () { 
  var i = 100
  return function () {
    console.log(i)
  }
}

const tempFn = fn() // Use a temporary function to receive and execute the temporary function

tempFn() / / 100
Copy the code

They are written exactly the same as require:

const moduleXXX = require('xxx.js')
moduleXXX()

// This is the same as that of a variable function

const tempFn = fn() 
tempFn()
Copy the code

Combined with this article’s modularity example, the packaged function R emulates the require method, which returns a function.

var o = {
    85: () = >{... }// The logic of module A
    326: () = >{... }// The logic of module B
}

function r(e) {
  return o(e) // Return either o(85) or o(326) are functions
}

;(() = > {
  const o = r(85)  // 85 is the contents of module A. the r method returns a function, which is received by o
  o()
  const n = r(326) // 326 is the contents of module B
  n()
})()
Copy the code

To summarize: the implementation of the require function also uses closures.

Expansion: modules become more and more complex reference relations

No matter how many modules there are and how complex the reference relationships are, the packaged code is still similar to the previous example.

For example, if we add another module C, module A will reference module C, and write module B more complex, exporting multiple methods:

// modulea.js imports module C
const funcC = require('./moduleC.js')

module.exports = function funcA() {
    var time = 'funcA! ' + Date.now()
    console.log(time)
    funcC()
}
Copy the code
// moduleb.js export a few more methods
function funcB1() {
  var time = 'funcB1! ' + Date.now()
  console.log(time)
}

function funcB2() {
  var time = 'funcB2! ' + Date.now()
  console.log(time)
}

function funcB3() {
  var time = 'funcB3! ' + Date.now()
  console.log(time)
}

module.exports = {
  funcB1,
  funcB2,
  funcB3
}
Copy the code
// modulec.js moduleC remains unchanged
module.exports = function funcC() {
  const timec = Date.now()
  console.log('funcC! ' + timec)
}
Copy the code
// index.js
const funcA = require('./moduleA.js')
const { funcB1, funcB2, funcB3 } = require('./moduleB.js')

var time = Date.now()
console.log('index.js' + time)

funcA()
funcB1()
funcB2()
funcB3()
Copy the code

The resulting package looks like this:

// main.js; (() = > {
  var n = {
      664: (n, o, c) = > { // in module A, function C (require function) can be passed in as an argument
        const t = c(761) // Execute the logic of module C in module A
        n.exports = function () {
          var n = 'funcA! ' + Date.now()
          console.log(n), t()
        }
      },
      300: n= > {
        n.exports = { // multiple methods in module B, all mounted to N.xports
          funcB1: function () {
            var n = 'funcB1! ' + Date.now()
            console.log(n)
          },
          funcB2: function () {
            var n = 'funcB2! ' + Date.now()
            console.log(n)
          },
          funcB3: function () {
            var n = 'funcB3! ' + Date.now()
            console.log(n)
          }
        }
      },
      761: n= > {
        n.exports = function () {
          const n = Date.now()
          console.log('funcC! ' + n)
        }
      }
    },
    o = {}
  function c (t) {
    var e = o[t]
    if (void 0! == e)return e.exports
    var r = (o[t] = { exports: {}})returnn[t](r, r.exports, c), r.exports } ; (() = > {
    const n = c(664),
      { funcB1: o, funcB2: t, funcB3: e } = c(300)
    var r = Date.now()
    console.log('index.js' + r), n(), o(), t(), e()
  })()
})()
Copy the code

Understand the first example of this article, this example is not difficult to analyze, no matter how many modules, no matter how complex the reference relationship, the core idea is to use closures to simulate require function.

At the end

After looking at this example, you will find that closures are not an octagon. Closures are ubiquitous in all kinds of JS applications. Understanding closures is the first step towards JS advancement.

If my article is helpful to you, your 👍 is my biggest support ^_^

You can also follow the front End Daily Question column in case you lose contact

I’m Allyn, export insight technology, goodbye!

The last:

“Front-end Daily Ask (34)” closures and loop traps