اشتباهات رایج برنامه نویسان جاوا اسکریپت

16 اسفند 1397
javascript-mistakes

با سلام خدمت شما، حالا که به قسمت های پایانی این سری آموزشی نزدیک می شویم بهتر است بحث اشتباهات رایج برنامه نویسان جاوا اسکریپت را نیز مطرح کنیم. اگر از افراد تازه کار و مبتدی هستید حتما این قسمت را مطالعه کنید.

استفاده ی اشتباه از اپراتور انتساب

بسیاری از برنامه نویسان به اشتباه اپراتور انتساب را (یعنی =) با اپراتور مقایسه (یعنی ==) اشتباه می گیرند. این اشتباه از آن جا به وجود می آید که در ریاضی همیشه یاد گرفته ایم که علامت = به معنی مساوی است. باید سعی کنید این مطلب را از ذهنتان بیرون کنید و همیشه در برنامه نویسی مراقب استفاده ی اشتباه از آن باشید. به طور مثال:

همانطور که انتظار داریم شرط if زیر مقدار false را بر میگرداند:

var x = 0;
if (x == 10)

چرا که مقدار x مساوی با 10 نیست.

اما اگر کد را به این شکل بنویسیم:

var x = 0;
if (x = 10)

مقدار برگشت داده شده true خواهد بود. به نظر شما دلیل چیست؟

زمانی که در شرط if می گوییم x = 10، گفته ایم x را مساوی عدد 10 قرار بده. این عملیات در منطق جاوا اسکریپت برابر با true است چرا که هیچ اشتباهی رخ نداده و مقدار دهی به درستی انجام می شود.

حال تصور کنید این کد را به شکل زیر بنویسیم:

var x = 0;
if (x = 0)

شرط if مقدار false را بر میگرداند! سعی کنید روی آن فکر کنید و خودتان به جواب برسید.

دلیل برگشتfalse به ما این است که باز هم مقایسه ای در کار نبوده و مقدار دهی (انتساب) انجام شده است. در مثال قبل نیز انتساب به جای مقایسه انجام شده بود اما مقدار برگشتی true بود، چرا؟ زیرا حاصل x = 10 این است که x برابر با عدد 10 باشد اما حاصل عبارت x = 0 این است که x برابر صفر باشد و عدد صفر در منطق جاوا اسکریپت همیشه false خواهد بود.

عدم درک مقایسه ها

در حالت عادی جاوا اسکریپت نوع داده ها را مقایسه نمی کند. به طور مثال شرط if زیر مقدار true را بر میگرداند:

var x = 10;
var y = "10";
if (x == y)

به عبارت دیگر رشته ی 10 با عدد 10 یکی در نظر گرفته می شود و نوع داده هیچ اهمیتی ندارد. به این نوع مقایسه، regular comparison یا loose comparison (به معنی «مقایسه ی سُست») می گویند.

اما در strict comparison (به معنی «مقایسه ی سخت‌گیرانه») نوع داده اهمیت پیدا می کند. به طور مثال شرط if زیر مقدار false را بر میگرداند:

var x = 10;
var y = "10";
if (x === y)

دلیل نیز این است که رشته ی 10 و عدد 10 دو نوع داده ی متفاوت هستند.

بسیاری از اوقات نیز برنامه نویسان فراموش می کنند که دستور switch از مقایسه ی سخت‌گیرانه استفاده می کند. به طور مثال دستور case switch زیر یک alert را به ما نشان می دهد:

<!DOCTYPE html>
<html>
<body>

<p id="demo"></p>

<script>
var x = 10;
switch(x) {
  case 10: alert("Hello");
}
</script>

</body>
</html>

میتوانید خروجی این کد را در ادیتور آنلاین جاوا اسکریپت ببینید.

اما کد زیر هیچ alert ای نشان نمی دهد:

<!DOCTYPE html>
<html>
<body>

<p id="demo"></p>

<script>
var x = 10;
switch(x) {
  case "10": alert("Hello");
}
</script>

</body>
</html>

دلیل آن نیز این است که متغیر x دارای مقدار 10 (به صورت عددی) است اما "case "10 به دنبال یک 10 از نوع رشته ای می گردد بنابراین هیچ وقت چنین شرطی برقرار نشده و پیام alert نیز نمایش داده نخواهد شد.

سردرگمی در رابطه با اپراتور +

به صورت ساده می گویم:

  • اگر با اعداد کار کرده و از اپراتور + استفاده می کنید، عملیات Addition (به معنی «جمع کردن») را انجام داده اید.
  • اگر با رشته ها کار کرده و از اپراتور + استفاده می کنید، عملیات Concatenation (به معنی «الحاق») را انجام داده اید.

با اینکه در ظاهر هر دو حالت یک اپراتور دارند (+) اما عملیات هایی که انجام می شود کاملا متفاوت است. به مثال های زیر توجه کنید:

<!DOCTYPE html>
<html>
<body>

<p id="demo"></p>

<script>
var x = 10 + "5";
document.getElementById("demo").innerHTML = x;
</script>

</body>
</html>

خروجی این کد عدد 105 خواهد بود. چرا؟ جاوا اسکریپت با خود می گوید از من خواسته شده که عدد 10 را با رشته ی 5 جمع بزنم. چنین چیزی عملا غیر ممکن است بنابراین احتمالا اشتباهی رخ داده و منظور جمع زدن رشته های 10 و 5 بوده است! به همین خاطر عدد 10 را نیز تبدیل به رشته می کند و از جلسات قبل می دانیم که در این حالت دو عدد جمع (addition) نمی شوند بلکه کنار هم قرار می گیرند (concatenation).

به این مثال توجه کنید:

var x = 10 + 5;          // نتیجه عدد 15 خواهد بود
var x = 10 + "5";        // نتیجه 105 خواهد بود

در این مثال مطلب را به طور واضحی مشاهده می کنید. بگذارید یک مثال دیگر نیز بزنم:

<!DOCTYPE html>
<html>
<body>

<p id="demo"></p>

<script>
var x = 10;
var y = "5";
var z = x + y;
document.getElementById("demo").innerHTML = z;
</script>

</body>
</html>

به نظر شما خروجی این کد چه مقدار خواهد داشت؟

درست است! خروجی این کد عدد 105 است چرا که باز هم قصد جمع زدن بین یک عدد و یک رشته را داشته ایم.

عدم درک صحیح float ها

ابتدا باید توضیحی در مورد Float ها یا همان اعداد اعشاری در جاوا اسکریپت بدهم. در جاوا اسکریپت می توان اعداد را با رقم اعشار و یا بدون رقم اعشار آورد:

var x = 3.14;    // با اعشار
var y = 3;       // بدون اعشار

بسیاری از زبان های برنامه نویسی مانند جاوا اعداد را دسته بندی می کنند؛ به طور مثال byte و int و long و float و ... اما برخلاف بسیاری از این زبان های برنامه نویسی، جاوا اسکریپت اعداد را دسته بندی نمی کند (دسته های اعداد صحیح، اعشاری و …). به همین دلیل اعداد جاوا اسکریپتی از نوع floating point هستند و از استاندارد بین المللی IEEE 754 پیروی می کنند؛ این فرمت، اعداد را در 64 بیت ذخیره می کند.

حال باید بدانیم که جاوا اسکریپت مانند بسیاری از زبان های برنامه نویسی دیگر با اعداد اعشاری مشکل دارد:

var x = 0.1;
var y = 0.2;
var z = x + y

به نظر شما خروجی این کد چه عددی است؟ حتما حدس می زنید 0.3 اما این حدس اشتباه است! خروجی این کد عدد 0.30000000000000004 خواهد بود! قبلا در این مورد صحبت کرده بودیم. برای حل این مشکل می توان اعشار را از بین برد و سپس دوباره اضافه کرد:

var z = (x * 10 + y * 10) / 10;

خروجی این کد عدد 0.3 خواهد بود.

معمولا افراد مبتدی و تازه کار هستند که به دام این اشتباهات می افتند و برنامه نویسان با تجربه کمتر دچارشان می شوند.

شکستن رشته ها

همانطور که می دانید بسیاری از زبان های برنامه نویسی به شما اجازه می دهند رشته ها را از وسط بشکنید. مثال زیر در زبان PHP نوشته شده است:

<!DOCTYPE html>
<html>
<body>

<h1>My first PHP page</h1>

<?php
echo "Hello World!";
?> 

</body>
</html>

خروجی این کد همان رشته ی Hello World خواهد بود.

شما می توانید دستور زیر را:

echo "Hello World!";

به این شکل بنویسید:

echo "Hello
World!";

بنابراین با شکستن رشته ها از وسط یا هرکجای کد، مشکلی پیش نخواهد آمد.

در جاوا اسکریپت این مسئله کمی متفاوت است. شما می توانید یک کد را به شکل زیر بشکنید:

<script>
document.getElementById("demo").innerHTML =
"Hello World!";
</script>

(کد را قبل از علامت = با اینتر شکسته ایم)

اما اگر کد را به شکل زیر بنویسید چطور؟

<script>
document.getElementById("demo").innerHTML = "Hello 
World!";
</script>

(در این کد، رشته را از وسط اش شکسته ایم)

در مثال بالا خروجی کد ما خطای زیر خواهد بود:

Uncaught SyntaxError: Invalid or unexpected token at submitTryit

دلیل اینکه به چنین خطایی برخورد کرده ایم این است که زبان جاوا اسکریپت مانند PHP نیست و شما نمی توانید رشته ها را از وسط بشکنید. اگر می خواهید چنین کاری انجام دهید باید قبل از شکستن خط از یک بک اسلش (علامت \) استفاده کنید. مثال:

<script>
document.getElementById("demo").innerHTML = "Hello \
World!";
</script>

خروجی این کد رشته ی !Hello World خواهد بود.

جایگذاری اشتباه نقطه‌ویرگول (;)

اگر در جاوا اسکریپت با ساختار یا syntax دستورات آشنا نباشید به مشکل بر میخورید. یکی از این مشکلات جا به جا کردن semicolon یا علامت نقطه ویرگول است که باعث مشکلات زیادی می شود. یکی از این مشکلات را به صورت یک مثال در کد زیر مشاهده می کنیم:

<!DOCTYPE html>
<html>
<body>

<p id="demo"></p>

<script>
var x = 5;
if (x == 19);
{
document.getElementById("demo").innerHTML = "Hello";
}
</script>

</body>
</html>

شرط ما در کد بالا این است که x بزرگتر از 19 باشد. از طرفی می بینیم که در سورس کد x برابر با 5 است بنابراین این شرط هیچ وقت نباید برقرار باشد اما به دلیل اشتباه ما کدهای داخل شرط همیشه اجرا می شوند. اگر دقت کنید متوجه می شوید ساختار کد بالا به این شکل است:

if (x == 19);
{
  // بلوکه ی کد 
}

پس از پرانتزهای آرگومان ها نباید هیچ نقطه ویرگولی قرار بگیرد و اشتباه کد نیز در همین است. به خاطر این اشتباه، این شرط همیشه برقرار بوده و اجرا خواهد شد. می دانید چرا؟ زمانی که از نقطه ویرگول استفاده می کنید به جاوا اسکریپت می گویید کار فلان دستور تمام شده است. بنابراین با گذاشتن نقطه ویرگول روبروی پرانتز های if می گوییم کار تمام شده است و این قسمت به طور کل نادیده گرفته می شود سپس دو براکت داریم که شامل کدهایی هستند. این کدها نیز اجرا می شوند! به زبان ساده، کد بالا اصلا کد if نیست و ساختار دیگری پیدا کرده است.

شکستن دستورات مختلف

رفتار پیش فرض جاوا اسکریپت به این شکل است که statement ها را به صورت خودکار ببندد (علامت نقطه ویرگول). بنابراین هر دو مثال زیر صحیح هستند:

بدون نقطه ویرگول در انتهای statement

<script>
document.getElementById("demo").innerHTML = myFunction(55);
function myFunction(a) {
  var power = 10
  return a * power
}
</script>

با نقطه ویرگول در انتهای statement

<script>
document.getElementById("demo").innerHTML = myFunction(55);
function myFunction(a) {
  var power = 10;
  return a * power;
}
</script>

اگر چه هر دو دستور صحیح هستند اما توصیه ی اکید ما و استاندارد رایج این است که از نقطه ویرگول استفاده کنید.

همچنین جاوا اسکریپت به شما اجازه می دهد statement ها را به دو قسمت تقسیم کنید و به همین خاطر کد زیر صحیح می باشد:

<script>
document.getElementById("demo").innerHTML = myFunction(55);
function myFunction(a) {
  var
  power = 10;
  return a * power;
}
</script>

در کد بالا هنگام تعریف متغیر، پس از کلمه ی var اینتر زده ایم.

اما استثنائی نیز وجود دارد؛ اگر دستور return را به شکل زیر بکشنید خروجی ما undefined خواهد شد:

function myFunction(a) {
  var
  power = 10;  
  return
  a * power;
}

به نظر شما چرا این اتفاق می افتد؟ به دلیل اینکه جاوا اسکریپت تصور می کند منظور شما این بوده است:

function myFunction(a) {
  var
  power = 10;  
  return;
  a * power;
}

آیا متوجه شدید چرا بهتر است از استاندارد پیروی کرد و همیشه نقطه ویرگول ها را قرار داد؟

index های اسمی در آرایه ها

بسیاری از زبان های برنامه نویسی به شما اجازه می دهند از index های اسمی استفاده کنید که به آن ها آرایه های متناظر یا associative arrays می گویند اما یادتان باشد که جاوا اسکریپت از index های اسمی پشتیبانی نمی کند و تنها می توانید از index های عددی استفاده کنید.

تنها عناصری که در جاوا اسکریپت از index های اسمی استفاده می کنند، اشیاء هستند بنابراین اگر در آرایه ی خود از index های اسمی استفاده کنید، جاوا اسکریپت آن آرایه را تبدیل به یک شیء می کند. پس از تبدیل شدن به شیء تمام متد ها و خصوصیات آرایه ها به صورت undefined یا نوعی خطا در خواهند آمد:

<!DOCTYPE html>
<html>
<body>

<h2>JavaScript Arrays</h2>

<p id="demo"></p>

<script>
var person = [];
person["firstName"] = "John";
person["lastName"] = "Doe";
person["age"] = 46; 
document.getElementById("demo").innerHTML =
person[0] + " " + person.length;
</script>

</body>
</html>

خروجی این کد مقدار undefined 0 خواهد بود.

قرار دادن ویرگول اضافه

برخی از برنامه نویسان مبتدی در عناصر آخر از آرایه ها و اشیاء نیز ویرگول می گذارند مانند:

شیء:

person = {firstName:"John", lastName:"Doe", age:46,}

آرایه:

points = [40, 100, 1, 5, 25, 10,];

پس از عنصر آخر نیازی به قرار دادن ویرگول نیست.

گرچه این کار در ECMAScript 5 مجاز است اما به هیچ عنوان آن را انجام ندهید چرا که نه تنها کد شما را زشت می کند و اضافه نویسی محسوب می شود بلکه در Internet Explorer 8 و اطلاعات JSON باعث crash شدن و توقف برنامه می شود.

بررسی null در اشیاء

مقادیر خالی در اشیاء می توانند null باشند و طبیعتا برای اینکه ببینیم شیء ای خالی است یا نه باید null بودنش را چک کنیم اما مشکلی وجود دارد. نمی توان این کار را به طور مستقیم انجام داد.

برای آن که بدانید یک شیء وجود دارد یا خیر از کد زیر استفاده می کنیم:

if (typeof myObj === "undefined")

برای آن که بدانیم یک شیء null است یا خیر باید به شکل زیر عمل کنیم (ترتیب چک کردن مهم است):

if (typeof myObj !== "undefined" && myObj !== null)

امیدوارم از این قسمت لذت برده و اشتباهات خود را اصلاح کرده باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری آموزش جاوا اسکریپت | Javascript توصیه می‌کند:
نویسنده شوید

دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.