الإغلاق في TypeScript

الإغلاق في 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 هو () => void، مما يشير إلى أنه يعيد وظيفة۔
  • تم تحديد نوع innerFunction صراحة إلى void، مما يشير إلى أنه لا يحتوي على قيمة إرجاع۔

استخدامات ومزايا الإغلاق

تغليف البيانات

فيما يلي مثال على الإغلاق مع توضيحات النوع لتغليف البيانات۔

 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 تأخذ معاملًا من النوع number وتعيد وظيفة من النوع (value: number) => number۔
  • الوظيفة الداخلية أيضًا تقبل value كنوع number وتعيد النتيجة كنوع number۔

مثال على تنفيذ الإغلاق في 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 بشكل صحيح۔

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 أيضًا.۔

YouTube Video