Home  >  Article  >  Web Front-end  >  A brief introduction to JavaScript closures

A brief introduction to JavaScript closures

WBOY
WBOYforward
2023-01-21 06:30:021106browse

This article brings you relevant knowledge about JavaScript, which mainly introduces issues related to JavaScript closures. There are many versions of the concept of closures, and different places have different opinions on closures. The following is Let's take a look, hope it helps everyone.

A brief introduction to JavaScript closures

What is a closure?

The concept of closure has many versions, and different places have different opinions about closure

Wikipedia: In computer science, closure (English: Closure), Also known as Lexical Closure or function closures, it is a technique for implementing lexical binding in programming languages ​​that support first-class functions.

MDN: closure (closure) is a function and its bundled surrounding environment state (lexical environment, lexical environment ) is a combination of references.

Personal understanding:

  • Closure is a function (returns a function)
  • The returned function saves references to external variables

A simple example

function fn() {    let num = 1;    return function (n) {        return n + num
    }
}let rFn = fn()let newN = rFn(3) // 4

The num variable scope is in the fn function, but the rFn function can access the num variable. This means that the closure function can access external function variables.

Looking at closures from browser debugging and VSCode Nodejs debugging

  • Browser

A brief introduction to JavaScript closures

  • VS Code cooperates with Node.js

A brief introduction to JavaScript closures

to see that fn in Closure is a closure function, which saves the num variable.

A classic closure: single-threaded event mechanism loop problem and solution

for (var i = 1; i  {    console.log(i);
  }, i * 1000);
}

The output results are all 6, why?

  • for loop is a synchronous task
  • setTimeout asynchronous task

If you loop once, the setTimeout asynchronous task will be added to the browser's asynchronous task queue After the synchronous task is completed, a new task is taken from the asynchronous task and executed in the thread. Since setTimeout can access the external variable i, when the synchronization task is completed, i has become 6, and the variable i that can be accessed in setTimeout is all 6.

Solution 1: Use let statement

for (var i = 1; i  {    console.log(i);
  }, i * 1000);
}

Solution 2: Self-executing function closure

for (var i = 1; i  {    console.log(i);
  }, i * 1000)
  })(i)
}

Solution 3: SetTimeout passes the third parameter

The meaning of the third parameter: additional parameters, once the timer expires, they will be passed as parameters to the function to be executed

for (var i = 1; i  {    console.log(j);
  }, 1000 * i, i);
}

Closure and function curry

function add(num) {  return function (y) {    return num + y;
  };
};let incOneFn = add(1); let n = incOneFn(1);  // 2let decOneFn = add(-1); let m = decOneFn(1); // 0

The parameters of the add function save the closure function variables.

Actual effect

Closures play a very important role in functional programming. Lodash and other early tool functions that make up for the shortcomings of JavaScript have a large number of usage scenarios for closures.

Usage scenarios

  • Create private variables
  • Extend variable life cycle

Throttling Function

To prevent scrolling behavior and excessive execution of functions, throttling is required. The throttling function accepts function time as parameters, which are all variables in the closure. , the following is a simple setTimeout version:

function throttle(fn, time=300){    var t = null;    return function(){        if(t) return;
        t = setTimeout(() => {
            fn.call(this);
            t = null;
        }, time);
    }
}

Anti-shake function

A simple implementation of a setTimeout anti-shake function

function debounce(fn,wait){    var timer = null;    return function(){        if(timer !== null){            clearTimeout(timer);
        }
        timer = setTimeout(fn,wait);
    }
}

React. useCallback closure trap problem

Problem Description:Parent/Child Component relationship, parent and child components can use the click event to modify state data at the same time, and the child component gets the passed props event attribute, It is optimized through useCallback. That is, this optimized function has a closure trap, (the initial state value is always saved)

import { useState, useCallback, memo } from "react";const ChildWithMemo = memo((props: any) => {  return (    <div>
      <button>Child click</button>
    </div>
  );
});const Parent = () => {  const [count, setCount] = useState(1);  const handleClickWithUseCallback = useCallback(() => {    console.log(count);
  }, []); // 注意这里是不能监听 count, 因为每次变化都会重新绑定,造成造成子组件重新渲染

  return (    <div>
      <div>parent count : {count}</div>
      <button> setCount(count + 1)}>click</button>
      <childwithmemo></childwithmemo>
    </div>
  );
};export default Parent
  • ChildWithMemo uses memo for optimization,
  • handleClickWithUseCallback uses useCallback for optimization

The problem is that when the subcomponent is clicked, the output count is the initial value (closed).

The solution is to use useRef to save the operation variable function:

import { useState, useCallback, memo, useRef } from "react";const ChildWithMemo = memo((props: any) => {  console.log("rendered children")  return (    <div>
      <button> props.countRef.current()}>Child click</button>
    </div>
  );
});const Parent = () => {  const [count, setCount] = useState(1);  const countRef = useRef<any>(null)

  countRef.current = () => {    console.log(count);
  }  return (    <div>
      <div>parent count : {count}</div>
      <button> setCount(count + 1)}>click</button>
      <childwithmemo></childwithmemo>
    </div>
  );
};export default Parent</any>

In response to this problem, React once approved the community's proposal to add useEvent, but later the useEvent semantic issue was abandoned. For rendering optimization, React adopts a compilation optimization solution. In fact, similar problems will also occur in useEffect. Pay attention to closure traps when using it.

Performance issues

  • Don’t define closures at will. Once defined, you must find a suitable location for destruction. Because the variables of the closure are stored in memory and will not be destroyed, they occupy a high amount of memory.

Use the chrome panel function timeline profiles panel

  1. Open the developer tools and select the Timeline panel
  2. Capture# at the top ## Check Memory
  3. in the field and click the record button in the upper left corner.
  4. Perform various operations on the page to simulate user usage.
  5. After a period of time, click the stop button in the dialog box, and the memory usage during this period will be displayed on the panel.
[Related recommendations:

JavaScript video tutorial, web front-end

The above is the detailed content of A brief introduction to JavaScript closures. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.im. If there is any infringement, please contact admin@php.cn delete