scope

Concept: Scope in JS refers to the scope within which a variable can be accessed. Scope is divided into global scope, function scope and block-level scope according to the different scopes that can be accessed. Block-level scope is a new concept in ES6.

Global scope

A variable is mounted under a window (or global) object and can be accessed anywhere. The variable is a global variable, corresponding to the global scope.

Function scope

A variable defined in a function is called a function variable, and it can only be accessed from inside the function, so its scope, that is, inside the function, is called the function scope.

Block-level scope

Variables defined using the LET keyword can only be accessed in the block-level scope, and let-defined variables have a temporary dead zone that cannot be used until the variable is defined.

The simple personal understanding is that {… } let or const defines a variable only in {… }, and the corresponding block level scope is formed.

console.log(a) //a is not defined
if (true) {
  let a = '123' / / in the if () {... } the variable a declared in} can only be called in this directory and cannot be used anywhere else
  console.log(a)  / / 123
}
console.log(a) //a is not defined

// Use b before declaration, var will promote the variable, print undefined, and let will cause a temporary dead zone.
console.log(b) // undefined
if (true) {
  var b = '456'
  // Use var to declare variable b, which can be accessed outside of if. This is the difference between the block-level scope created by let and var.
  console.log(b)  / / 456
}
console.log(b) / / 456
Copy the code

Block-level scope can be mimicked with call-now functions:

(function () {
  // block-level scope}) ();Copy the code

The scope chain

In general, variables are accessed in one order, as shown in the following code:

var a = 1
function test () {
  // If we only declare var a = 2 here, and we do not declare a in fun, then we will print 2
  function fun () {
    Var a = 3 var a = 3
    console.log(a)
  }
  return fun
}

test()() / / 1
Copy the code

When fun fails to find variable A, it looks for variable A in the scope of test. If it fails to find variable A, it looks for variable A in the global scope. In this way, the scope chain is formed.

At the same time, the inner environment can access all the outer environment through the scope chain, but the outer environment cannot access any variables and functions of the inner environment, that is, if you want to access the variables declared in fun in test, you cannot access them.

You can use with (expression) {statement} to add an object to the front of a scope chain, but with is not recommended, as it is prohibited in ECMAScript 5 strict mode. The recommended alternative is to declare a temporary variable to hold the attributes you need.

The catch block of try/catch creates a new variable object that contains the declaration of the error object to be thrown.

Note: when declaring a variable in a function, add var, or declare a global variable.

function foo () {
  // Without var, this becomes a global variable that can be accessed outside the function.
  a = 'hello'
}

foo()

console.log(a) // 'hello'
Copy the code

closure

General concept: Closures are functions that refer to variables in the scope of another function, usually implemented in nested functions.

A closure does not necessarily need to return another function within a function, but only need to reference each other’s variables.

Closures are defined in MDN as follows:

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.

Personal Understanding:

According to the general concept of closures and MDN concepts, any variable call between two scopes is a closure. Similar to the following example:

var a = 'hello'
function foo () {
  console.log(a)
}
Copy the code

The code above accesses the global scope variable A in the scope of function foo, which I see as a closure. Personally, I prefer this definition: a closure is a function and the sum of variables within the function that can access other scopes.

This also explains why there is a sentence at the end of the MDN definition: whenever a function is created, the closure is created at the same time the function is created.

Advantages of closures

  • Easy to call variables in other scopes.
  • Avoid contamination by writing variables globally.
  • Keep the called variable in memory all the time, can do cache. (Also a disadvantage, resulting in large memory consumption)

Disadvantages of closures

  • Because closures cause variables in a function to be stored in memory, memory consumption is high. You can solve this problem by removing all unused local variables before exiting the function.
// Use a little Red Book example:
function assignHandler () {
  var element = document.getElementById('someElement')
  /** Memory leak caused by circular reference, because IE bug, circular reference can not be detected automatically, so by copying the value of the inner and outer reference, so that can be recycled. IE9 and beyond have been fixed. * /
  var id = element.id  // Remove circular references
  element.onclick = function () {
    console.log(id)
  }
  element = null  // Unreference the document.getelementById ('someElement') object
}
Copy the code

Remind yourself that this is a memory drain, not a memory leak as previously thought. The leak is due to an Internet Explorer bug that prevents you from reclaiming variables referenced by closures after you’ve used them. It’s an IE bug, not the closure itself. And a memory leak is when unreachable variables take up memory space, but the variables accessed by the closure are needed during execution.

Note:

Using this in closures can cause some problems. If the inner function does not use the arrow function, the this object is bound at run time to the context in which the function is executed.

var name = "Window"
varobject = {name: "Object".getName: function () {return function(){return this.name}}}console.log(object.getName()())
Copy the code

Just two questions

Q1: What does the following code output?

function fun (a, b) {
  console.log(b)
  return {
    fun: function (c) {
      return fun(c, a)
    }
  }
}

var d = fun(0)
d.fun(1)
d.fun(2)
d.fun(3)

var d1 = fun(0).fun(1).fun(2).fun(3)

var d2 = fun(0).fun(1
d2.fun(2)
d2.fun(3)
Copy the code

Output result:

// fun has two arguments, a and b, where 0 is passed, so b is undefined
var d = fun(0)  // undefined
/** fun returns an object d: d = {fun: Function (c) {return fun(c, a)}} d.sun (1); function (c) {return fun(c, a)} d.sun (1)
d.fun(1)        / / 0
// d is always the object returned by the first execution of d = fun(0), which is equivalent to fun(2, 0)
d.fun(2)        / / 0
/ / with the
d.fun(3)        / / 0

/** the value printed will change; The first execution is fun(0, undefined), which prints undefined and returns {fun: Function (c) {return fun(c, a)}} function (c) {return fun(c, a)}} function (c) {return fun(c, a)} Fun (2, 1) is executed the third time; Print 1 executes fun(3, 2) for the fourth time; The reason for printing 2 is that the return function has a reference to the value of a in the scope of the parent function, so A will always exist. However, when fun is called in the returned object, the value of A is changed, so the printed result will change. * /
var d1 = fun(0).fun(1).fun(2).fun(3)  // undefined, 0, 1, 2

// A combination of the above two situations, without further ado
var d2 = fun(0).fun(1) / / undefined, 0
d2.fun(2) / / 1
d2.fun(3) / / 1
Copy the code

Question 2: What is the output?

for (var i = 0; i < 6; i++) {
  setTimeout(function () {
    console.log(i)
  }, i * 1000)}Copy the code

Since var I = 0 in for creates a global variable I, and setTimeout is asynchronous, the function in setTimeout is executed after the completion of the for loop, so the result is to print a 6 every one second.

Extension: how to make it print 0,1,2,3,4,5 every second?

  • Option 1: define I with let, such that for(…) {… } form block-level scopes.
for (let i = 0; i < 6; i++) {
  setTimeout(function () {
    console.log(i)
  }, i * 1000)}Copy the code

This code is equivalent to executing:

{
  // form block-level scopes
  let i = 0
  {
    let ii = i
    setTimeout(function () {
      console.log(ii)
    }, i * 1000 )
  }
  i++
  {
    let ii = i
    setTimeout(function () {
      console.log(ii)
    }, i * 1000 )
  }
  i++
  {
    let ii = i
    setTimeout(function () {
      console.log(ii)
    }, i * 1000)}... }Copy the code
  • Plan 2: Use the immediate execution function
for (var i = 0; i < 6; i++) {
  (function (i) {
    setTimeout(function () {
      console.log(i)
    }, i * 1000)
  })(i)
}
Copy the code

If I is passed in as an argument, then each function scope has a variable I. When the setTimeout function is executed, the I in the currently-executed function scope is accessed.

  • Scheme 3: Use the third parameter of setTimeout
for (var i = 0; i < 6; i++) {
  setTimeout(function (j) {
    console.log(j)
  }, i * 1000, i)
}
Copy the code