String vs string في TypeScript: أيهما تختار للسرعة؟

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

String vs string: القاتل الصامت لأداء كودك

\n\n

هل تساءلت يومًا لماذا في مكان ما يشتكي TypeScript من String، وفي مكان آخر من string؟ أو ربما رأيت مطورين متمرسين يتجهمون عند رؤية new String("hello")؟ هذا السؤال هو نجم حقيقي على Stack Overflow، حاصدًا آلاف المشاهدات. وليس من دون سبب. الفرق بين String و string ليس مجرد نزوة لغوية، بل هو اختلاف جوهري يؤثر على أداء وأمان وقابلية قراءة كودك.

\n\n

في هذه المقالة، لن نقدم مجرد تعريف جاف. سنحلل ما يكمن وراء كل من هذه الأنواع، وكيف تتصرف في وقت التشغيل، ولماذا الاختيار الصحيح يمكن أن يسرّع تطبيقك بعشرات النسبة المئوية. استعدوا، سيكون الأمر ممتعًا!

\n\n

البدائيات مقابل الكائنات: أساسيات الأساسيات

\n\n

لفهم الفرق، علينا أن نتذكر كيف يعمل JavaScript (وبالتالي TypeScript) مع البيانات. هناك نوعان من القيم في اللغة:

\n\n
    \n
  • البدائيات (primitives): string، number، boolean، null، undefined، symbol، bigint. وهي غير قابلة للتغيير وتُخزَّن بالقيمة.
  • \n
  • الكائنات (objects): Object، Array، Function، وكذلك String (بحرف كبير)، Number، Boolean. وهي قابلة للتغيير وتُخزَّن بالمرجع.
  • \n
\n\n

عندما تكتب let name: string = "Alice"، فإنك تنشئ بدائيًا. وعندما تكتب let name: String = new String("Alice")، فإنك تنشئ كائنًا غلافًا. وهنا يبدأ الجزء المثير للاهتمام.

\n\n

النوع string (بحرف صغير)

\n

هذا نوع بدائي. يُستخدم في 99.9% من الحالات. إنه خفيف الوزن، سريع، ويمكن التنبؤ به. مقارنة بدائيين تتم حسب القيمة:

\n\n
let a: string = "hello";\nlet b: string = "hello";\nconsole.log(a === b); // true
\n\n

يوصي TypeScript باستخدامه تحديدًا. إنه المعيار الفعلي للإعلان عن المتغيرات النصية، ومعاملات الدوال، وقيم الإرجاع.

\n\n

النوع String (بحرف كبير)

\n

هذا نوع كائني، مُنشئ غلاف. إنه موجود منذ أيام JavaScript للعمل مع دوال النصوص. ومع ذلك، فإن استخدامه المباشر هو نمط مضاد إلى حد كبير، إلا إذا كانت لديك مهمة محددة جدًا. مقارنة كائنين من String تتم حسب المرجع:

\n\n
let a: String = new String("hello");\nlet b: String = new String("hello");\nconsole.log(a === b); // false! هذان كائنان مختلفان في الذاكرة
\n\n

هذا يمكن أن يؤدي إلى أخطاء يصعب تتبعها. تخيل أنك تتحقق مما إذا كانت قيمة النص تساوي "hello"، وتحصل على false فقط لأن شخصًا ما استخدم new String().

\n\n

الأداء: لماذا يفوز string؟

\n\n

الآن ننتقل إلى الجزء الأكثر إثارة — الأرقام والتحسين. دعنا نكتب اختبار أداء بسيط (يمكنك تشغيله في بيئتك، مثل Node.js أو المتصفح).

\n\n
// اختبار الأداء\nconst iterations = 1_000_000;\n\nconsole.time("string (primitive)");\nlet sum = "";\nfor (let i = 0; i < iterations; i++) {\n    sum += "a";\n}\nconsole.timeEnd("string (primitive)");\n\nconsole.time("String (object)");\nlet sumObj = new String("");\nfor (let i = 0; i < iterations; i++) {\n    sumObj += "a"; // هنا يحدث التغليف التلقائي وإلغاء التغليف\n}\nconsole.timeEnd("String (object)");
\n\n

ستكون النتيجة على جهاز نموذجي مختلفة بشكل كبير (لصالح البدائي بمقدار 2-5 مرات). لماذا؟ لأن:

\n
    \n
  • البدائي string هو مجرد سلسلة من الأحرف في الذاكرة. العمليات عليه مُحسَّنة على مستوى المحرك.
  • \n

المدونات

توصيات الكتب