Heim >Web-Frontend >js-Tutorial >Die Lücke, die LeetCodes Tage von JavaScript tatsächlich füllen

Die Lücke, die LeetCodes Tage von JavaScript tatsächlich füllen

Patricia Arquette
Patricia ArquetteOriginal
2024-12-16 16:34:10908Durchsuche

The Gap That LeetCode

Bei den meisten Codierungsherausforderungen lernen Sie, Rätsel zu lösen. Der 30-Tage-JavaScript-Lernplan von LeetCode macht etwas anderes: Er zeigt Ihnen, wie sich Puzzleteile in Bausteine ​​verwandeln lassen, mit denen Sie reale Projekte erstellen können.

Diese Unterscheidung ist wichtig. Wenn Sie ein typisches algorithmisches Problem lösen, trainieren Sie Ihren Geist, abstrakt zu denken. Aber wenn Sie eine entprellte Funktion1 implementieren oder einen Ereignisemitter erstellen2, lernen Sie, wie echte Software funktioniert.

Das habe ich entdeckt, als ich die Herausforderungen selbst durchgearbeitet habe. Die Erfahrung ähnelte weniger dem Lösen von Denksportaufgaben, sondern eher einer Archäologie – der Entdeckung spezifischer, moderner JavaScript-Konzepte. Jeder Abschnitt konzentrierte sich auf einen anderen Teil der modernen Funktionen von JS.

Das Besondere an diesem Lernplan ist, dass er Ihnen kein JavaScript beibringt. Tatsächlich glaube ich, dass man JavaScript bereits einigermaßen gut beherrschen muss, um davon zu profitieren. Stattdessen wird gelehrt, wie JavaScript tatsächlich zur Lösung realer technischer Probleme eingesetzt wird.

Denken Sie an die Memoize3-Herausforderung. Oberflächlich betrachtet geht es um das Zwischenspeichern von Funktionsergebnissen. Aber was Sie wirklich lernen, ist, warum Bibliotheken wie React eine Memoisierung benötigen, um das Rendern von Komponenten effizient zu handhaben. Oder nehmen Sie das Debounce1-Problem – es geht nicht nur um die Implementierung von Verzögerungen; Es hilft Ihnen, aus erster Hand zu verstehen, warum jedes moderne Frontend-Framework, jeder Elevator und grundsätzlich jedes System mit einer interaktiven Benutzeroberfläche dieses Muster benötigt.

Dieser Fokus auf praktische Muster statt auf Sprachgrundlagen schafft eine interessante Einschränkung; Sie müssen sich in einer von zwei Positionen befinden, um davon zu profitieren:

  1. Sie verstehen die CS-Grundlagen (insbesondere Datenstrukturen und Algorithmen) und sind mit JavaScript vertraut
  2. Du bist gut in CS-Theorie und hast bereits Erfahrung mit JavaScript

Brücke zwischen Informatik und Software-Engineering

Etwas Seltsames passiert zwischen dem Erlernen der Informatik und der Ausübung der Softwareentwicklung. Der Übergang fühlt sich an, als würde man jahrelang Schachtheorie lernen, nur um dann festzustellen, dass man ein völlig anderes Spiel spielt – eines, bei dem sich die Regeln ständig ändern und die meisten Züge in keinem Buch stehen.

In CS lernen Sie, wie ein Binärbaum funktioniert. In der Softwareentwicklung verbringen Sie Stunden damit, Ihre API zu debuggen und zu verstehen, warum das Antwort-Caching nicht funktioniert. Aus der Ferne mag die Überschneidung dieser Welten deutlich größer erscheinen, als sie tatsächlich ist. Da gibt es eine Lücke, die CS-Absolventen oft schockiert, wenn sie ihre Karriere beginnen. Leider schaffen es die meisten Bildungsressourcen nicht, dies zu überbrücken. Sie bleiben entweder rein theoretisch („So funktioniert Quicksort“) oder rein praktisch („So stellt man eine React-App bereit“).

Was diesen JavaScript-Lernplan interessant macht, ist nicht, dass er besonders gut gestaltet ist, sondern dass er Verbindungen zwischen diesen Welten herstellt. Nehmen Sie das Memoisierungsproblem: 2623. Merken Sie sich3. In CS-Begriffen geht es darum, berechnete Werte zwischenzuspeichern. Die Implementierung zwingt Sie jedoch dazu, sich mit den Besonderheiten von JavaScript in Bezug auf Objektreferenzen, Funktionskontexte und Speicherverwaltung auseinanderzusetzen. Plötzlich,
Sie lernen nicht nur einen Algorithmus – Sie beginnen zu verstehen, warum so etwas wie Redis existiert.

Dieser Stil wiederholt sich während der Herausforderungen. Bei der Event Emitter2-Implementierung geht es nicht nur um ein Lehrbuch-Beobachtermuster – Sie können es als den Grund betrachten, warum es tatsächlich Sinn machte, die V8-Engine aus dem Browser zu entfernen und Node.js darauf aufzubauen . Der Promise Pool4 befasst sich mit der parallelen Ausführung, auch bekannt als dem Grund, warum Ihre Datenbank eine Verbindungsbegrenzung benötigt.

Der verborgene Lehrplan

Die Reihenfolge der Aufgaben in diesem Studienplan ist nicht zufällig. Es geht darum, Schicht für Schicht ein mentales Modell des modernen JavaScript aufzubauen.

Es beginnt mit Schließungen. Nicht weil Abschlüsse das einfachste Konzept sind – sie sind notorisch verwirrend – sondern weil sie grundlegend dafür sind, wie JavaScript den Zustand verwaltet.

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

const counter1 = createCounter(10);
console.log(counter1()); // 10
console.log(counter1()); // 11
console.log(counter1()); // 12

// const counter1 = createCounter(10);
// when this^ line executes:
// - createCounter(10) creates a new execution context
// - local variable count is initialized to 10
// - a new function is created and returned
// - this returned function maintains access 
// to the count variable in its outer scope
// - this entire bundle 
// (function (the inner one) + its access to count) 
// is what we call a closure

Dieses Muster ist der Ausgangspunkt für die gesamte Zustandsverwaltung in JavaScript. Sobald Sie verstehen, wie dieser Zähler funktioniert, verstehen Sie auch, wie useState von React unter der Haube funktioniert. Sie verstehen, warum Modulmuster in JavaScript vor ES6 entstanden sind.

Dann geht der Plan zu Funktionstransformationen über. Hier lernen Sie die Funktionsdekoration kennen, bei der Funktionen andere Funktionen umschließen, um deren Verhalten zu ändern. Dies ist nicht nur ein technischer Trick; Es geht darum, wie Express-Middlewares funktionieren, wie React-Komponenten höherer Ordnung funktionieren,
und auch, wie TypeScript-Dekoratoren funktionieren.

Wenn Sie die asynchronen Herausforderungen erreichen, lernen Sie nicht nur etwas über Promises – Sie entdecken auch, warum JavaScript sie überhaupt benötigte. Das Promise Pool4-Problem besteht nicht darin, Ihnen ein innovatives, skurriles JS-Konzept beizubringen; Es zeigt Ihnen, warum Verbindungspooling in jeder Datenbank-Engine vorhanden ist.

Hier ist eine grobe Zuordnung der Abschnitte des Studienplans zu realen Software-Engineering-Konzepten:

  • Schließungen → Staatsverwaltung
  • Grundlegende Array-Transformationen → Grundfertigkeit (Hilfsfertigkeit); Praxisbeispiel: Datenmanipulation
  • Funktionstransformationen → Middleware-Muster
  • Versprechen und Zeit -> Asynchroner Kontrollfluss
  • JSON -> Grundfertigkeit (Hilfskompetenz); Praxisbeispiel: Datenserialisierung, API-Kommunikation
  • Klassen (insbesondere im Kontext von Event Emittern) → Message Passing Systems
  • Bonus (Premium gesperrt) -> Mischung aus schwierigeren Herausforderungen, die in den oben genannten Abschnitten hätten enthalten sein können; Promise Pool4 ist mein Favorit aus diesem Abschnitt

Mustererkennung, nicht Problemlösung

Lassen Sie uns einige Probleme analysieren, die den wahren Wert dieses Studienplans verdeutlichen.

  1. Merken (#2623)

Denken Sie an die Memoize-Herausforderung. Was ich daran liebe, ist die Tatsache, dass es die beste Lösung ist (die ich finden konnte)
ist so unkompliziert, als würde der Code selbst Ihnen sanft sagen, was er tut (ich habe trotzdem einige Kommentare eingefügt).

Das macht #2623 keineswegs zu einem einfachen Problem. Ich brauchte zwei vorherige Iterationen, um es so sauber zu machen:

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

const counter1 = createCounter(10);
console.log(counter1()); // 10
console.log(counter1()); // 11
console.log(counter1()); // 12

// const counter1 = createCounter(10);
// when this^ line executes:
// - createCounter(10) creates a new execution context
// - local variable count is initialized to 10
// - a new function is created and returned
// - this returned function maintains access 
// to the count variable in its outer scope
// - this entire bundle 
// (function (the inner one) + its access to count) 
// is what we call a closure
  1. Entprellen (#2627)

Stellen Sie sich vor, Sie befinden sich in einem Aufzug und da ist eine Person, die wiederholt hektisch auf die Taste „Tür schließen“ drückt.

drücken drücken drücken drücken drücken

Ohne Entprellung: Der Aufzug würde bei jedem einzelnen Tastendruck versuchen, die Tür zu schließen, wodurch der Türmechanismus ineffizient funktioniert und möglicherweise kaputt geht.

Mit Entprellung: Der Aufzug wartet, bis die Person für eine bestimmte Zeit (sagen wir 0,5 Sekunden) aufgehört hat zu drücken, bevor er tatsächlich versucht, die Tür zu schließen. Das ist viel effizienter.

Hier ist ein weiteres Szenario:

Stellen Sie sich vor, Sie implementieren eine Suchfunktion, die Ergebnisse abruft, wenn ein Benutzer Folgendes eingibt:

Ohne Entprellung:

/**
 * @param {Function} fn
 * @return {Function}
 */
function memoize(fn) {
    // Create a Map to store our results
    const cache = new Map();

    return function(...args) {
        // Create a key from the arguments
        const key = JSON.stringify(args);

        // If we've seen these arguments before, return cached result
        if (cache.has(key)) {
            return cache.get(key);
        }

        // Otherwise, calculate result and store it
        const result = fn.apply(this, args);
        cache.set(key, result);
        return result;
    }
}

const memoizedFn = memoize((a, b) => {
    console.log("computing...");
    return a + b;
});

console.log(memoizedFn(2, 3)); // logs "computing..." and returns 5
console.log(memoizedFn(2, 3)); // just returns 5, no calculation
console.log(memoizedFn(3, 4)); // logs "computing..." and returns 7


// Explanantion:
// It's as if our code had access to an external database

// Cache creation
// const cache = new Map();
// - this^ uses a closure to maintain the cache between function calls
// - Map is perfect for key-value storage

// Key creation
// const key = JSON.stringify(args);
// - this^ converts arguments array into a string
// - [1,2] becomes "[1,2]"
// - we are now able to use the arguments as a Map key

// Cache check
// if (cache.has(key)) {
//     return cache.get(key);
// }
// - if we've seen these arguments before, return cached result;
// no need to recalculate

Dies würde 10 API-Aufrufe erfordern. Die meisten davon sind nutzlos, da der Benutzer noch tippt.

Mit Entprellung (300 ms Verzögerung):

// typing "javascript"
'j' -> API call
'ja' -> API call
'jav' -> API call
'java' -> API call
'javas' -> API call
'javasc' -> API call
'javascr' -> API call
'javascri' -> API call
'javascrip' -> API call
'javascript' -> API call

Entprellen ist, als würde man Ihrem Code sagen: „Warten Sie, bis der Benutzer X Millisekunden lang aufgehört hat, etwas zu tun, bevor Sie diese Funktion tatsächlich ausführen.“

Hier ist die Lösung für LeetCode #2627:

// typing "javascript"
'j'
'ja'
'jav'
'java'
'javas'
'javasc'
'javascr'
'javascri'
'javascrip'
'javascript' -> API call (only one call, 300ms after user stops typing)

Andere gängige reale Anwendungsfälle für die Entprellung (abgesehen von Suchleisten):

  • Entwürfe speichern (warten, bis der Benutzer die Bearbeitung beendet)
  • Senden-Schaltfläche (doppelte Übermittlungen verhindern)

Was es falsch macht

Ich hoffe, dass aufgrund des insgesamt positiven Tons dieses Artikels meine Meinung zu 30 Days of JS inzwischen klar geworden ist.

Aber keine Bildungsressource ist perfekt, und wenn es um Einschränkungen geht, ist Ehrlichkeit wertvoll. Dieser Studienplan weist mehrere blinde Flecken auf, die es wert sind, untersucht zu werden.

Der Studienplan setzt zunächst ein gewisses Maß an Vorkenntnissen voraus.
Wenn Sie mit JavaScript noch nicht vertraut sind, können einige der Herausforderungen überwältigend sein. Dies kann für Anfänger entmutigend sein, die möglicherweise andere Erwartungen an den Studienplan hatten.

Zweitens werden die Herausforderungen isoliert dargestellt.
Das macht am Anfang Sinn, kann aber im weiteren Verlauf des Plans enttäuschend sein. Probleme in der realen Welt erfordern oft die Kombination mehrerer Muster und Techniken. Der Studienplan könnte von stärker integrierten Herausforderungen profitieren, die die gemeinsame Verwendung mehrerer Konzepte erfordern (Ausnahme: Wir verwenden im gesamten Plan Abschlüsse). Diese könnten gut in den Bonusbereich passen (der bereits Premium-Benutzern vorbehalten ist).

Schließlich liegt die Hauptschwäche dieser Reihe von Herausforderungen in ihren Konzepterklärungen. Komme aus der Wettbewerbsprogrammierung,
Ich bin es gewohnt, in Problemstellungen klare Definitionen neuer Begriffe und Konzepte zu geben. Allerdings sind die Beschreibungen von LeetCode oft unnötig komplex – ihre Erklärung einer entprellten Funktion zu verstehen ist schwieriger als die eigentliche Lösung zu implementieren.

Trotz seiner Mängel ist der Studienplan eine wertvolle Ressource für das Verständnis von modernem JavaScript.

Über die 30 Tage hinaus

Diese Muster zu verstehen ist erst der Anfang.
Die eigentliche Herausforderung besteht darin, zu erkennen, wann und wie sie im Produktionscode anzuwenden sind. Folgendes habe ich entdeckt, nachdem ich diesen Mustern in freier Wildbahn begegnet bin.

Erstens treten diese Muster selten isoliert auf. Echte Codebasen kombinieren sie auf eine Weise, die die Herausforderungen nicht erkunden können. Erwägen Sie eine von Grund auf implementierte Suchfunktion. Möglicherweise verwenden Sie Folgendes:

  • Entprellung für die Eingabeverarbeitung
  • Memoisierung für die Zwischenspeicherung von Ergebnissen
  • Versprechen Sie Zeitüberschreitungen für API-Aufrufe
  • Ereignisemitter für die Suchstatusverwaltung

Alle diese Muster interagieren und erzeugen eine Komplexität, auf die Sie keine einzelne Herausforderung vorbereitet. Aber nachdem Sie jeden Teil selbst implementiert haben, erhalten Sie eine allgemeine Vorstellung davon, wie die gesamte Implementierung funktionieren soll.

Entgegen der Intuition besteht die wertvollste Fähigkeit, die Sie erwerben werden, nicht darin, diese Muster zu implementieren, sondern darin, sie im Code anderer Leute zu erkennen.

Letzte Gedanken

Nach Abschluss dieses Studienplans sind Codierungsinterviews nicht der einzige Ort, an dem Sie diese Muster erkennen werden.

Sie werden sie im Open-Source-Code, in den Pull-Requests Ihrer Kollegen entdecken und vielleicht bemerken Sie sie auch in Ihren vergangenen Projekten. Möglicherweise haben Sie sie schon einmal implementiert, ohne es zu merken. Am wichtigsten ist, dass Sie verstehen, warum sie dort sind.

Was mit dem Lösen von Rätseln begann, entwickelte sich zu einem tieferen Verständnis des modernen JavaScript-Ökosystems.

Das ist die Lücke, die dieser Studienplan füllt: die Verbindung von theoretischem Wissen mit praktischer Ingenieursweisheit.



  1. 2627. Entprellen (Versprechen und Zeit) ↩

  2. 2694. Ereignisemitter (Klassen) ↩

  3. 2623. Memoize (Funktionstransformationen) ↩

  4. 2636. Versprechenspool (Bonus) ↩

Das obige ist der detaillierte Inhalt vonDie Lücke, die LeetCodes Tage von JavaScript tatsächlich füllen. 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