جاوا‌اسکریپت: بررسی وجود یک ویژگی در شئ

برای این کار سه روش معمول وجود داره که به ترتیب اونارو بررسی می‌کنیم:

روش خسته: استفاده از خاصیت undefined

انجام این کار به لطف یک رفتار خاص و البته جالب جاوا‌اسکریپت خیلی راحت انجام می‌شه: اگه یک ویژگی در شیئ موجود نباشه، مقدار undefined برای اون بر‌می‌گرده. مثلا:

const objA = {
  propA: 'A property!',
  propB: 'B property!',
};

console.log(typeof objA.propA); // prints 'string'
console.log(typeof objA.propC); // prints 'undefined'


function fA (obj) {
  // هست propFooBar اگه شیئ دارای ویژگی
  if (obj.propFooBar === undefined) // Or: typeof obj.propFooBar === 'undefined'
    console.log(obj.propFooBar);
  else
    console.log('Object does not have propFooBar');
}

fA({propSilly: "I am silly!"}); // prints 'Object does not have propFooBar'
fA({propFooBar: "I am here!"}); // prints 'I am here!'

از اونجا که در جاوا‌اسکریپت undefined وقتی به یک مقدار بولین (Boolean) تبدیل میشه معادل false میشه، شاید بعضیا تصور کنن به جای استفاده از اپراتور typeof یا مقایسه با undefined می‌تونن این کار رو انجام بدن:

const objA = {};
const objB = {propFooBar: false};

function fA (obj) {
  if (obj.propFooBar)
    console.log('Object has propFooBar');
  else
    console.log('Object does not have propFoobar');
}

/*
 * هردوی دستورات زیر خروجی دوم رو چاپ می‌کنن
 * رو ندارن propFooBar یعنی تابع فکر می‌کنه هردوی این اشیاء ویژگی 
 * در صورتی‌که شئ دوم این ویژگی رو داره
 */
fA(objA);
fA(objB);

اما این کار اشتباهه چون اگه حتی propFooBar در شئ تعریف شده باشه و مثلا مقدارش false یا null باشه بازهم به این تعبیر می‌شه که این ویژگی تعریف نشده و می‌تونه برنامه رو با خطا مواجه کنه.

حالا چرا گفتیم این ویژگی خاصه؟ چون در اکثر زبان‌ها (مثل پایتون)، وقتی می‌خوایم به یک ویژگی از شئ که اون ویژگی موجود نیست، دسترسی پیدا کنیم، با خطا مواجه می‌شیم. یعنی برای بررسی وجود یک ویژگی در شئ باید یک بلوک try…catch بنویسیم یا از توابع و امکانات خاصی که در اون زبان وجود داره استفاده کنیم (مثلا در پایتون میشه از تابع hasattr استفاده کرد). اما توجه کنید که این ویژگی در جاوا‌اسکریپت یه چاقوی دو لبس و اگه حواسمون نباشه می‌تونه آسیب بزنه!

روش دوم: استفاده از hasOwnProperty

بیاین شیئ زیر رو در نظر بگیریم:

const objC = {
  propA: undefined,
};

در objC ویژگی propA تعریف شده ولی صریحا مقدار undefined به اون داده شده! احتمالا می‌تونید حدس بزنید که اگه بخوایم با استفاده از روش قبلی بررسی کنیم که آیا این شئ ویژگی propA رو داره یا نه به اشتباه فکر خواهیم کرد که این ویژگی روی شئ تعریف نشده، در صورتیکه اینطور نیست! پس باید چیکار کرد؟ باید دست به دامن ابزار‌های خاصی شد که جاوا‌اسکریپت برامون فراهم کرده.

تمام اشیاء در جاوا‌اسکریپت متدی رو دارن به اسم hasOwnProperty که بررسی می‌کنه آیا یک شیئ یک ویژگی مشخص رو داره یا نه. کار باهاش خیلی راحته؛ اسم ویژگی رو به صورت رشته بهش می‌دی و مقدار true یا false برمی‌گردونه:

const objC = {
  propA: undefined,
};

console.log(objC.hasOwnProperty('propA')); // prints 'true'
console.log(objC.hasOwnProperty('propSilly')); // prints 'false'

// پس می‌تونیم تابعمون رو به این صورت بازنویسی کنیم
function fA (obj) {
  if (obj.hasOwnProperty('propFooBar'))
    console.log('Object has propFooBar');
  else
    console.log('Object does not have propFoobar');
}

روش سوم: استفاده از اپراتو in

تا الان تمام ویژگی‌هایی که بررسی کردیم به طور صریح روی یک شئ تعریف شده بود. یعنی فرض می‌کردیم شئ objA را داریم و بعد مقدار ویژگی مورد نظرمون رو روی اون به طور صریح قرار می‌دادیم و حالا روش‌هایی رو استفاده می‌کردیم که می‌شد بررسی کرد آیا این ویژگی روی یک شئ هست یا نه. به طور صریح یعنی چیزایی شبیه به این‌ها:

const objA = {
  propA: "I am A!"
};

// یا

const objB = {};
objB.propB = "I am B!";

خب حالا پس غیر صریح چجوریه؟ غیر صریح یعنی این شئ ما ویژگیش رو نه به طور مستقیم بلکه از کلاسی که از جنسشه به ارث برده باشه. یعنی مثلا:

class A {
  get propA () {
    return "I am A!";
  }
}

objA = new A();

// استفاده کرد hasOwnProperty نمیشه از متد propA در این صورت برای بررسی وجود ویژگی
objA.hasOwnProperty('propA'); // returns false

همونطور که دیدید در صورتیکه شیئ ویژگی خودش رو به ارث برده باشه (یعنی حداقل یکی از کلاس‌های اجدادش این ویژگی رو به اون داده باشه. مثلا فرض کنید شیئ از نوع A باشه و A هم از B ارث بری کنه و … . کافیه در این سلسله از کلاس‌ها، یکی از اون‌ها ویژگی propA رو داشته باشه) نمی‌شه از روش اول و دوم برای بررسی این که این ویژگی در شیئ هست یا نه استفاده کرد. برای این کار باید از اپراتور in استفاده کنیم:

class A {
  get propA () {
    return "I am A!";
  }
}

objA = new A();

console.log('propA' in objA); // prints 'true'

جمع‌بندی

خب الان اومدیم سه تا راه رو برای انجام یه کار گفتیم! می‌رسیم سراغ بحث همیشگی در برنامه‌نویسی؛ کدوم راه بهتره؟

جواب این سوال طبق معمول اینه که بستگی داره!

اول از همه این که اگه می‌خواین بررسی وجود ویژگی روی شئ، شامل ویژگی‌هایی هم باشه که اون شئ به ارث برده، تنها راه، استفاده از روش سومه؛ یعنی اپراتور in.

اگه مطمئن هستید که ویژگی مورد نظرتون به طور مستقیم به شئ داده شده، مطمئن‌ترین راه استفاده از متد hasOwnProperty است، چون احتمال خطا رو توی کدتون کم‌تر می‌کنه.

اگه بازهم مطمئن هستید که ویژگی شما یا تعریف نمیشه یا اگه تعریف میشه مقداری غیر از undefined داره، می‌تونید خیلی راحت از روش اول استفاده کنید.

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *