تتيح لنا JavaScript التعامل مع أنواع البيانات الأولية (النصوص، الأرقام، وغيرها) كما لو أنها كائنات، كما تزودنا بتوابع (methods) لاستدعائها كما الكائنات. وسندرس هذه التوابع قريبًا، لكن لنرى أولًا كيف تعمل لأن الأنواع الأولية (primitives) ليست كائنات (أي object) وسنجعل الأمر واضحًا هنا.
لنرى إلى الفروق الأساسية بين الأنواع الأولية والكائنات.
النوع الأولي:
- هو قيمة من نوع أولي (primitive type).
-
يوجد 6 أنواع أولية: نص
string
، رقمnumber
، قيمة منطقيةboolean
، رمزsymbol
، قيمة فارغةnull
، وقيمة غير معرفةundefined
.
الكائن:
- قادر على تخزين العديد من القيم ضمن خاصيات.
-
يمكن إنشاؤه باستخدام
{}
، مثلًا:{name: "John", age: 30}
. يوجد أنواع أخرى من الكائنات في JavaScript: مثلا، تعد الدوال كائنات.
أحد أفضل الأشياء بالنسبة للكائنات هو إمكانية تخزين دالة في خاصية من خواص هذا الكائن.
let john = { name: "John", sayHi: function() { alert("Hi buddy!"); } }; john.sayHi(); // Hi buddy!
إذًا، أنشأنا الكائن john
محتويًا الدالة sayHi
.
يوجد العديد من الكائنات المدمجة في اللغة، مثل التي تتعامل مع التواريخ، الأخطاء، عناصر HTML، وغيرها. ولديها خاصيات وتوابع مختلفة. لكن لهذه الميزات ثمن!
الكائنات أثقل من المتغيرات الأولية، فهي تتطلب موارد (resources) أكثر لدعم آليتها الداخلية.
نوع أولي مثل كائن
هنا نجد التناقض الذي واجهه مُنشِئ JavaScript:
- يوجد الكثير من الأشياء التي قد يريد أحد القيام بها مع المتغيرات الأولية مثل النص أو الرقم. سيكون من الرائع الوصول إليها كتوابع.
- يجب أن تكون المتغيرات الأولية سريعة وخفيفة قدر الامكان.
يبدو الحل صعبًا قليلًا، لكن هذا هو:
- تبقى المتغيرات الأولية كما هي، قيمة واحدة مثل المطلوب.
- تتيح لنا اللغة الوصول إلى توابع وخاصيات النصوص، الأرقام، القيم المنطقية، والرموز.
- حتى يعمل ذلك، يُنشَأ «كائن مغلِّف» (object wrapper) خاص يزود المتغيرات بالوظائف الإضافية، ثم يُدَمَّر.
يختلف الكائن المغلِّف "object wrappers" من نوع أولي لآخر ويُسمَى: String
، و Number
، و Boolean
، و Symbol
، لذا فإنه يزود كل نوع بمجموعة مختلفة من التوابع الخاصة به.
مثلا، يوجد دالة للنصوص str.toUpperCase()
والتي تُرجِع النص str
بأحرف كبيرة.
آلية عملها:
let str = "Hello"; alert( str.toUpperCase() ); // HELLO
الأمر بسيط، أليس كذلك؟ ما يحدث فعلا في الدالة str.toUpperCase()
هو كالتالي:
1- النص str
هو متغير أولي، لذا فعند محاولة الوصول إلى خاصيتِه، يُنشَأ كائن خاص يعرف قيمة النص ويحتوي هذا الكائن على توابع مفيدة من بينها التابع toUpperCase()
.
2- تعمل هذه الدالة وتُرجِع نصًا جديدًا (يمكن عرضه باستخدام alert
).
3- يُدَمَّر الكائن الخاص تاركًا المتغير الأولي str
.
هكذا يمكن للمتغيرات الأولية أن تحوي توابعًا، وتبقى خفيفة في الوقت ذاته. يُحَسِّن محرك JavaScript هذه العملية بدرجة عالية. قد يتخطى إنشاء الكائن الإضافي، لكن يجب أن يظل قائمًا بالعمل المطلوب كما لو كان قد أنشأ الكائن.
لدى الأعداد توابع خاصة بها، مثلا، toFixed(n)
تُقرِّب الرقم المُعطَى إلى الدقة المطلوبة:
let n = 1.23456; alert( n.toFixed(2) ); // 1.23
سنرى المزيد من التوابع المخصصة في فصل النصوص والأعداد.
البانيات String
أو Number
أو Boolean
هي للاستخدام الداخلي فقط
تتيح لنا بعض اللغات مثل Java إنشاء كائنات مغلِّفة "wrapper objects" بشكل صريح باستخدام صيغة مثل: new Number(1)
أو new Boolean(false)
. ذلك ممكن أيضًا في JavaScript لأسباب تاريخية، لكنه غير مستحسن لأن الأمور قد تسير بشكل خاطئ في العديد من الأماكن.
مثلا:
alert( typeof 0 ); // "number" alert( typeof new Number(0) ); // "object"!
تكون قيمة الكائنات دائمًا true
في if
، لذا سيتم عرض ما بداخل alert
أدناه:
let zero = new Number(0); if (zero) { // alert( "zero is truthy!?!" ); }
تقييم قيمة المتغير zero
هنا هي القيمة المنطقية true
، لأنه كائن.
بالمقابل، من الممكن استخدام التوابع String/Number/Boolean
بدون new
، إذ تقوم هذه التوابع بتحويل القيمة إلى النوع المقابل: إلى نص، أو رقم، أو قيمة منطقية (أولية).
مثال: الأمر التالي صحيح تمامًا:
let num = Number("123"); // تحوِّل النص إلى رقم
ليس لدى القيمتان الأوليتان null/undefined توابعًا
النوعان الأوليان null
و undefined
هما حالة استثناء، فليس لديها كائن مغلِّف (wrapper object) ولا توابع، إذ يعدان من الأنواع الأكثر أولية.
ستتسبب المحاولة في الوصول إلى خاصية بظهور خطأ:
alert(null.test); // error
الخلاصة
-
لدى الأنواع الأولية توابع مساعدة عدا
null
وundefined
تسهل التعامل معها، سندرسها في الفصول اللاحقة. - تعمل هذه التوابع عبر كائنات مؤقتة، لكن محرك JavaScript مُعد لتحسين العملية داخليًا. لذا فإن استدعاء الكائن لا يتطلب الكثير من الموارد.
المهام
هل من الممكن إضافة خاصية نصية؟
الأهمية: 5
خذ بالحسبان الشيفرة التالية:
let str = "Hello"; str.test = 5; alert(str.test);
ماذا تظن؟ هل ستعمل؟ هل ستُعرَض؟
الحل
جرب تشغيلها:
let str = "Hello"; str.test = 5; // (*) alert(str.test);
يعتمد الأمر على إن كنت تستخدم use strict
أم لا، قد تكون النتيجة أحد الخيارين:
1- undefined
بدون استخدام الوضع الصارم 2- خطأ في الوضع الصارم
لماذا؟ لنفكر فيمَ يحصل في السطر (*)
:
1- يُنشئ "wrapper object" عند محاولة الوصول إلى خاصية للمتغير str
.
2- الكتابة إلى هذه الخاصية يُعَدُّ خطأ في الوضع الصارم.
3- في الحالة الأخرى، تستمر عملية التخزين في الخاصية، يحصل الكائن على الخاصية test
. لكن، يُدمَّر الكائن بعد ذلك فلا يصبح لدى str
مرجِعًا إليه مما يجعل قيمتها غير معرفة.
يوضح هذا المثال أن المتغيرات الأولية ليست كائنات.
ترجمة -وبتصرف- للفصل Methods of primitives من كتاب The JavaScript Language
اقرأ أيضًا
- المقال التالي: الأعداد
- المقال السابق: الباني والعامل "new"
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.