What is a closure?
- A closure refers to the ability of a function to access and manipulate variables outside its lexical scope.
- A closure is a function that can read variables inside other functions.
- For example, in JavaScript, only subfunctions inside functions can read local variables, so closures can be understood as "functions defined inside a function".
- In essence, closures are bridges connecting the inside and outside of the function.
Features: Functions are nested and return subfunctions, which access external variables.
//External functionsfunction outerFunction() { //Variables outside internal function var outerVariable = 'I am from outer function'; //Return internal function function innerFunction() { (outerVariable); } return innerFunction; } // The outerFunction is destroyed after execution, but the outerVariable is referenced so it is still alivevar closure = outerFunction(); closure(); // Output:I am from outer function
What is the role of closure?
- Encapsulated private variables: Closures can be used to create private variables and methods. By defining variables inside the function and returning an internal function, these variables cannot be directly accessed from the outside, the encapsulation effect is achieved. This can avoid contamination of global variables and improve the maintainability and security of the code.
- Delayed execution: Closures can be used to achieve the effect of delayed execution. Execution can be triggered when needed by defining a timer or event listener inside the function and returning an internal function.
- Memorization/keeping state: Closures can be used to achieve the effect of memory, that is, cache the calculation results of the function so that the cached results are directly returned during subsequent calls, improving the execution efficiency of the function.
- Callback function: Closures can be used to implement callback functions. The callback mechanism for asynchronous operations can be implemented by passing a function as a parameter to another function and calling the function in the internal function.
- Modular development: Closures can be used to implement modular development. By encapsulating a set of related variables and methods in a closure, the pollution of the global namespace can be avoided and the independence and reusability of the module can be achieved.
Closure defect?
- Memory usage: Closures will cause variables of external functions to be garbage collected, thereby increasing memory usage. If the closure exists for a long time, external variables will not be released, which may lead to memory leaks.
- Performance loss: Closures involve the search process of scope chains, which will bring certain performance losses. In scenarios with high performance requirements, you need to pay attention to the use of closures.
Application scenarios of closures?
Reference link:Summary of 6 application scenarios of js closure_javascript skills_me ()
1. Self-executing function (can implement singleton mode)
let say = (function(){ let val = 'hello world'; function say(){ (val); } return say; })()
var Singleton = (function () { var instance; function createInstance() { var object = new Object("I am the instance"); return object; } return { getInstance: function () { if (!instance) { instance = createInstance(); } return instance; }, }; })();
2. Anti-shake throttling
// Throttle function encapsulationfunction throttle(func, delay) { let timer = null; return function () { if (!timer) { timer = setTimeout(() => { (this, arguments); timer = null; }, delay); } }; } // Anti-shake function packagingfunction debounce(func, delay) { let timer = null; return function () { clearTimeout(timer); timer = setTimeout(() => { (this, arguments); }, delay); }; }
3. Function Currying
//Before Curryfunction add(a, b, c) { return a + b + c; } (add(1, 2, 3)); //6 //After Curryingfunction addCurried1(a) { return function (b) { return function (c) { return a + b + c; }; }; } //Abbreviation of Arrow Functionconst addCurried2 = (a) => (b) => (c) => a + b + c; (addCurried1(1)(2)(3)); //6 (addCurried2(1)(2)(3)); //6
4. Publish Subscription
function createPubSub() { // Store events and their corresponding subscribers const subscribers = {}; // Subscribe to events function subscribe(event, callback) { // If the event does not exist, create a new empty array if (!subscribers[event]) { subscribers[event] = []; } // Add callback function to subscriber array subscribers[event].push(callback); } // Publish an event function publish(event, data) { // If the event does not exist, return directly if (!subscribers[event]) { return; } // Iterate through the subscriber array and call each subscriber's callback function subscribers[event].forEach((callback) => { callback(data); }); } // Return to the subscription and publish functions return { subscribe, publish, }; } // Use exampleconst pubSub = createPubSub(); // Subscribe to events("event1", (data) => { ("Subscriber 1 receives data for event 1:", data); }); ("event2", (data) => { ("Subscriber 2 receives data for event 2:", data); }); // Publish an event("event1", "Hello"); // Output: Subscriber 1 receives data for event 1: Hello("event2", "World"); // Output: Subscribers2Received an event2Data of: World
5. Iterator
function createIterator(arr) { let index = 0; return { next: function() { if (index < ) { return { value: arr[index++], done: false }; } else { return { done: true }; } } }; } const myIterator = createIterator([1, 2, 3]); (()); // { value: 1, done: false } (()); // { value: 2, done: false } (()); // { value: 3, done: false } (()); // { done: true }
How to release closures?
Releasing a closure is usually achieved by unreference to the closure. When no longer need to use closures, the variable where the closure is located can be set tonull
Or assign the variable where the closure is located to another value, thereby breaking the reference to the closure. In this way, the JavaScript engine will determine that the closure is no longer referenced during the next garbage collection, thereby freeing up the memory space occupied by the closure.
Related concepts
The lexical scope of a function (also called static scope/closed scope)
It refers to the scope determined when the function is defined, not the scope determined when the function is called. It is determined by the context in which the function is defined and independent of the call location of the function.
Rules: Variables outside the function can be accessed inside the function. Variables inside the function cannot be accessed outside the function. Functions inside the function can be accessed outside the function. Functions inside the function can access external function variables.
The advantage of lexical scope is that it provides a more reliable and predictable way to access variables. When defining a function, the variables that can be accessed within the function are determined and will not be affected by the function call location. This static scoped feature makes the code easier to understand and maintain, and can implement some advanced programming skills such as closures and modular development.
Execution context
Whenever JavaScript code is executed, an execution context is created (a data structure inside the JavaScript engine, used to manage the execution environment and scope of variables) and is managed and destroyed according to specific rules.
The execution context can be divided into three types:
- Global Execution Context: The global execution context is created when the entire script file is executed, and it is the outermost execution context. In the global execution context, variables and function declarations are promoted and global objects (such as window objects in the browser environment) and global variables are created.
- Function Execution Context: Whenever a function is called, a function execution context is created. The function execution context contains the function parameters, local variables, variables inside the function and function declarations. Each function execution context has its own scope chain and this value.
- Eval Execution Context: When executing code using the eval() function, an eval execution context is created. It is similar to the global execution context, but it has its own lexical scope.
The life cycle of the execution context includes the following stages:
- Creation Phase: At this stage, the JavaScript engine creates an execution context and declares variables and functions. Variables will be initialized to undefined and the function will be stored in memory.
- Execution Phase: At this stage, the JavaScript engine will execute statements in the order of code, assign values to variables, execute function calls and other operations.
Observe and understand the execution context
The management and switching of execution contexts are automatically completed by the JavaScript engine. Developers can better understand the code execution process and the scope of functions by understanding the concepts and rules of the execution context.
Although the execution context cannot be accessed or output directly, we can observe and understand the behavior and characteristics of the execution context in some indirect ways:
- Use (): You can use the () method in the code to output information such as the value of variables, the execution result of the function, etc., so as to indirectly observe the impact of the execution context.
- Using debugging tools: Modern browsers and development tools provide powerful debugging functions, allowing you to view changes in execution context, values of variables, and call stack during code execution.
- Using closures: By creating closures, we can indirectly observe the role of the execution context. A closure allows internal functions to access variables of external functions, thus forming a closure execution context that contains the external execution context.
The above is the detailed content of the role and application scenarios of closures in JavaScript. For more information about JavaScript closures, please pay attention to my other related articles!