تحويل الأنواع في JavaScript
تحويل الأنواع هو الجزء الأكثر عرضة للأخطاء في JavaScript. رقم مكتوب في نموذج؟ оказار أنه سلسلة! "5" + 3 يُعطي "53" بدلاً من 8 — كل مبتدئ يصادف هذه "المفاجآت".
📖 ملخص
لماذا تحويل الأنواع مهم
قيم input في نماذج HTML هي دائماً سلاسل، حتى مع type="number". يجب تحويل الأنواع قبل القيام بالعمليات الحسابية، وإلا ستكون النتائج غير متوقعة.
<div id="demo"></div>
<script>
const input = "42";
const result = input + 8;
document.getElementById("demo").textContent = 'input + 8 = ' + result + ' (النوع: ' + typeof result + ')';
</script>
التحويل الصريح: String()، Number()، Boolean()
استدعاء وظائف التحويل صراحةً يجعل نيتك واضحة — هذا هو النهج الموصى به:
| التحويل | الصيغة | مثال |
|---|---|---|
| إلى سلسلة | String(value) |
String(123) → "123" |
| إلى رقم | Number(value) |
Number("42") → 42 |
| إلى منطقي | Boolean(value) |
Boolean(0) → false |
التحويل الضمني: ربط الـ + ومقارنة الـ ==
محرك JavaScript ي执行 تحويل نوع تلقائي غالباً ما يُنتج نتائج غير متوقعة:
- عامل
+: إذا كان أي طرف سلسلة، يتحول الآخر إلى سلسلة للربط - مقارنة
==: تconvert الأنواع قبل المقارنة، بقواعد معقدة يصعب تذكرها
<script>
console.log("5" + 3); // "53" (الرقم 3 تحول إلى سلسلة)
console.log("5" - 3); // 2 (السلسلة "5" تحولت إلى رقم)
console.log("" == 0); // true (السلسلة الفارغة تحولت إلى 0)
console.log(null == undefined); // true
</script>
مشكلات شائعة
هذه مشكلات عالية التكرار في المقابلات والتطوير الحقيقي:
<script>
console.log("5" + 3); // "53" + تربط عندما ترى سلسلة
console.log("5" - 3); // 2 - تقوم فقط بالطرح، تconvert السلسلة إلى رقم
console.log("5" * 3); // 15 * أيضاً تconvert إلى رقم
console.log(true + 1); // 2 true تconvert إلى 1
console.log(false + 1); // 1 false تconvert إلى 0
console.log("" + 0); // "0" الرقم يconvert إلى سلسلة
console.log("" == 0); // true كلا الجانبين يconvertان إلى أرقام
console.log(null == 0); // false null يساوي فقط undefined بشكل فضفاض
console.log("0" == false); // true كلاهما يconvert إلى 0
</script>
تذكر هذه القاعدة: + تربط عندما تصادف سلسلة؛ جميع عوامل الحساب الأخرى convert السلاسل إلى أرقام.
قواعد تحويل Number()
| المدخل | النتيجة |
|---|---|
Number("42") |
42 |
Number("3.14") |
3.14 |
Number("") |
0 (سلسلة فارغة → 0، مشكلة كلاسيكية) |
Number(" ") |
0 (سلسلة مسافات فقط → 0) |
Number("42px") |
NaN (تحتوي أحرف غير رقمية → NaN) |
Number(true) |
1 |
Number(false) |
0 |
Number(null) |
0 |
Number(undefined) |
NaN |
السلسلة الفارغة تconvert إلى 0 بدلاً من NaN — هذا التصميم أوقع العديد من المطورين.
قواعد تحويل Boolean() (القيم Falsy)
هذه الـ 6 قيم convert إلى false وتسمى قيم falsy. كل شيء آخر هو true:
<script>
console.log(Boolean(false)); // false
console.log(Boolean(0)); // false
console.log(Boolean(-0)); // false
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
</script>
ملاحظة: Boolean("0") هو true، و Boolean([]) هو أيضاً true — السلاسل غير الفارغة والمصفوفات غير الفارغة هي truthy.
=== المساواة الصارمة مقابل == المساواة الفضفاضة
| العامل | السلوك | التوصية |
|---|---|---|
=== |
يُرجع false إذا اختلفت الأنواع | استخدم دائماً |
== |
يconvert الأنواع قبل المقارنة | تجنب |
<script>
console.log(5 === "5"); // false (أنواع مختلفة)
console.log(5 == "5"); // true (السلسلة convertت إلى رقم أولاً)
console.log(null === undefined); // false
console.log(null == undefined); // true
</script>
القاعدة الحديدية: استخدم دائماً ===، ولا تستخدم == أبداً. إلا إذا كنت تعرف بالضبط ما تفعله (مثلاً value == null يمكنها مطابقة كل من null و undefined).
مثال: مقارنة تحويل الأنواع
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>تحويل الأنواع</title>
<style>
body { font-family: sans-serif; padding: 20px; }
table { border-collapse: collapse; margin: 16px 0; width: 100%; max-width: 700px; direction: ltr; }
td, th { border: 1px solid #ddd; padding: 10px 14px; text-align: left; }
th { background: #4a90d9; color: #fff; }
.surprise { color: #d9534f; font-weight: bold; }
.normal { color: #5cb85c; }
tr:nth-child(even) { background: #f9f9f9; }
</style>
</head>
<body>
<h2>نتائج التحويل الضمني في لمحة</h2>
<div id="output"></div>
<script>
const tests = [
['"5" + 3', "5" + 3, '"53"', "ربط سلسلة", true],
['"5" - 3', "5" - 3, "2", "سلسلة إلى رقم", false],
['"5" * 3', "5" * 3, "15", "سلسلة إلى رقم", false],
['true + 1', true + 1, "2", "true→1", false],
['false + 1', false + 1, "1", "false→0", false],
['"" + 0', "" + 0, '"0"', "رقم إلى سلسلة", true],
['"" == 0', "" == 0, "true", "كلا الجانبين convertان إلى أرقام", true],
['"0" == false', "0" == false, "true", "كلاهما convert إلى 0", true],
['null == 0', null == 0, "false", "null لا يساوي 0", false],
['5 === "5"', 5 === "5", "false", "أنواع مختلفة", false],
];
document.getElementById("output").innerHTML = `
<table>
<tr><th>التعبير</th><th>النتيجة الفعلية</th><th>النوع</th><th>التوضيح</th><th>م counter-intuitive?</th></tr>
${tests.map(t => `<tr>
<td><code>${t[0]}</code></td>
<td class="${t[4] ? 'surprise' : 'normal'}">${t[2]}</td>
<td>${typeof t[1]}</td>
<td>${t[3]}</td>
<td>${t[4] ? "نعم" : "لا"}</td>
</tr>`).join("")}
</table>
`;
</script>
</body>
</html>
مثال: مرجع سريع لقيم Boolean() Falsy
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>تحويل Boolean</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; margin: 16px 0; }
.card { padding: 16px; border-radius: 8px; text-align: center; font-weight: bold; }
.falsy { background: #fff0f0; border: 2px solid #d9534f; color: #d9534f; }
.truthy { background: #f0fff0; border: 2px solid #5cb85c; color: #5cb85c; }
.card code { display: block; font-size: 18px; margin-bottom: 4px; direction: ltr; }
.card span { font-size: 13px; opacity: 0.8; }
.note { background: #fff8e1; padding: 12px; border-radius: 6px; border-right: 4px solid #f0ad4e; margin: 16px 0; }
</style>
</head>
<body>
<h2>نتائج تحويل Boolean()</h2>
<div id="output"></div>
<script>
const values = [
[false, "false"],
[0, "0"],
["", '""'],
[null, "null"],
[undefined, "undefined"],
[NaN, "NaN"],
[true, "true"],
[1, "1"],
["0", '"0"'],
["false", '"false"'],
[[], "[]"],
[{}], "{}"
];
const cards = values.map(([val, label]) => {
const result = Boolean(val);
const display = label || String(val);
return `<div class="card ${result ? 'truthy' : 'falsy'}">
<code>${display}</code>
<span>${result}</span>
</div>`;
});
document.getElementById("output").innerHTML = `
<div class="grid">${cards.join("")}</div>
<div class="note">
<code>"0"</code> هو truthy! <code>[]</code> هو أيضاً truthy! فقط تلك القيم الـ 7 falsy convert إلى false.
</div>
`;
</script>
</body>
</html>
مثال: حساب مدخلات النموذج
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>حساب النموذج</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.calc { max-width: 400px; margin: 16px auto; padding: 20px; background: #f9f9f9; border-radius: 8px; }
.row { margin: 12px 0; }
label { display: inline-block; width: 80px; }
input { padding: 8px 12px; font-size: 16px; border: 2px solid #ccc; border-radius: 6px; width: 120px; }
button { padding: 10px 24px; font-size: 16px; border: none; border-radius: 6px; cursor: pointer; background: #4a90d9; color: #fff; margin-top: 8px; }
button:hover { background: #357abd; }
.result { margin-top: 16px; padding: 12px; border-radius: 6px; font-size: 18px; font-weight: bold; }
.right { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.wrong { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
</style>
</head>
<body>
<h2 style="text-align:center;">حساب مدخلات النموذج (تحويل الأنواع عملياً)</h2>
<div class="calc">
<div class="row">
<label>السعر:</label>
<input type="number" id="price" value="12.5" />
</div>
<div class="row">
<label>الكمية:</label>
<input type="number" id="qty" value="3" />
</div>
<div style="text-align:center;">
<button id="calcWrong">احسب بدون تحويل (خطأ)</button>
<button id="calcRight" style="background:#5cb85c;">احسب باستخدام Number()</button>
</div>
<div class="result" id="result"></div>
</div>
<script>
document.getElementById("calcWrong").addEventListener("click", function() {
const price = document.getElementById("price").value;
const qty = document.getElementById("qty").value;
const total = price * qty;
document.getElementById("result").className = "result wrong";
document.getElementById("result").innerHTML =
`بدون تحويل: "${price}" * "${qty}" = ${total}<br>` +
`(${typeof price} * ${typeof qty} = ${typeof total})<br>` +
`يعمل بالحظ لأن * convert ضمنياً، لكن + ستفشل!`;
});
document.getElementById("calcRight").addEventListener("click", function() {
const price = Number(document.getElementById("price").value);
const qty = Number(document.getElementById("qty").value);
if (isNaN(price) || isNaN(qty)) {
document.getElementById("result").className = "result wrong";
document.getElementById("result").textContent = "إدخال غير صالح!";
return;
}
const total = price * qty;
document.getElementById("result").className = "result right";
document.getElementById("result").innerHTML =
`مع التحويل الصريح: ${price} × ${qty} = ${total.toFixed(2)}<br>` +
`(${typeof price} × ${typeof qty} = ${typeof total})`;
});
</script>
</body>
</html>
مثال: مقارنة === مقابل ==
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>المساواة الصارمة مقابل الفضفاضة</title>
<style>
body { font-family: sans-serif; padding: 20px; }
table { border-collapse: collapse; margin: 16px 0; width: 100%; max-width: 600px; direction: ltr; }
td, th { border: 1px solid #ddd; padding: 10px 14px; text-align: center; }
th { background: #4a90d9; color: #fff; }
.same { background: #d4edda; }
.diff { background: #f8d7da; }
.recommend { background: #fff8e1; padding: 16px; border-radius: 8px; border-right: 4px solid #f0ad4e; margin: 16px 0; max-width: 600px; }
</style>
</head>
<body>
<h2>=== المساواة الصارمة مقابل == المساواة الفضفاضة</h2>
<div id="output"></div>
<script>
const comparisons = [
["5", 5],
[0, false],
[0, ""],
["0", false],
[null, undefined],
[null, 0],
[null, false],
[NaN, NaN],
];
const rows = comparisons.map(([a, b]) => {
const loose = a == b;
const strict = a === b;
const aStr = JSON.stringify(a);
const bStr = JSON.stringify(b);
return `<tr>
<td><code>${aStr}</code> vs <code>${bStr}</code></td>
<td class="${loose ? 'same' : 'diff'}">${loose}</td>
<td class="${strict ? 'same' : 'diff'}">${strict}</td>
</tr>`;
});
document.getElementById("output").innerHTML = `
<table>
<tr><th>المقارنة</th><th>== نتيجة</th><th>=== نتيجة</th></tr>
${rows.join("")}
</table>
<div class="recommend">
<strong>القاعدة الحديدية</strong>: استخدم دائماً <code>===</code>. قواعد تحويل <code>==</code> معقدة جداً — باستثناء الحالة الخاصة <code>value == null</code> (التي تطابق كل من null و undefined)، لا تستخدمها.
</div>
`;
</script>
</body>
</html>
❓ أسئلة شائعة
"5" - 3 يساوي 2 بدلاً من إلقاء خطأ؟- يقوم فقط بالعمليات الحسابية، لذا JavaScript convert السلسلة إلى رقم تلقائياً. فقط عامل + مبهم (يمكن أن يكون جمعاً أو ربطاً)، لذا يربط عندما يرى سلسلة. جميع العوامل الأخرى (-، *، /، %) convert دائماً إلى أرقام.Number("") تُرجع 0 بدلاً من NaN؟Number(input.value) تُرجع 0 للحقول الفارغة بدلاً من NaN، مما قد يضلل المنطق اللاحق. استخدم input.value.trim() === "" للتحقق من القيم الفارغة أولاً.null == 0 هو false؟==، null convert فقط إلى/من undefined — لا convert إلى أرقام. لذا null == 0 هو false، لكن null == undefined هو true. هذه حالة خاصة يحددها المعيار.📝 تمارين
- بدون تشغيل الكود، توقع نتائج هذه التعبيرات:
"10" + 5،"10" - 5،"10" * "2"،true + true،Boolean("false")،"1" == 1،"1" === 1. ثم اكتب كوداً للتحقق. - اكتب دالة
calculateBMI(weight, height)حيث المعاملات تأتي من مدخلات النماذج (سلاسل). يجب أن convert الأنواع داخلياً قبل حساب BMI، وتتعامل مع المدخلات الفارغة أو غير الصالحة (تُرجع رسالة خطأ). - اكتب دالة
strictCompare(a, b)تستخدم فقط===للمقارنة. إذا اختلفت الأنواع، تُرجع مباشرة"أنواع مختلفة، لا يمكن المقارنة"؛ إذا تطابقت الأنواع، تُرجع نتيجة المقارنة.



