Heim  >  Artikel  >  Web-Frontend  >  Eine ausführliche Erklärung der Schließungen in JavaScript

Eine ausführliche Erklärung der Schließungen in JavaScript

青灯夜游
青灯夜游Original
2023-04-24 17:57:493515Durchsuche

Der JavaScript-Abschluss ist ein wichtiges Konzept, das in der JavaScript-Programmierung weit verbreitet ist. Obwohl es für Anfänger verwirrend sein kann, ist es eines der Schlüsselkonzepte zum Verständnis des Kerns der JavaScript-Sprache. Dieser Artikel befasst sich ausführlich mit JavaScript-Abschlüssen, damit Sie verstehen, wie sie funktionieren und wie Sie sie in realen Anwendungen verwenden.

Eine ausführliche Erklärung der Schließungen in JavaScript

Was sind JavaScript-Abschlüsse?

In JavaScript bedeutet ein Abschluss, dass eine Funktion auf außerhalb definierte Variablen zugreifen kann. Diese Variablen werden oft als „freie Variablen“ bezeichnet, da sie weder lokal für die Funktion noch Argumente für die Funktion sind. Abschlüsse können innerhalb einer Funktion oder außerhalb einer Funktion erstellt werden.

Jede Funktion in JavaScript ist ein Abschluss, da sie alle Zugriff auf freie Variablen haben. Wenn eine Funktion aufgerufen wird, erstellt sie eine neue Ausführungsumgebung, die die lokalen Variablen und Parameter der Funktion enthält. Der Ausführungskontext enthält auch einen Verweis auf den Bereich, in dem die Funktion definiert ist. Diese Referenz wird als „Bereichskette“ der Funktion bezeichnet. Dabei handelt es sich um eine verknüpfte Liste aller Bereichsobjekte, die die Definition der Funktion enthalten. [Empfohlenes Lernen: Javascript-Video-Tutorial]

Wenn innerhalb einer Funktion auf eine freie Variable zugegriffen werden muss, prüft sie zunächst, ob die Variable in ihrer eigenen lokalen Variablen vorhanden ist. Wenn sie nicht vorhanden ist, wird sie in der Bereichskette fortgesetzt, bis sie die Variable findet. Dies ist der Kernmechanismus von Verschlüssen.

Einfach ausgedrückt ist ein Abschluss eine Funktion, die Verweise auf externe Variablen enthält, die außerhalb der Funktion definiert sind, aber dennoch innerhalb der Funktion aufgerufen und manipuliert werden können. Der Kern des Abschlusses besteht darin, eine Funktion und die externen Variablen, auf die sie sich bezieht, zu kapseln und eine Umgebung ohne externe Störungen zu schaffen, sodass die Funktion auf externe Variablen zugreifen und diese ändern kann. Diese Änderungen werden auch in den Variablen außerhalb der Funktion widergespiegelt.

Zu verstehen, wie Abschlüsse funktionieren, ist für das Schreiben von qualitativ hochwertigem JavaScript-Code von entscheidender Bedeutung, da wir so den Umfang von Variablen und Funktionen besser verwalten und komplexere Funktionen implementieren können.

Verwendung von Abschlüssen

Einkapseln von Variablen und Funktionen

Abschlüsse können verwendet werden, um Variablen zu kapseln, sodass sie keinen externen Störungen ausgesetzt sind. Dies liegt daran, dass ein Abschluss eine Variable innerhalb einer Funktion definieren und eine Funktion außerhalb der Funktion erstellen kann, die auf diese Variable zugreift. Diese Zugriffsfunktion kann auf die Variable zugreifen, es kann jedoch nicht direkt von außen auf die Variable zugegriffen werden, wodurch die Sicherheit der Variablen gewährleistet wird.

Zum Beispiel können wir Abschlüsse verwenden, um einen Zähler zu implementieren:

function createCounter() {
  let count = 0;
  return function() {
    count++;
    return count;
  }
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

In diesem Beispiel verwenden wir Abschlüsse, um die Anzahl der Zählervariablen zu kapseln, damit sie keinen externen Störungen ausgesetzt ist. Bei jedem Aufruf der Zählerfunktion wird der nächste Wert des Zählers zurückgegeben.

Daten zwischenspeichern

Durch die Verwendung von Abschlüssen können die Berechnungsergebnisse von Funktionen zwischengespeichert werden, um zu vermeiden, dass derselbe Wert mehrmals berechnet wird, wodurch die Leistung des Codes verbessert wird. Diese Methode eignet sich für Funktionen, die einen großen Rechenaufwand erfordern, deren Ergebnisse sich jedoch nicht häufig ändern, wie beispielsweise die Fibonacci-Folge.

Sehen Sie sich unten ein Codebeispiel an:

function memoize(fn) {
  const cache = {};
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache[key]) {
      return cache[key];
    } else {
      const result = fn(...args);
      cache[key] = result;
      return result;
    }
  }
}

function fibonacci(n) {
  if (n <= 1) {
    return n;
  }
  return fibonacci(n - 1) + fibonacci(n - 2);
}

const memoizedFib = memoize(fibonacci);
console.log(memoizedFib(10)); // 输出 55
console.log(memoizedFib(10)); // 输出 55,直接从缓存中读取

In diesem Beispiel definieren wir eine Memoize-Funktion, die eine Funktion als Parameter akzeptiert und eine Abschlussfunktion zurückgibt. Die Abschlussfunktion verwaltet intern einen Cache-Objekt-Cache, der zum Speichern der Berechnungsergebnisse der Funktion verwendet wird. Bei jedem Aufruf der Abschlussfunktion generiert sie einen eindeutigen Schlüsselwert basierend auf den übergebenen Parametern und versucht, das Berechnungsergebnis aus dem Cache zu lesen. Wenn der Schlüsselwert bereits im Cache vorhanden ist, wird das Cache-Ergebnis direkt zurückgegeben, andernfalls wird die übergebene Funktion zur Berechnung des Ergebnisses aufgerufen und das Ergebnis im Cache gespeichert. Dieser Ansatz verbessert die Leistung Ihres Codes, indem er die mehrfache Berechnung desselben Werts vermeidet.

Modularität erreichen

Durch die Verwendung von Abschlüssen kann eine modulare Programmiermethode erreicht werden, sodass sich jedes Modul nur auf seine eigenen Funktionen konzentriert, wodurch die Wartbarkeit und Lesbarkeit des Codes verbessert wird. Gleichzeitig können Schließungen auch öffentliche und private Variablen kapseln und so die Verschmutzung globaler Variablen vermeiden.

Zum Beispiel können wir Schließungen verwenden, um ein einfaches Modul zu implementieren:

const module = (function() {
  const privateVar = &#39;I am private&#39;;
  const publicVar = &#39;I am public&#39;;
  function privateFn() {
    console.log(&#39;I am a private function&#39;);
  }
  function publicFn() {
    console.log(&#39;I am a public function&#39;);
  }
  return {
    publicVar,
    publicFn
  };
})();

console.log(module.publicVar); // 输出 &#39;I am public&#39;
module.publicFn(); // 输出 &#39;I am a public function&#39;
console.log(module.privateVar); // 输出 undefined
module.privateFn(); // 报错,无法访问私有函数

In diesem Beispiel definieren wir eine Funktion zur sofortigen Ausführung, die intern ein Objekt zurückgibt. Objekte enthalten öffentliche Variablen und Funktionen sowie private Variablen und Funktionen. Auf diese Weise können wir den Code in mehrere Module aufteilen, wobei sich jedes Modul nur auf seine eigenen Funktionen konzentriert, wodurch die Wartbarkeit und Lesbarkeit des Codes verbessert wird. Gleichzeitig sind private Variablen und Funktionen nur innerhalb der Funktion sichtbar und können von außen nicht aufgerufen und geändert werden, wodurch die Verschmutzung globaler Variablen vermieden wird.

Ereignisabwicklung

Das Folgende ist ein Beispiel für die Verwendung von Schließungen für die Ereignisabwicklung:

function createCounter() {
  let count = 0;

  function increment() {
    count++;
    console.log(`Clicked ${count} times`);
  }

  function decrement() {
    count--;
    console.log(`Clicked ${count} times`);
  }

  function getCount() {
    return count;
  }

  return {
    increment,
    decrement,
    getCount
  };
}

const counter = createCounter();

document.querySelector(&#39;#increment&#39;).addEventListener(&#39;click&#39;, counter.increment);
document.querySelector(&#39;#decrement&#39;).addEventListener(&#39;click&#39;, counter.decrement);

在这个示例中,我们定义了一个名为createCounter的函数,该函数返回一个对象,该对象包含三个方法:increment,decrement和getCount。increment方法将计数器加1,decrement方法将计数器减1,getCount方法返回当前计数器的值。

我们使用createCounter函数创建了一个计数器对象counter,并将increment方法和decrement方法分别注册为加1和减1按钮的点击事件处理函数。由于increment和decrement方法内部引用了createCounter函数内部的局部变量count,因此它们形成了闭包,可以访问和修改count变量。

这个示例中,我们将计数器对象的逻辑封装在一个函数内部,并返回一个包含方法的对象,这样可以避免全局变量的使用,提高代码的可维护性和可重用性。

函数柯里化

以下是一个使用闭包实现的函数柯里化例子:

function add(x) {
  return function(y) {
    return x + y;
  }
}

const add5 = add(5); // x = 5
console.log(add5(3)); // 输出 8
console.log(add5(7)); // 输出 12

在这个例子中,我们定义了一个名为add的函数,该函数接受一个参数x并返回一个内部函数,内部函数接受一个参数y,并返回x + y的结果。

我们使用add函数创建了一个新的函数add5,该函数的x值为5。我们可以多次调用add5函数,每次传入不同的y值进行求和运算。由于add函数返回了一个内部函数,并且内部函数引用了add函数内部的参数x,因此内部函数形成了一个闭包,可以访问和保留x值的状态。

这个例子中,我们实现了一个简单的函数柯里化,将接收多个参数的函数转化为接收一个参数的函数。函数柯里化可以帮助我们更方便地进行函数复合和函数重用。

异步编程

以下是一个使用闭包实现的异步编程的例子:

function fetchData(url) {
  return function(callback) {
    fetch(url)
      .then(response => response.json())
      .then(data => {
        callback(null, data);
      })
      .catch(error => {
        callback(error, null);
      });
  }
}

const getData = fetchData(&#39;https://jsonplaceholder.typicode.com/todos/1&#39;);
getData(function(error, data) {
  if (error) {
    console.error(error);
  } else {
    console.log(data);
  }
});

在这个例子中,我们定义了一个名为fetchData的函数,该函数接受一个URL参数,并返回一个内部函数。内部函数执行异步操作,请求URL并将响应解析为JSON格式的数据,然后调用传入的回调函数并将解析后的数据或错误作为参数传递。

我们使用fetchData函数创建了一个getData函数,该函数请求JSONPlaceholder API的一个TODO项,并将响应解析为JSON格式的数据,然后将数据或错误传递给回调函数。由于fetchData函数返回了一个内部函数,并且内部函数引用了fetchData函数内部的URL参数和回调函数,因此内部函数形成了闭包,可以访问和保留URL参数和回调函数的状态。

这个例子中,我们使用了异步编程模型,通过将回调函数作为参数传递,实现了在异步请求完成后执行相关的操作。使用闭包可以方便地管理异步请求和相关的状态,提高代码的可读性和可维护性。

闭包的缺陷

JS 闭包具有许多优点,但也有一些缺点,包括:

内存泄漏问题

由于闭包会将外部函数的局部变量引用保存在内存中,因此如果闭包一直存在,外部函数的局部变量也会一直存在,从而导致内存泄漏。

在 JavaScript 中,闭包是指一个函数能够访问并操作其父级作用域中的变量,即便该函数已经执行完毕,这些变量仍然存在。由于闭包会引用父级作用域中的变量,因此,这些变量不会在函数执行完毕时被垃圾回收机制回收,从而占用了内存资源,这就是闭包引起内存泄漏的原因。

以下是一个闭包引起内存泄漏的示例:

function myFunc() {
  var count = 0;
  setInterval(function() {
    console.log(++count);
  }, 1000);
}

myFunc();

在这个示例中,myFunc 函数中定义了一个变量 count,然后创建了一个计时器,在每秒钟打印 count 的值。由于计时器函数是一个闭包,它会保留对 myFunc 中的 count 变量的引用,这意味着即使 myFunc 函数执行完毕,计时器函数仍然可以访问 count 变量,从而阻止 count 变量被垃圾回收机制回收。如果我们不停地调用 myFunc 函数,将会创建多个计时器函数,每个函数都会占用一定的内存资源,最终会导致内存泄漏。

性能问题

由于闭包会在每次函数调用时创建新的作用域链,因此会增加函数的内存消耗和运行时间。在循环中创建闭包时,尤其需要注意性能问题。

在JavaScript中,每当创建一个函数时,都会为该函数创建一个新的作用域链。函数作用域链是一个指向其父级作用域的指针列表,其中包含了该函数能够访问的变量和函数。

Ein Abschluss bezieht sich auf eine innerhalb einer Funktion definierte Funktion, die Zugriff auf die Variablen und Parameter der externen Funktion hat und diese Variablen und Parameter nach dem Aufruf der externen Funktion weiterhin verwenden kann. Wenn ein Abschluss erstellt wird, speichert er einen Verweis auf die Bereichskette der äußeren Funktion, sodass bei Bedarf darauf zugegriffen werden kann.

Da der Abschluss einen Verweis auf die Bereichskette der äußeren Funktion enthält, wird bei jedem Funktionsaufruf eine neue Bereichskette erstellt. Dies liegt daran, dass jeder Aufruf einer Funktion eine neue Funktionsbereichskette erstellt, auch wenn die Funktion durch denselben Abschluss erstellt wird. Dies bedeutet, dass jeder Abschluss über eine eigene Bereichskette verfügt und jeder Aufruf des Abschlusses eine neue Bereichskette erstellt.

Dies ist einer der Gründe, warum Sie bei der Verwendung von Verschlüssen vorsichtig sein müssen. Da jeder Aufruf eines Abschlusses eine neue Bereichskette erstellt, kann dies zu Speicherverbrauch und Leistungsproblemen führen. In einigen Fällen kann es erforderlich sein, die Ressourcen des Abschlusses manuell freizugeben, um Probleme mit Speicherverlusten zu vermeiden.

Sicherheitsprobleme

Da Abschlüsse auf lokale Variablen externer Funktionen zugreifen können, kann der Abschluss auf private Daten zugreifen und diese ändern, wenn sie versehentlich in lokalen Variablen gespeichert werden, was zu Sicherheitsproblemen führt.

Lesbarkeitsprobleme

Da Abschlüsse die Lebensdauer von Variablen verlängern und implizit Daten übergeben, können sie das Verstehen und Debuggen von Code erschweren, insbesondere wenn mehrere Funktionsebenen verschachtelt sind.

Zusammenfassung

Obwohl Schließungen eine leistungsstarke Programmiertechnik sind, müssen Sie bei ihrer Verwendung auf die oben genannten Mängel achten und das entsprechende Anwendungsszenario und den geeigneten Programmierstil auswählen, um die Wartbarkeit und Leistung des Codes sicherzustellen.

Weitere Kenntnisse zum Thema Programmierung finden Sie unter: Programmierlehre! !

Das obige ist der detaillierte Inhalt vonEine ausführliche Erklärung der Schließungen in JavaScript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn