Förstå JavaScript-förslutningar i loopar: praktiska exempel

Förstå JavaScript-förslutningar i loopar: praktiska exempel
Förstå JavaScript-förslutningar i loopar: praktiska exempel

Rensa upp slingförslutningar i JavaScript

JavaScript-utvecklare stöter ofta på oväntat beteende när de använder stängningar inuti loopar. Detta problem kan leda till förvirring, särskilt för dem som är nya i begreppet nedläggningar.

I den här artikeln kommer vi att utforska praktiska exempel som illustrerar vanliga fallgropar och tillhandahåller lösningar för att använda stängningar effektivt i loopar, oavsett om det handlar om händelseavlyssnare, asynkron kod eller iterering över arrayer.

Kommando Beskrivning
let Deklarerar en blockomfattad lokal variabel, valfritt initialiserar den till ett värde. Används för att säkerställa att varje iteration av slingan har sin egen omfattning.
const Deklarerar en blockomfattad, skrivskyddad namngiven konstant. Används för att skapa en funktion eller variabel vars värde inte ska ändras.
Promise Representerar slutförandet (eller misslyckandet) av en asynkron operation och dess resulterande värde.
setTimeout Anropar en funktion eller utvärderar ett uttryck efter ett angivet antal millisekunder.
addEventListener Bifogar en händelsehanterare till ett specificerat element utan att skriva över befintliga händelsehanterare.
IIFE Omedelbart anropat funktionsuttryck. En funktion som körs så snart den är definierad. Används för att skapa lokala scopes i loopar.
for...in Itererar över ett objekts otaliga egenskaper, i en godtycklig ordning.
for...of Itererar över värdena för ett itererbart objekt (som en array eller en sträng), i en specifik ordning.

Förstå JavaScript-förslutningar i loopar

Skripten i de tidigare exemplen tar upp det vanliga problemet med stängningar inom loopar i JavaScript. När du använder en var deklaration inom en loop delar alla iterationer samma funktionsomfång. Det är därför, i det första exemplet, utdata är "Mitt värde: 3" tre gånger. Lösningen är att använda let, vilket skapar ett blockomfång som bibehåller det korrekta värdet för varje iteration. Detta tillvägagångssätt säkerställer att varje iteration har sin egen omfattning, vilket bevarar det korrekta värdet när funktionen anropas. Skriptet visar hur man ändrar deklarationen från var till let åtgärdar problemet och loggar "Mitt värde: 0", "Mitt värde: 1" och "Mitt värde: 2" som avsett.

För asynkron kod kan samma stängningsproblem uppstå. Använder sig av Promises och setTimeout fungerar med let säkerställer att varje asynkront anrop bibehåller det korrekta iterationsvärdet. Skriptet visar att genom att använda wait med let, loggar varje löst löfte det förväntade värdet. Eventlyssnare kan också möta liknande problem; dock att linda in lyssnarfunktionen i en IIFE (Omedelbart anropat funktionsuttryck) hjälper till att fånga det korrekta värdet genom att skapa ett nytt omfång för varje iteration. Användningen av for...in och for...of loops visar ytterligare vikten av scoping i stängningar, visar hur man korrekt fångar index och värde med IIFE för att skapa distinkta omfång för varje loop-iteration.

Lösning av stängningsproblem i JavaScript-loopar med let

JavaScript (ES6)

let funcs = [];
// Let's create 3 functions
for (let i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value:", i);
  };
}
for (let j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

Säkerställande av korrekta stängningsvärden i asynkron kod

JavaScript (ES6)

const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));
for (let i = 0; i < 3; i++) {
  // Log `i` as soon as each promise resolves.
  wait(i * 100).then(() => console.log(i));
}

Korrekt stängning av händelseavlyssnare som använder IIFE

JavaScript (ES6)

var buttons = document.getElementsByTagName("button");
// Let's create 3 functions
for (var i = 0; i < buttons.length; i++) {
  // as event listeners
  (function(i) {
    buttons[i].addEventListener("click", function() {
      // each should log its value.
      console.log("My value:", i);
    });
  })(i);
}

Korrekt stängning med för...in och för...slingor

JavaScript (ES6)

const arr = [1, 2, 3];
const fns = [];
for (const i in arr) {
  fns.push(((i) => () => console.log("index:", i))(i));
}
for (const v of arr) {
  fns.push(((v) => () => console.log("value:", v))(v));
}
for (const n of arr) {
  const obj = { number: n };
  fns.push(((n, obj) => () => console.log("n:", n, "|", "obj:", JSON.stringify(obj)))(n, obj));
}
for (const f of fns) {
  f();
}

Utforska användningen av stängningar i avancerade JavaScript-funktioner

Stängningar är ett grundläggande begrepp i JavaScript som tillåter en funktion att komma åt variabler från dess omslutande omfattning, även efter att det omfånget har stängts. Den här funktionen är särskilt kraftfull när du skapar avancerade funktioner som de som används i memoization, currying och funktionell programmering. Till exempel använder memoisering stängningar för att komma ihåg resultaten av dyra funktionsanrop och returnera det cachade resultatet när samma inmatningar inträffar igen. Genom att använda stängningar kan vi skapa mer effektiv och optimerad kod som förbättrar prestandan, särskilt i rekursiva funktioner som att beräkna Fibonacci-sekvenser.

En annan avancerad användning av stängningar är att skapa privata variabler och funktioner inom JavaScript-objekt, simulera privata metoder och egenskaper. Denna teknik används ofta i modulmönster och omedelbart anropade funktionsuttryck (IIFE) för att kapsla in funktionalitet och undvika att förorena det globala omfånget. Dessutom spelar stängningar en avgörande roll i händelsehantering och asynkron programmering, där de hjälper till att behålla tillstånd och sammanhang över tid. Att förstå och effektivt använda stängningar kan avsevärt höja dina JavaScript-programmeringsfärdigheter och göra det möjligt för dig att skriva mer modulär, återanvändbar och underhållbar kod.

Vanliga frågor om JavaScript-stängningar

  1. Vad är en stängning i JavaScript?
  2. En stängning är en funktion som behåller tillgången till sitt lexikaliska omfång, även när funktionen exekveras utanför det omfånget.
  3. Varför sker stängningar i slingor?
  4. Stängningar i loopar uppstår eftersom loopen skapar funktioner som fångar samma variabelreferens, vilket leder till oväntat beteende om det inte hanteras korrekt.
  5. Hur kan vi åtgärda stängningsproblem i loopar?
  6. Använder sig av let istället för var i slingor eller med hjälp av IIFE (Omedelbart anropade funktionsuttryck) kan fixa stängningsproblem genom att skapa ett nytt omfång för varje iteration.
  7. Vad är en IIFE?
  8. En IIFE är en funktion som exekveras direkt efter att den har skapats, ofta används för att skapa ett nytt omfång och undvika variabla konflikter.
  9. Kan stängningar användas i asynkron programmering?
  10. Ja, stängningar är viktiga i asynkron programmering för att upprätthålla tillståndet och sammanhanget över asynkrona operationer som löften och återuppringningar.
  11. Vad är memoisering och hur hjälper nedläggningar?
  12. Memoisering är en optimeringsteknik för att cachelagra resultaten av dyra funktionsanrop. Stängningar hjälper genom att behålla åtkomsten till cachen över flera funktionsanrop.
  13. Hur hjälper stängningar vid hantering av evenemang?
  14. Stängningar behåller tillståndet för variabler som behövs av händelsehanterare, vilket säkerställer att de fungerar korrekt när händelsen utlöses.
  15. Vad är modulmönstret i JavaScript?
  16. Modulmönstret använder stängningar för att skapa privata variabler och funktioner, kapsla in funktionalitet och undvika globala föroreningar.
  17. Kan stängningar simulera privata metoder i JavaScript?
  18. Ja, stängningar kan simulera privata metoder genom att hålla variabler och funktioner tillgängliga endast inom funktionens räckvidd där de är definierade.

Slutliga tankar om JavaScript-stängningar i loopar

Att bemästra stängningar i JavaScript, särskilt inom loopar, är avgörande för att skriva förutsägbar och effektiv kod. Genom att utnyttja let, Promises, och IIFE, kan utvecklare undvika vanliga fallgropar och säkerställa korrekt variabel omfattning. Denna förståelse förbättrar förmågan att hantera asynkrona uppgifter och händelsedriven programmering, vilket i slutändan leder till mer robusta applikationer.