في JavaScript، قد تكون القيم من أنواع مختلفة. ويمكن تغيير نوع المتغير من خلال تحويل النوع الصريح. وإلى جانب ذلك، تُجري JavaScript أيضًا الإكراه النوعي (التحويل الضمني للنوع) عندما يتطلب السياق ذلك.
لا تملك JavaScript بنية لتحويل قيمة إلى نوع (مختلف) كما تفعل لغات أخرى كثيرة، لكن يمكن الاستعانة ببعض الأدوات المدمجة بدلًا من ذلك.
وأبرزها أن الكائنات العامة Boolean وNumber وString يمكن استخدامها كدوال لتحويل قيمة إلى النوع المقابل.
باستخدام Boolean(value) يمكنك تحويل أي قيمة إلى قيمة منطقية.
فكيف يحدث ذلك؟
هناك مجموعة ثابتة من القيم، تُسمّى القيم الخاطئة، تتحول إلى false.
وأهمها false و0 والسلسلة النصية الفارغة وnull وundefined وNaN، فهي كلها قيم خاطئة.
ويعرض مقال MDN عن "Falsy" القائمة الكاملة.
أما جميع القيم الأخرى، فتُرجع Boolean القيمة true.
وتُسمّى هذه القيم صحيحة.
Boolean(-1);
// => true
Boolean(0);
// => false
Boolean(' ');
// => true
Boolean('');
// => false
لاحظ أنه بفضل القواعد الموصوفة، فإن '0' و'false' و[] و{} تُعد صحيحة في JavaScript.
يمكن استخدام Number(value) لتحويل قيمة إلى عدد.
تُتجاهل المسافات البيضاء في بداية السلسلة النصية ونهايتها، وتُحوَّل السلسلة النصية الفارغة إلى 0.
وإذا حاولت تحويل قيمة غير أولية أو سلسلة نصية لا تمثّل عددًا، فلن يُطرح أي خطأ.
بل ستكون النتيجة NaN (ليس عددًا).
Number(' -12.34 ');
// => -12.34
Number('1,2');
// => NaN
Number('1 2');
// => NaN
Number('');
// => 0
Number('10e3');
// => 10000
Number({ key: '123' });
// => NaN
وفي الأسفل ترى ما تُرجعه Number لبقية القيم الأولية.
Number(true);
// => 1
Number(false);
// => 0
Number(null);
// => 0
Number(undefined);
// => NaN
لاحظ أنه بخلاف المثال الأخير، فإن استدعاء Number() دون أي وسيط مُعرَّف بأنه يُرجع 0.
وتوفّر JavaScript أيضًا الدالتين parseInt وparseFloat.
وهما تطبّقان قواعد أكثر تسامحًا فيما يخص السلاسل النصية التي يمكن تحويلها إلى عدد.
ولهذا يُفضَّل استخدام Number كدالة تحويل لتجنّب نتائج غير متوقعة.
parseInt('123a45');
// => 123
Number('123a45');
// => NaN
باستخدام String(value) يمكنك تحويل قيمة إلى سلسلة نصية.
وتكون النتيجة كما تتوقعها بالنسبة إلى القيم الأولية.
String(12.34);
// => '12.34'
String(false);
// => 'false'
String(null);
// => 'null'
String(undefined);
// => 'undefined'
أما المصفوفات، فستُطبّق دالة String تحويل السلسلة النصية على كل عنصر ثم تدمج النتائج بفاصلة.
ويمكنك أيضًا استخدام طريقة join بنفسك، مثلًا لتخصيص الفاصل.
غير أنه في هذه الحالات تُحوَّل null وundefined إلى سلسلة نصية فارغة.
String([42, null, true, 'abc']);
// => '42,,true,abc'
أما الكائنات، فتُرجع String افتراضيًا نصًا غير مفيد.
String({ key: 'value' });
// => '[object Object]'
ويمكنك تخصيص سلوك التحويل، مثلًا بتوفير طريقة toString.
ويشرح قسم "تحويل الكائن إلى قيمة أولية" على javascript.info التفاصيل.
وثمة طريقة شائعة أخرى للحصول على تمثيل نصي أفضل للكائنات والمصفوفات، وهي استخدام ترميز JSON.
فيمكنك التحويل إلى سلسلة نصية عبر JSON.stringify، والعودة إلى كائن أو مصفوفة عبر JSON.parse.
const obj = {
name: 'Gilda Guerra',
address: {
city: 'Goiânia',
},
};
JSON.stringify(obj);
// => '{"name":"Gilda Guerra","address":{"city":"Goiânia"}}'
في سياقات معينة، تُحوّل JavaScript تلقائيًا قيمة إلى نوع بيانات آخر قبل أن تُقيّم عبارة ما. ويُسمّى هذا التحويل الضمني الإكراه النوعي.
عندما تُستخدم قيمة في سياق منطقي، تُطبّق JavaScript القواعد نفسها التي تطبّقها دالة Boolean لتحويل القيمة ضمنيًا.
عندما لا يكون شرط الجملة الشرطية قيمة منطقية، يُطبَّق الإكراه لتحديد ما إذا كان الشرط قد تحقق أم لا.
وينطبق الأمر نفسه على الطرف الأول من العامل الثلاثي ?.
const num = 0;
if (num) {
// this block is NOT executed because 0 is falsy
}
const name = 'Jin';
name ? 'name was provided' : 'no name provided';
// => 'name was provided'
كما يُحوَّل طرف عامل النفي المنطقي ! إلى قيمة منطقية قبل تطبيق عملية النفي.
const name = '';
!name;
// => true
ومن نتائج السلوك الموصوف أن !!value لها الأثر نفسه الذي ينتج عن Boolean(value).
ومع ذلك، يُستحسن استخدام Boolean لوضوح القراءة.
وتطبّق JavaScript أيضًا الإكراه على طرفَي العاملين المنطقيين AND (&&) وOR (||).
لكن تذكّر أن ناتج التعبير ليس بالضرورة قيمة منطقية.
بل يُرجع أحد الطرفين الأصليين (انظر توثيق MDN للعوامل المنطقية).
null || 'hello';
// => 'hello'
إذا استُخدم عامل الجمع + مع قيم أولية وكان أحد الطرفين سلسلة نصية، فسيُحوَّل الطرف الآخر إلى سلسلة نصية أيضًا.
ومنطق التحويل هو نفسه المستخدم في دالة String.
بعد ذلك تُدمج السلسلتان النصيتان.
let name;
'hello ' + name;
// => 'hello undefined'
ويحدث التحويل الضمني نفسه للقيم غير النصية التي تُضمَّن في القوالب النصية.
const degrees = 23;
`It is ${degrees} °C`;
// => 'Is is 23 °C.';
تحوّل عوامل كثيرة أطرافها إلى أعداد (عند الحاجة) وفق منطق دالة Number الموضّح أعلاه.
+ (إذا لم تكن هناك سلسلة نصية)، و-، و*، و/، و%، و**
+، -
>، >=، <، <=
|، &، ^، ~
راجع قائمة العوامل في MDN لمزيد من التفاصيل عن أي منها.
عندما يكون أحد الأطراف سلسلة نصية محتملة، فالأفضل دائمًا التحويل صراحةً بدالة Number لتجنّب الأخطاء.
'1' + '2';
// => '12'
// addition operator in string context as explained above
Number('1') + Number('2');
// => 3
أحيانًا سترى عامل الجمع الأحادي يُستخدم لإكراه سلسلة نصية لتصبح عددًا.
ولا يُنصَح بهذا، لأنه أصعب في القراءة بكثير من استدعاء Number الصريح.
const value = '42';
+value;
// => 42
Number(value);
// => 42
كذلك غالبًا ما يتضمن استخدام عاملي المساواة وعدم المساواة غير الصارمتين ==/!= تحويلًا ضمنيًا إلى عدد.
غير أن المنطق الدقيق لهذين العاملين معقّد نوعًا ما (انظر توثيق MDN للمساواة غير الصارمة).
ونتابجهما صعبة التوقع، وأحيانًا ليست ما تتوقعه.
فاستخدم بدلًا منهما عاملي المساواة وعدم المساواة الصارمتين ===/!== لتجنّب هذه التحويلات الضمنية.