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.
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:
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.
to see that fn in Closure is a closure function, which saves the num variable.
for (var i = 1; i { console.log(i); }, i * 1000); }
The output results are all 6, why?
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.
for (var i = 1; i { console.log(i); }, i * 1000); }
for (var i = 1; i { console.log(i); }, i * 1000) })(i) }
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); }
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.
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.
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); } }
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); } }
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
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.
[Related recommendations:
- Open the developer tools and select the Timeline panel
- in the field and click the record button in the upper left corner.
Capture# at the top ## Check Memory
- Perform various operations on the page to simulate user usage.
- 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.
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!