404 Not Found

404 Not Found


nginx

طرق الأدوات المساعدة في JavaScript

غطينا تحويلات الأنواع — الآن لنتعلم مجموعة من طرق الأدوات المساعدة التي ستستخدمها يومياً. فكّر بها كسكين سويسري — كل واحدة بسيطة، لكنك ستفتقد أي واحدة منها عندما تحتاجها.

📖 ملخص

parseInt و parseFloat

تحليل الأرقام من بداية السلسلة، والتوقف عند أول حرف غير رقمي:

HTML
<script>
console.log(parseInt("42px"));      // 42
console.log(parseInt("3.14"));      // 3 (الجزء الصحيح فقط)
console.log(parseFloat("3.14"));    // 3.14
console.log(parseFloat("3.14abc")); // 3.14
console.log(parseInt("abc42"));     // NaN (لا يبدأ برقم)
</script>

المعامل الثاني لـ parseInt يحدد الأساس:

HTML
<script>
console.log(parseInt("FF", 16));  // 255 (ست عشري)
console.log(parseInt("10", 2));   // 2 (ثنائي)
</script>

مشكلة: بدون أساس، السلاسل التي تبدأ بـ 0x تُحلل كست عشرية. مرر دائماً 10 كمعامل ثاني.

isNaN مقابل Number.isNaN

الطريقة السلوك
isNaN(value) convert إلى رقم أولاً، ثم يتحقق ما إذا كان NaN
Number.isNaN(value) يتحقق صارماً ما إذا كانت القيمة NaN (بدون تحويل)
HTML
<script>
console.log(isNaN("hello"));         // true (Number("hello") → NaN، ثم true)
console.log(Number.isNaN("hello"));  // false ("hello" نفسها ليست NaN)
console.log(isNaN(undefined));       // true
console.log(Number.isNaN(undefined));// false
</script>

الخلاصة: استخدم Number.isNaN() — لن يُخفي تحويل الأنواع.

toFixed

يتحكم في المنازل العشرية، يُرجع سلسلة:

HTML
<script>
console.log((3.14159).toFixed(2));  // "3.14"
console.log((3.1).toFixed(4));      // "3.1000" (م padding بأصفار)
console.log((3.145).toFixed(2));    // "3.14" أو "3.15" (مشكلة دقة الفاصلة المتحولة، التقريب غير مضمون)
</script>

لاحظ أن toFixed يُرجع سلسلة — استخدم Number() لإعادتها للحسابات.

toString

يconvert رقم إلى سلسلة، مع دعم اختياري للأساس:

HTML
<script>
console.log((255).toString());      // "255"
console.log((255).toString(16));    // "ff"
console.log((8).toString(2));       // "1000"
console.log((100).toString(8));     // "144"
</script>

القيم المنطقية والمصفوفات لديها أيضاً toString:

HTML
<script>
console.log(true.toString());       // "true"
console.log([1,2,3].toString());    // "1,2,3"
</script>

Number.isInteger

يتحقق ما إذا كانت القيمة عدد صحيح:

HTML
<script>
console.log(Number.isInteger(5));      // true
console.log(Number.isInteger(5.0));    // true (5.0 هو فقط 5)
console.log(Number.isInteger(5.5));    // false
console.log(Number.isInteger("5"));    // false (سلسلة ليست عدداً صحيحاً)
console.log(Number.isInteger(NaN));    // false
</script>

Math.trunc و Math.sign

Math.trunc ببساطة يقطع الجزء العشري (بدون تقريب):

HTML
<script>
console.log(Math.trunc(4.9));    // 4
console.log(Math.trunc(-4.9));   // -4
console.log(Math.trunc(4.1));    // 4
</script>

Math.sign يُرجع إشارة الرقم:

HTML
<script>
console.log(Math.sign(5));       // 1
console.log(Math.sign(-5));      // -1
console.log(Math.sign(0));       // 0
console.log(Math.sign(-0));      // -0
console.log(Math.sign(NaN));     // NaN
</script>

قيود typeof

typeof عادةً يعمل للتحقق من النوع، لكن هناك بعض المشكلات الكلاسيكية:

HTML
<script>
console.log(typeof 42);           // "number"  ✅
console.log(typeof "hello");      // "string"  ✅
console.log(typeof true);         // "boolean" ✅
console.log(typeof undefined);    // "undefined" ✅
console.log(typeof null);         // "object"  ❌ هذا خطأ تاريخي!
console.log(typeof []);           // "object"  ❌ المصفوفات أيضاً كائنات
console.log(typeof {});           // "object"  ✅
console.log(typeof function(){}); // "function" ✅
</script>

typeof null === "object" هو "خطأ قديم" في JavaScript — موجود منذ 1995 ولا يمكن إصلاحه. استخدم value === null للتحقق من null. استخدم Array.isArray() للتحقق من المصفوفات.


مثال: حاسبة الأسعار

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: 440px; margin: 16px auto; padding: 24px; background: #f9f9f9; border-radius: 12px; }
    .row { margin: 12px 0; display: flex; align-items: center; gap: 10px; }
    label { width: 80px; font-weight: bold; }
    input { padding: 8px 12px; font-size: 16px; border: 2px solid #ccc; border-radius: 6px; flex: 1; }
    button { padding: 10px 24px; font-size: 16px; border: none; border-radius: 6px; cursor: pointer; background: #4a90d9; color: #fff; width: 100%; margin-top: 8px; }
    button:hover { background: #357abd; }
    .result { margin-top: 16px; padding: 16px; background: #fff; border-radius: 8px; border: 2px solid #5cb85c; }
    .result div { margin: 4px 0; }
    .total { font-size: 22px; font-weight: bold; color: #d9534f; }
  </style>
</head>
<body>
  <h2 style="text-align:center;">حاسبة الأسعار</h2>
  <div class="calc">
    <div class="row">
      <label>سعر الوحدة:</label>
      <input type="text" id="price" value="29.90" placeholder="مثال: 29.90" />
    </div>
    <div class="row">
      <label>الكمية:</label>
      <input type="text" id="qty" value="3" placeholder="مثال: 3" />
    </div>
    <div class="row">
      <label>الخصم:</label>
      <input type="text" id="discount" value="0.85" placeholder="مثال: 0.85 (خصم 15%)" />
    </div>
    <button id="calc">احسب</button>
    <div class="result" id="result" style="display:none;"></div>
  </div>
  <script>
    document.getElementById("calc").addEventListener("click", function() {
      const priceStr = document.getElementById("price").value.trim();
      const qtyStr = document.getElementById("qty").value.trim();
      const discountStr = document.getElementById("discount").value.trim();

      const price = parseFloat(priceStr);
      const qty = parseInt(qtyStr, 10);
      const discount = parseFloat(discountStr);

      if (Number.isNaN(price) || price <= 0) {
        alert("سعر وحدة غير صالح! الرجاء إدخال رقم موجب.");
        return;
      }
      if (Number.isNaN(qty) || qty <= 0 || !Number.isInteger(qty)) {
        alert("كمية غير صالحة! الرجاء إدخال عدد صحيح موجب.");
        return;
      }
      if (Number.isNaN(discount) || discount <= 0 || discount > 1) {
        alert("خصم غير صالح! الرجاء إدخال عدد عشري بين 0 و 1.");
        return;
      }

      const subtotal = price * qty;
      const total = subtotal * discount;
      const saved = subtotal - total;

      document.getElementById("result").style.display = "block";
      document.getElementById("result").innerHTML = `
        <div>سعر الوحدة: $${price.toFixed(2)}</div>
        <div>الكمية: ${qty}</div>
        <div>المجموع الفرعي: $${subtotal.toFixed(2)}</div>
        <div>الخصم: ${(discount * 100).toFixed(0)}% (-$${saved.toFixed(2)})</div>
        <div class="total">الإجمالي: $${total.toFixed(2)}</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; }
    .demo { max-width: 500px; margin: 16px auto; }
    .row { margin: 12px 0; display: flex; align-items: center; gap: 10px; }
    label { width: 80px; font-weight: bold; }
    input { padding: 8px 12px; font-size: 16px; border: 2px solid #ccc; border-radius: 6px; flex: 1; }
    input.valid { border-color: #5cb85c; background: #f0fff0; }
    input.invalid { border-color: #d9534f; background: #fff0f0; }
    .msg { font-size: 13px; margin-right: 90px; }
    .msg.valid { color: #5cb85c; }
    .msg.invalid { color: #d9534f; }
    .methods { margin-top: 20px; padding: 16px; background: #f5f5f5; border-radius: 8px; }
    .methods div { margin: 4px 0; font-family: monospace; font-size: 14px; direction: ltr; }
  </style>
</head>
<body>
  <h2 style="text-align:center;">عرض التحقق من المدخلات</h2>
  <div class="demo">
    <div class="row">
      <label>العمر:</label>
      <input type="text" id="age" placeholder="أدخل العمر" />
    </div>
    <div class="msg" id="ageMsg"></div>

    <div class="row">
      <label>الهاتف:</label>
      <input type="text" id="phone" placeholder="أدخل رقم الهاتف" />
    </div>
    <div class="msg" id="phoneMsg"></div>

    <div class="row">
      <label>المبلغ:</label>
      <input type="text" id="amount" placeholder="أدخل المبلغ" />
    </div>
    <div class="msg" id="amountMsg"></div>

    <div class="methods" id="methods"></div>
  </div>
  <script>
    function validateAge(value) {
      const num = parseInt(value, 10);
      if (value.trim() === "") return { valid: false, msg: "لا يمكن أن يكون فارغاً" };
      if (Number.isNaN(num)) return { valid: false, msg: "عدد صحيح غير صالح" };
      if (!Number.isInteger(Number(value))) return { valid: false, msg: "العمر يجب أن يكون عدداً صحيحاً" };
      if (num < 0 || num > 150) return { valid: false, msg: "نطاق العمر: 0–150" };
      return { valid: true, msg: `صالح — عمرك ${num} سنة` };
    }

    function validatePhone(value) {
      const trimmed = value.trim();
      if (trimmed === "") return { valid: false, msg: "لا يمكن أن يكون فارغاً" };
      if (!/^\d+$/.test(trimmed)) return { valid: false, msg: "أرقام فقط" };
      if (trimmed.length !== 11) return { valid: false, msg: "رقم الهاتف يجب أن يكون 11 رقم" };
      return { valid: true, msg: "التنسيق صحيح" };
    }

    function validateAmount(value) {
      const num = parseFloat(value);
      if (value.trim() === "") return { valid: false, msg: "لا يمكن أن يكون فارغاً" };
      if (Number.isNaN(num)) return { valid: false, msg: "رقم غير صالح" };
      if (num <= 0) return { valid: false, msg: "المبلغ يجب أن يكون أكبر من 0" };
      return { valid: true, msg: `صالح — المبلغ $${num.toFixed(2)}` };
    }

    function setupValidation(inputId, msgId, validator) {
      const input = document.getElementById(inputId);
      const msg = document.getElementById(msgId);
      input.addEventListener("input", function() {
        const result = validator(input.value);
        input.className = result.valid ? "valid" : "invalid";
        msg.className = "msg " + (result.valid ? "valid" : "invalid");
        msg.textContent = result.msg;
        updateMethods();
      });
    }

    setupValidation("age", "ageMsg", validateAge);
    setupValidation("phone", "phoneMsg", validatePhone);
    setupValidation("amount", "amountMsg", validateAmount);

    function updateMethods() {
      const age = document.getElementById("age").value;
      const phone = document.getElementById("phone").value;
      const amount = document.getElementById("amount").value;
      document.getElementById("methods").innerHTML = `
        <strong>نتائج طرق الأدوات المساعدة:</strong>
        <div>parseInt("${age}", 10) → ${parseInt(age, 10)}</div>
        <div>Number.isNaN(parseInt("${age}")) → ${Number.isNaN(parseInt(age, 10))}</div>
        <div>parseFloat("${amount}") → ${parseFloat(amount)}</div>
        <div>typeof "${phone}" → "${typeof phone}"</div>
      `;
    }
  </script>
</body>
</html>
▶ جرّب الكود

مثال: typeof والتحقق من النوع

HTML
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
  <meta charset="UTF-8">
  <title>التحقق من النوع بـ typeof</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; }
    .bug { background: #fff0f0; }
    .ok { background: #f0fff0; }
    .tip { background: #fff8e1; padding: 12px; border-radius: 6px; border-right: 4px solid #f0ad4e; margin: 16px 0; max-width: 700px; }
  </style>
</head>
<body>
  <h2>typeof تحقق لكل نوع</h2>
  <div id="output"></div>
  <script>
    const values = [
      [42, "Number"],
      ["hello", "String"],
      [true, "Boolean"],
      [undefined, "undefined"],
      [null, "null ⚠️"],
      [[1,2,3], "Array ⚠️"],
      [{a:1}, "Object"],
      [function(){}, "Function"],
      [NaN, "NaN"],
      [Infinity, "Infinity"],
    ];

    const rows = values.map(([val, label]) => {
      const typeResult = typeof val;
      const isBug = (val === null && typeResult === "object") ||
                    (Array.isArray(val) && typeResult === "object");
      const correctWay = val === null ? "value === null" :
                         Array.isArray(val) ? "Array.isArray(value)" :
                         "typeof صحيح";
      return `<tr class="${isBug ? 'bug' : 'ok'}">
        <td>${label}</td>
        <td><code>${JSON.stringify(val)}</code></td>
        <td><code>"${typeResult}"</code></td>
        <td>${isBug ? "❌ غير دقيق" : "✅ صحيح"}</td>
        <td>${isBug ? correctWay : "—"}</td>
      </tr>`;
    });

    document.getElementById("output").innerHTML = `
      <table>
        <tr><th>النوع المتوقع</th><th>القيمة</th><th>typeof نتيجة</th><th>دقيق؟</th><th>ال الصحيح</th></tr>
        ${rows.join("")}
      </table>
      <div class="tip">
        💡 تذكر حالتين حديتين: <code>typeof null</code> يُرجع <code>"object"</code> (خطأ تاريخي)،
        و <code>typeof []</code> أيضاً يُرجع <code>"object"</code> (المصفوفات ليست نوعاً منفصلاً).
        استخدم <code>=== null</code> لفحص null، و <code>Array.isArray()</code> لفحص المصفوفات.
      </div>
    `;
  </script>
</body>
</html>
▶ جرّب الكود

❓ أسئلة شائعة

س لماذا parseInt("08") قد يُرجع 0؟
ج في بعض المحركات القديمة، السلاسل التي تبدأ بـ 0 كانت تُحلل كس octal. "08" غير صالح في octal، لذا يُرجع 0. الحل: مرر دائماً المعامل الثاني 10 — أي parseInt("08", 10)8.
س لماذا toFixed لا يضمن التقريب؟
ج لأن الأرقام ذات الفاصلة المتحولة لا يمكن تمثيلها بدقة في النظام الثنائي. (0.1 + 0.2).toFixed(1) قد يُعطي "0.3" وقد لا يُعطي. للحصول على تقريب دقيق، استخدم Math.round(num * 100) / 100 قبل استدعاء toFixed.
س ما الفرق بين parseFloat و Number()؟
ج parseFloat("42px") يُرجع 42 (يحلل من البداية، يتوقف عند أول حرف غير رقمي)، بينما Number("42px") يُرجع NaN (يحلل السلسلة بأكملها — أي حرف غير رقمي يسبب فشل). استخدم parseFloat لاستخراج الأرقام من النص المختلط؛ استخدم Number() للتحقق الصارم.

📝 تمارين

  1. اكتب دالة parseChineseNumber(str) تحلل سلاسل مثل "3.5元"3.5، "100人"100، "约200"200 (باستخدام parseFloat). تعامل مع حالة النص العادي الذي يُرجع NaN.
  2. اكتب دالة formatMoney(amount) تستقبل رقماً وتُعيد سلسلة بفواصل الآلاف وعشرتين عشريتين، مثلاً 1234567.89"1,234,567.89" (تلميح: استخدم toFixed و toString مع طرق المصفوفات).
  3. اكتب دالة getType(value) ت弥补 قصور typeof: تُرجع null لـ null، تُرجع array للمصفوفات، وفي بقية الحالات تُرجع نتيجة typeof.
100%