طرق الأدوات المساعدة في JavaScript
غطينا تحويلات الأنواع — الآن لنتعلم مجموعة من طرق الأدوات المساعدة التي ستستخدمها يومياً. فكّر بها كسكين سويسري — كل واحدة بسيطة، لكنك ستفتقد أي واحدة منها عندما تحتاجها.
📖 ملخص
parseInt و parseFloat
تحليل الأرقام من بداية السلسلة، والتوقف عند أول حرف غير رقمي:
<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 يحدد الأساس:
<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 (بدون تحويل) |
<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
يتحكم في المنازل العشرية، يُرجع سلسلة:
<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 رقم إلى سلسلة، مع دعم اختياري للأساس:
<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:
<script>
console.log(true.toString()); // "true"
console.log([1,2,3].toString()); // "1,2,3"
</script>
Number.isInteger
يتحقق ما إذا كانت القيمة عدد صحيح:
<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 ببساطة يقطع الجزء العشري (بدون تقريب):
<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 يُرجع إشارة الرقم:
<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 عادةً يعمل للتحقق من النوع، لكن هناك بعض المشكلات الكلاسيكية:
<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() للتحقق من المصفوفات.
مثال: حاسبة الأسعار
<!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>
مثال: أداة التحقق من المدخلات
<!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 والتحقق من النوع
<!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() للتحقق الصارم.📝 تمارين
- اكتب دالة
parseChineseNumber(str)تحلل سلاسل مثل"3.5元"→3.5،"100人"→100،"约200"→200(باستخدامparseFloat). تعامل مع حالة النص العادي الذي يُرجعNaN. - اكتب دالة
formatMoney(amount)تستقبل رقماً وتُعيد سلسلة بفواصل الآلاف وعشرتين عشريتين، مثلاً1234567.89→"1,234,567.89"(تلميح: استخدمtoFixedوtoStringمع طرق المصفوفات). - اكتب دالة
getType(value)ت弥补 قصورtypeof: تُرجعnullلـnull، تُرجعarrayللمصفوفات، وفي بقية الحالات تُرجع نتيجةtypeof.



