Pernah merasakan kod anda mempunyai fikirannya sendiri—berkecoh dan enggan kekal teratur? Jangan risau, kita semua pernah ke sana. JavaScript boleh menjadi rumit, walaupun untuk ahli sihir berpengalaman. Tetapi bagaimana jika saya memberitahu anda ada senjata rahsia untuk mengawal keadaan? Masukkan penutupan.
Fikirkan penutupan sebagai beg galas ajaib fungsi anda membawa, menyimpan pembolehubah dan kenangan yang mungkin diperlukan kemudian. Keajaiban pengaturcaraan kecil ini memastikan kod anda teratur, mengurus keadaan tanpa kekacauan dan membuka pintu kepada corak yang dinamik dan fleksibel.
Dengan menguasai penutupan, anda akan membuka kunci tahap kuasa dan keanggunan baharu dalam kod anda. Jadi, ambil tongkat pengekodan anda (atau kopi pekat ☕), dan mari kita menerokai alam tersembunyi ini bersama-sama. ?✨
Penutupan hanyalah fungsi yang mengingati pembolehubah daripada persekitaran asalnya—walaupun selepas persekitaran itu tidak lagi wujud. Daripada membuang pembolehubah tersebut, JavaScript menyimpannya, bersedia untuk dipanggil apabila diperlukan.
const createCounter = () => { let count = 0; // Private variable in the closure's secret realm return () => { count++; // Whispers an increment to the hidden counter return count; // Reveal the mystical number }; } // Summoning our magical counter const counter = createCounter(); console.log(counter()); // Outputs: 1 console.log(counter()); // Outputs: 2 console.log(counter()); // Outputs: 3 console.log(counter.count); // Outputs: undefined (`count` is hidden!) ?️♀️
Fungsi dalaman mengekalkan akses untuk mengira, walaupun createCounter telah selesai dijalankan. "Memori" ini ialah intipati penutupan—memastikan data anda selamat dan membolehkan kod yang berkuasa dan fleksibel. ?✨
Walaupun penutupan mungkin terasa ajaib, ia hanyalah hasil daripada cara JavaScript mengendalikan skop dan ingatan. Setiap fungsi membawa pautan ke persekitaran leksikalnya—konteks di mana ia ditakrifkan.
? Persekitaran leksikal ialah rekod berstruktur bagi pengikatan pembolehubah, mentakrifkan perkara yang boleh diakses dalam skop itu. Ia seperti peta yang menunjukkan pembolehubah dan fungsi yang tinggal di dalam blok atau fungsi tertentu.
Penutupan tidak hanya mengunci satu nilai; mereka menjejaki perubahan dari semasa ke semasa. Jika pembolehubah skop luar dikemas kini, penutupan melihat nilai baharu.
const createCounter = () => { let count = 0; // Private variable in the closure's secret realm return () => { count++; // Whispers an increment to the hidden counter return count; // Reveal the mystical number }; } // Summoning our magical counter const counter = createCounter(); console.log(counter()); // Outputs: 1 console.log(counter()); // Outputs: 2 console.log(counter()); // Outputs: 3 console.log(counter.count); // Outputs: undefined (`count` is hidden!) ?️♀️
Penutupan membolehkan pengkapsulan dengan mencipta pembolehubah peribadi dengan akses terkawal, urus keadaan merentas berbilang panggilan tanpa bergantung pada global dan kuasa tingkah laku dinamik seperti kilang, panggilan balik , dan cangkuk.
Rangka kerja seperti React memanfaatkan kuasa ini, membiarkan komponen berfungsi kekal tanpa kewarganegaraan sambil menguruskan keadaan dengan cangkuk seperti useState—semuanya berkat keajaiban penutupan.
Penutupan boleh menyimpan keadaan, menjadikannya sesuai untuk ejaan seperti caching hasil operasi yang mahal. Mari kita terokai langkah demi langkah ini dan tingkatkan mantera kita semasa kita pergi.
Mantra pertama kami adalah ringkas tetapi berkuasa: penjaga ingatan. Jika diminta untuk input yang sama sekali lagi, ia mengembalikan hasil cache serta-merta.
// A variable in the global magical realm let multiplier = 2; const createMultiplier = () => { // The inner function 'captures' the essence of the outer realm return (value: number): number => value * multiplier; }; // Our magical transformation function const double = createMultiplier(); console.log(double(5)); // Outputs: 10 multiplier = 3; console.log(double(5)); // Outputs: 15 (The magic adapts!) ✨
Sesetengah mantera, walau bagaimanapun, terlalu kuat untuk kekal selama-lamanya. Mari tingkatkan cache kita dengan keupayaan untuk melupakan kenangan lama. Kami akan mencipta CacheEntry untuk menyimpan bukan sahaja nilai, tetapi jangka hayatnya yang ajaib.
(Perhatikan cara kami membina idea sebelumnya—penutupan memudahkan untuk menambah kerumitan tanpa kehilangan landasan.)
function withCache(fn: (...args: any[]) => any) { const cache: Record<string, any> = {}; return (...args: any[]) => { const key = JSON.stringify(args); // Have we encountered these arguments before? if (key in cache) return cache[key]; // Recall of past magic! ? // First encounter? Let's forge a new memory const result = fn(...args); cache[key] = result; return result; }; } // Example usage const expensiveCalculation = (x: number, y: number) => { console.log('Performing complex calculation'); return x * y; }; // Summoning our magical cached calculation const cachedCalculation = withCache(expensiveCalculation); console.log(cachedCalculation(4, 5)); // Calculates and stores the spell console.log(cachedCalculation(4, 5)); // Uses cached spell instantly
Kadangkala, mantra memerlukan masa—seperti menunggu oracle (atau API) yang jauh untuk bertindak balas. Mantra kami juga boleh mengatasinya. Ia akan menunggu janji, menyimpan nilai yang diselesaikan dan mengembalikannya pada masa hadapan—tiada pengambilan berulang.
type CacheEntry<T> = { value: T; expiry: number; }; function withCache<T extends (...args: any[]) => any>( fn: T, expirationMs: number = 5 * 60 * 1000, // Default 5 minutes ) { const cache = new Map<string, CacheEntry<ReturnType<T>>>(); return (...args: Parameters<T>): ReturnType<T> => { const key = JSON.stringify(args); const now = Date.now(); // Current magical moment const cached = cache.get(key); // Is our magical memory still vibrant? if (cached && now < cached.expiry) return cached.value; // The memory has faded; it’s time to create new ones! const result = fn(...args); cache.set(key, { value: result, expiry: now + expirationMs }); return result; }; } // ... const timeLimitedCalc = withCache(expensiveCalculation, 3000); // 3-second cache console.log(timeLimitedCalc(4, 5)); // Stores result with expiration console.log(timeLimitedCalc(4, 5)); // Returns cached value before expiry setTimeout(() => { console.log(timeLimitedCalc(4, 5)); // Recalculates after expiration }, 3000);
Teroka ejaan penuh di sini.
Mantra caching kami hebat, tetapi ia hanya permulaan. Fikirkan anda boleh tingkatkan kod? Pertimbangkan untuk menambah pengendalian ralat, melaksanakan pembersihan memori ajaib atau mencipta strategi caching yang lebih canggih. Seni pengekodan sebenar terletak pada percubaan, menolak sempadan, dan membayangkan semula kemungkinan! ??
Penutupan adalah hebat, tetapi mantra terbaik pun datang dengan risiko. Mari kita temukan beberapa perangkap biasa dan penyelesaiannya untuk membantu anda menggunakan penutupan dengan yakin.
Gotch JavaScript klasik, sering dipaparkan dalam temu bual pengekodan, melibatkan gelung—khususnya, cara mereka mengendalikan pembolehubah gelung dan penutupan.
// ... // The memory has faded; it’s time to create new ones! const result = fn(...args); if (result instanceof Promise) { return result.then((value) => { cache.set(key, { value, expiry: now + expirationMs }); return value; }); } // ...
Contoh di atas mencatatkan nombor 5 lima kali kerana var mencipta pembolehubah tunggal yang dikongsi untuk semua penutupan.
Penyelesaian 1: Gunakan let untuk memastikan skop blok.
Kata kunci let mencipta pembolehubah berskop blok baharu untuk setiap lelaran, jadi penutupan menangkap nilai yang betul.
const createCounter = () => { let count = 0; // Private variable in the closure's secret realm return () => { count++; // Whispers an increment to the hidden counter return count; // Reveal the mystical number }; } // Summoning our magical counter const counter = createCounter(); console.log(counter()); // Outputs: 1 console.log(counter()); // Outputs: 2 console.log(counter()); // Outputs: 3 console.log(counter.count); // Outputs: undefined (`count` is hidden!) ?️♀️
Penyelesaian 2: Gunakan IIFE (Ungkapan Fungsi Dipanggil Serta-merta).
IIFE mencipta skop baharu untuk setiap lelaran, memastikan pengendalian pembolehubah yang betul dalam gelung.
// A variable in the global magical realm let multiplier = 2; const createMultiplier = () => { // The inner function 'captures' the essence of the outer realm return (value: number): number => value * multiplier; }; // Our magical transformation function const double = createMultiplier(); console.log(double(5)); // Outputs: 10 multiplier = 3; console.log(double(5)); // Outputs: 15 (The magic adapts!) ✨
Petua Bonus: ? Helah Fungsional.
Beberapa ahli sihir mengetahui mantra ini, dan sejujurnya, saya jarang (jika pernah) melihatnya disebut semasa temu bual pengekodan. Tahukah anda bahawa setTimeout boleh menghantar argumen tambahan terus kepada panggilan baliknya?
function withCache(fn: (...args: any[]) => any) { const cache: Record<string, any> = {}; return (...args: any[]) => { const key = JSON.stringify(args); // Have we encountered these arguments before? if (key in cache) return cache[key]; // Recall of past magic! ? // First encounter? Let's forge a new memory const result = fn(...args); cache[key] = result; return result; }; } // Example usage const expensiveCalculation = (x: number, y: number) => { console.log('Performing complex calculation'); return x * y; }; // Summoning our magical cached calculation const cachedCalculation = withCache(expensiveCalculation); console.log(cachedCalculation(4, 5)); // Calculates and stores the spell console.log(cachedCalculation(4, 5)); // Uses cached spell instantly
Penutupan mengekalkan rujukan kepada skop luarnya, yang bermaksud pembolehubah mungkin kekal lebih lama daripada yang dijangkakan, yang membawa kepada kebocoran memori.
type CacheEntry<T> = { value: T; expiry: number; }; function withCache<T extends (...args: any[]) => any>( fn: T, expirationMs: number = 5 * 60 * 1000, // Default 5 minutes ) { const cache = new Map<string, CacheEntry<ReturnType<T>>>(); return (...args: Parameters<T>): ReturnType<T> => { const key = JSON.stringify(args); const now = Date.now(); // Current magical moment const cached = cache.get(key); // Is our magical memory still vibrant? if (cached && now < cached.expiry) return cached.value; // The memory has faded; it’s time to create new ones! const result = fn(...args); cache.set(key, { value: result, expiry: now + expirationMs }); return result; }; } // ... const timeLimitedCalc = withCache(expensiveCalculation, 3000); // 3-second cache console.log(timeLimitedCalc(4, 5)); // Stores result with expiration console.log(timeLimitedCalc(4, 5)); // Returns cached value before expiry setTimeout(() => { console.log(timeLimitedCalc(4, 5)); // Recalculates after expiration }, 3000);
Apa yang berlaku di sini? Penutupan ini mengekalkan keseluruhan pembolehubah data, walaupun hanya sekeping kecil diperlukan, yang berpotensi membazirkan sumber yang ketara.
Penyelesaiannya adalah untuk mengurus dengan teliti perkara yang ditangkap oleh penutupan dan secara eksplisit mengeluarkan rujukan yang tidak perlu. Ini memastikan set data yang besar dimuatkan hanya apabila diperlukan dan dibebaskan secara proaktif dengan kaedah pembersihan.
// ... // The memory has faded; it’s time to create new ones! const result = fn(...args); if (result instanceof Promise) { return result.then((value) => { cache.set(key, { value, expiry: now + expirationMs }); return value; }); } // ...
Penutupan boleh menyebabkan tingkah laku yang tidak dijangka apabila keadaan kongsi diubah. Perkara yang kelihatan seperti rujukan mudah boleh membawa kepada kesan sampingan yang tidak diingini.
for (var i = 0; i < 5; i++) { setTimeout(() => { console.log(i); // Logs 5, five times ? }, i * 1000); }
Apa yang berlaku di sini? Kaedah getUsers mendedahkan tatasusunan pengguna, memecahkan enkapsulasi dan mempertaruhkan kesan sampingan yang tidak diingini daripada pengubahsuaian luaran.
Penyelesaian adalah dengan mengembalikan salinan keadaan dalaman. Ini menghalang pengubahsuaian luaran, mengekalkan integriti data dan melindungi logik dalaman penutupan.
for (let i = 0; i < 5; i++) { setTimeout(() => { console.log(i); // Works as expected ? }, i * 1000); }
Menguasai teknik ini akan membantu anda menggunakan keajaiban penutupan dengan yakin. Penguasaan sejati terletak pada pemahaman, bukan penghindaran. ✨
Penutupan mungkin pada mulanya kelihatan rumit, tetapi ia membuka kunci potensi untuk menulis kod yang lebih elegan dan cekap. Dengan menukar fungsi mudah kepada entiti yang berterusan dan berstatus, penutupan boleh berkongsi rahsia secara elegan merentas masa dan ruang. Ciri berkuasa ini meningkatkan JavaScript daripada menjadi bahasa skrip yang mudah kepada alat yang berkuasa dan fleksibel untuk menyelesaikan masalah yang rumit.
Perjalanan anda tidak berakhir di sini; menyelam lebih dalam ke dalam corak async, pengaturcaraan berfungsi dan kerja dalaman enjin JavaScript. Setiap langkah mendedahkan lebih banyak lapisan bahasa yang mempesonakan ini, mencetuskan idea dan penyelesaian baharu.
Lagipun, penguasaan sebenar datang daripada rasa ingin tahu dan penerokaan. Semoga kod anda sentiasa elegan, cekap dan agak ajaib. ?
Atas ialah kandungan terperinci Penutupan Didedahkan: Meneroka Alam Tersembunyi JavaScript. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!