الإغلاق في TypeScript
في هذه المقالة، سنشرح الإغلاق في TypeScript۔
YouTube Video
الإغلاق في TypeScript
ما هو الإغلاق؟
الإغلاق يشير إلى القدرة على الاحتفاظ بالمراجع إلى النطاق (البيئة) الذي تم تعريف الوظيفة فيه، حتى إذا تم استدعاء الوظيفة خارج هذا النطاق۔ فيما يلي، سيتم شرح الإغلاق بما في ذلك توضيحات النوع۔
ببساطة، يدمج الإغلاق بين وظيفة وبيئة المتغير حيث تم تعريف الوظيفة، مما يسمح بالوصول إلى تلك البيئة عند استدعاء الوظيفة۔
الآلية الأساسية للإغلاق
في TypeScript، عند تعريف وظيفة داخل وظيفة أخرى، يصبح واضحًا أن الوظيفة الداخلية يمكنها الوصول إلى متغيرات الوظيفة الخارجية۔ إليك مثالاً أساسيًا على الإغلاق مع توضيحات النوع۔
1function outerFunction(): () => void {
2 let outerVariable: string = "I am from outer function";
3
4 function innerFunction(): void {
5 // The inner function accesses the variable of the outer function
6 console.log(outerVariable);
7 }
8
9 return innerFunction;
10}
11
12const closure: () => void = outerFunction();
13closure(); // "I am from outer function"
outerFunction
تعيد الدالة الداخليةinnerFunction
۔innerFunction
تعرض قيمة متغير الدالة الخارجيةouterVariable
۔
استخدامات ومزايا الإغلاق
تغليف البيانات
فيما يلي مثال على الإغلاق مع توضيحات النوع لتغليف البيانات۔
1function createCounter(): () => number {
2 let count: number = 0;
3
4 return function (): number {
5 count += 1;
6 return count;
7 };
8}
9
10const counter: () => number = createCounter();
11console.log(counter()); // 1
12console.log(counter()); // 2
13console.log(counter()); // 3
- دالة
createCounter
تُعيد دالة تُعيد قيمة من النوعnumber
۔ - تم تعريف المتغير
count
كنوعnumber
ويتم التعامل معه داخل الإغلاق۔
وظائف ذات ترتيب أعلى
الإغلاق مفيد عند إنشاء وظائف ذات ترتيب أعلى۔ هنا، الدالة عالية الرتبة هي دالة تأخذ دالة أخرى كوسيط أو تُرجع دالة كنتيجة۔ فيما يلي مثال على وظيفة ذات ترتيب أعلى مع توضيحات النوع الواضحة۔
1function createMultiplier(multiplier: number): (value: number) => number {
2 return function (value: number): number {
3 return value * multiplier;
4 };
5}
6
7const double: (value: number) => number = createMultiplier(2);
8console.log(double(5)); // 10
9
10const triple: (value: number) => number = createMultiplier(3);
11console.log(triple(5)); // 15
createMultiplier
هي دالة عالية الرتبة تنشئ دالة لضرب العدد الذي تستقبله كوسيط۔- دالة الضرب الداخلية تأخذ أيضًا رقمًا وتُرجع نتيجة الضرب كرقم۔
مثال على تنفيذ الإغلاق في TypeScript
دعونا نلقي نظرة أيضًا على مثال لتنفيذ عداد يعتمد على نطاق كإغلاق۔
1function rangeCounter(min: number, max: number): () => number | string {
2 let count: number = min;
3
4 return function (): number | string {
5 if (count <= max) {
6 return count++;
7 } else {
8 return `Count has exceeded the maximum value: ${max}`;
9 }
10 };
11}
12
13const counter: () => number | string = rangeCounter(1, 5);
14
15console.log(counter()); // 1
16console.log(counter()); // 2
17console.log(counter()); // 3
18console.log(counter()); // 4
19console.log(counter()); // 5
20console.log(counter()); // "Count has exceeded the maximum value: 5"
- وظيفة
rangeCounter
تعيد وظيفة تعيد إماnumber
أوstring
۔ - في الدالة الداخلية، إذا تجاوز
count
القيمةmax
، فإنه يُرجع رسالة من نوعstring
؛ خلاف ذلك، يُرجع قيمة من نوعnumber
۔
الاحتياطات عند استخدام الإغلاقات
تسربات محتملة للذاكرة بسبب الإغلاقات
يمكن للإغلاقات الاحتفاظ بالمتغيرات من النطاق الخارجي، مما قد يتسبب أحيانًا في تسرب للذاكرة۔ يجب تحرير الإغلاقات غير الضرورية من الذاكرة بشكل صريح۔
1function createLeak(): () => void {
2 // Large array consuming significant memory
3 const largeArray: string[] = new Array(1000000).fill("leak");
4
5 // Closure capturing `largeArray`
6 return function (): void {
7 console.log(largeArray[0]); // Using the captured array
8 };
9}
10
11// Create a closure that holds a reference to the large array
12let leakyFunction = createLeak();
13
14// The large array is not released as `leakyFunction` still references it
15
16// When the object is no longer needed
17leakyFunction = null; // Explicitly remove the reference
- في هذا الكود، من المفترض أن يتم تحرير
largeArray
التي تم إنشاؤها داخلcreateLeak
عندما تخرج عن النطاق، ولكن هذا لا يحدث لأن الإغلاق يحتفظ بـlargeArray
.۔ طالما أنleakyFunction
موجودة، ستستمر هذه الذاكرة غير الضرورية في الاحتفاظ بها.۔ - عندما يصبح الكائن أو المتغير غير ضروري، فإن تعيين مرجعه إلى
null
يسمح لجامع القمامة باكتشافه وتحرير الذاكرة.۔
سوء استخدام الإغلاقات في الحلقات
عند إنشاء إغلاقات داخل حلقة، قد تكون هناك مشكلات في الإشارة إلى نفس المتغير۔ يُظهر المثال التالي حالة لا يتصرف فيها المتغير i
المُعلن باستخدام var
بشكل صحيح۔
1for (var i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 3, 3, 3
لا يقدم هذا الكود النتيجة المطلوبة لأن i
يشير إلى القيمة 3 في نهاية الحلقة۔ لإصلاح ذلك، يمكن استخدام let
لفصل النطاق أو استخدام دالة تُستدعى فورًا۔
1for (let i: number = 0; i < 3; i++) {
2 setTimeout((): void => {
3 console.log(i);
4 }, 1000);
5}
6// Output: 0, 1, 2
باستخدام let
، يتم فصل نطاق i
لكل دورة في الحلقة، مما يعطي النتائج المتوقعة۔
الملخص
في TypeScript، يمكن أن تؤدي الإغلاقات إلى كود أكثر أمانًا وقابلية للتوقع من خلال الاستفادة من نظام النوعية۔ الاستخدام الصحيح للإغلاقات يسمح بتغليف البيانات وتصميم مرن للدوال ذات الترتيب الأعلى۔ بالإضافة إلى ذلك، يجب توخي الحذر مع إدارة الذاكرة والإشارات غير المقصودة للنطاق عند استخدام الإغلاقات۔
يمكنك متابعة المقالة أعلاه باستخدام Visual Studio Code على قناتنا على YouTube.۔ يرجى التحقق من القناة على YouTube أيضًا.۔