404 Not Found

404 Not Found


nginx

تحويل الأنواع في JavaScript

تحويل الأنواع هو الجزء الأكثر عرضة للأخطاء في JavaScript. رقم مكتوب في نموذج؟ оказار أنه سلسلة! "5" + 3 يُعطي "53" بدلاً من 8 — كل مبتدئ يصادف هذه "المفاجآت".

📖 ملخص

لماذا تحويل الأنواع مهم

قيم input في نماذج HTML هي دائماً سلاسل، حتى مع type="number". يجب تحويل الأنواع قبل القيام بالعمليات الحسابية، وإلا ستكون النتائج غير متوقعة.

HTML
<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 ي执行 تحويل نوع تلقائي غالباً ما يُنتج نتائج غير متوقعة:

HTML
<script>
console.log("5" + 3);       // "53" (الرقم 3 تحول إلى سلسلة)
console.log("5" - 3);       // 2 (السلسلة "5" تحولت إلى رقم)
console.log("" == 0);       // true (السلسلة الفارغة تحولت إلى 0)
console.log(null == undefined); // true
</script>

مشكلات شائعة

هذه مشكلات عالية التكرار في المقابلات والتطوير الحقيقي:

HTML
<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:

HTML
<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 الأنواع قبل المقارنة تجنب
HTML
<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).


مثال: مقارنة تحويل الأنواع

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: 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

HTML
<!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>
▶ جرّب الكود

مثال: حساب مدخلات النموذج

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>
▶ جرّب الكود

مثال: مقارنة === مقابل ==

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؟
ج هذا خيار تصميم تاريخي. السلسلة الفارغة تعتبر "بلا رقم"، ما يعادل 0.这意味着 Number(input.value) تُرجع 0 للحقول الفارغة بدلاً من NaN، مما قد يضلل المنطق اللاحق. استخدم input.value.trim() === "" للتحقق من القيم الفارغة أولاً.
س لماذا null == 0 هو false؟
ج في مقارنات ==، null convert فقط إلى/من undefined — لا convert إلى أرقام. لذا null == 0 هو false، لكن null == undefined هو true. هذه حالة خاصة يحددها المعيار.

📝 تمارين

  1. بدون تشغيل الكود، توقع نتائج هذه التعبيرات: "10" + 5، "10" - 5، "10" * "2"، true + true، Boolean("false")، "1" == 1، "1" === 1. ثم اكتب كوداً للتحقق.
  2. اكتب دالة calculateBMI(weight, height) حيث المعاملات تأتي من مدخلات النماذج (سلاسل). يجب أن convert الأنواع داخلياً قبل حساب BMI، وتتعامل مع المدخلات الفارغة أو غير الصالحة (تُرجع رسالة خطأ).
  3. اكتب دالة strictCompare(a, b) تستخدم فقط === للمقارنة. إذا اختلفت الأنواع، تُرجع مباشرة "أنواع مختلفة، لا يمكن المقارنة"؛ إذا تطابقت الأنواع، تُرجع نتيجة المقارنة.
100%