روش مقایسه اشیاء در جاوا اسکریپت javascript
چطور اشیاء را در جاوا اسکریپت با هم مقایسه کنیم
مقایسه مقادیر اولیه در جاوا اسکریپت کار سادهای است. فقط کافی است از عملگرهای مقایسهای موجود استفاده کنیم، برای مثال:
‘a’===’c’ ; // => false
1 === 1 ; // => true
اما مقایسه اشیاء کار سخت تری است، چون آنها ساختار دادهای هستند. در این مقاله شما یاد میگیرید که چطور اشیاء را در جاواد اسکریپت با هم مقایسه کنید.
۴ روش مقایسه اشیا جاوا اسکریپت در این مقاله
برابری ارجاعی
مقایسه دستی
مقایسه سطحی
مقایسه عمقی
مقایسه ارجاعی
جاوا اسکریپت سه روش برای مقایسه مقادیر فراهم کرده است:
عملگر برابری سخت ===
عمرگر مقایسه سست ==
تابع Object.is
زمان مقایسه اشیاء با هر یک از روشهای بالا، مقایسه تنها در زمانی که مرجع مقادیر از نمونه شی یکسانی باشد true می شود.
برای مثال دو شی hero1 و hero2 را تعریف می کنیم، برابری ارجاعی را در این مثال ببینید:
const hero1 = {
name: 'Batman'
};
const hero2 = {
name: 'Batman'
};
hero1 === hero1; // => true
hero1 === hero2; // => false
hero1 == hero1; // => true
hero1 == hero2; // => false
Object.is(hero1, hero1); // => true
Object.is(hero1, hero2); // => false
نتیجه hero1===hero1 برابر با true میشود چون هر دو عملوند به نمونه شی یکسانی (hero1) اشاره می کنند.
از طرف دیگر، نتیجه hero1===hero2 برابر با false میشود چون عملوند hero1 و hero2 نمونه شی های متفاوتی هستند.
با اینکه اشیاء hero1 و hero2 دارای محتوای یکسانی هستند: هر دو دارای یک ویژگی به نام name با مقدار Batman هستند. اما حتی مقایسه اشیاء با ساختار یکسان hero1===hero2 برابر با false است.
برابری ارجاعی زمانی مفید است که شما میخواهید مرجع اشیاء را صرف نظر از محتوای آنها مقایسه کنید.
اما در اکثر مواقع شما نیاز به مقایسه محتوای واقعی اشیاء دارید: ویژگیها و مقادیر آنها. حالا ببینیم چطور باید این کار رو بکنیم.
مقایسه دستی
روش شفاف مقایسه اشیاء با محتوای این است که ویژگیها را بخوانیم و آنها را به طور دستی با هم مقایسه کنیم.
برای مثال، یک تابع به نام isHeroEqual مینویسیم که دو شی hero را از ورودی می گیرد:
unction isHeroEqual(object1, object2) {
return object1.name === object2.name;
}
const hero1 = {
name: 'Batman'
};
const hero2 = {
name: 'Batman'
};
const hero3 = {
name: 'Joker'
};
isHeroEqual(hero1, hero2); // => true
isHeroEqual(hero1, hero3); // => false
تابع isHeroEqual به ویژگی name از هر دو شی دسترسی دارد و مقادیر آنها را با هم مقایسه می کند.
اگر اشیاء مقایسه شده دارای ویژگیهای کمی باشند، من ترجیح میدادم تا تابعی مشابه isHeroEqual بنویسم. چنین تابعی از نظر performance ی خوب است.
مقایسه دستی نیازمند استخراج دستی ویژگیها است. برای اشیاء ساده مشکلی نیست، اما برای مقایسه اشیاء بزرگتر (یا اشیاء با ساختار ناشناخته) مقایسه دستی راحت نیست چون نیازمند تعداد خط زیادی کد است.
حالا ببینید مقایسه سطحی اشیاء چطور به ما کمک می کند.
مقایسه سطحی
مقایسه سطحی، ابتدا با استفاده از ()Object.keys فهرست ویژگیهای اشیا را میگیرد و سپس مقادیر ویژگیها را با هم مقایسه میکند.
تابع زیر یک نمونه برای مقایسه سطحی دو شی می باشد:
function shallowEqual(object1, object2) {
const keys1 = Object.keys(object1);
const keys2 = Object.keys(object2);
if (keys1.length !== keys2.length) {
return false;
}
for (let key of keys1) {
if (object1[key] !== object2[key]) {
return false;
}
}
return true;
}
در تابع keys1 و keys2 آرایه هایی هستند که به ترتیب نام ویژگیهای اشیاء object1 و object2 را نگهداری می کنند.
از حلقه for استفاده میکنیم تا هر کدام از ویژگیهای object1 و object2 را برای بررسی object1[key]!==object2[key] مقایسه کنیم.
حالا با استفاده از تابع بالا اشیاء زیر را با هم مقایسه می کنیم:
const hero1 = {
name: 'Batman',
realName: 'Bruce Wayne'
};
const hero2 = {
name: 'Batman',
realName: 'Bruce Wayne'
};
const hero3 = {
name: 'Joker'
};
shallowEqual(hero1, hero2); // => true
shallowEqual(hero1, hero3); // => false
(shallowEqual(hero1,hero2 مقدار true را بر می گرداند چون اشیاء hero1 و hero2 دارای ویژگیهای یکسان (name و realName) و مقادیر یکسانی هستند.
از طرف دیگر (shallowEqual(hero1,hero3 مقدار false بر میگرداند چون hero1 و hero3 دارای ویژگیهای متفاوتی هستند.
اگر مقادیر ویژگیها اشیاء با مقادیر اولیه مقایسه شوند مقایسه سطحی روشی است که از آن استفاده می کنیم.
اما اشیاء در جاوا اسکریپت ممکن است تو در تو باشند. در این موارد متأسفانه مقایسه سطحی به خوبی کار نمی کند.
حالا با استفاده از مقایسه سطحی اشیاء تودرتو را چک می کنیم.
const hero1 = {
name: 'Batman',
address: {
city: 'Gotham'
}
};
const hero2 = {
name: 'Batman',
address: {
city: 'Gotham'
}
};
shallowEqual(hero1, hero2); // => false
این بار هر دو شی hero1 و hero2 محتوای یکسانی دارند، ولی shallowEqual(hero1,hero2) مقدار false را بر می گرداند.
اشیای تو دروتوی hero1.address و hero2.address نمونه شی های متفاوتی هستند. بنابراین مقایسه سطحی مقادیر hero1.address و hero2.address را متفاوت لحاظ می کند.
خوشبخاته، مقایسه عمیق اشیاء شامل اشیاء دیگر را به درستی مقایسه می کند. بیاید ببینیم چطور کار می کنه.
مقایسه عمقی
مقایسه عمقی شبیه مقایسه سطحی است فقط با یک تفاوت. در طول چک کردن مقایسه سطحی اگر ویژگیهای مقایسه شده شی باشند مقایسه سطحی به صورت بازگشتی فراخوانی میشود و روی این اشیاء درونی اجرا می شود.
در زیر پیادهسازی از این مقایسه عمقی آورده شده است:
function deepEqual(object1, object2) {
const keys1 = Object.keys(object1);
const keys2 = Object.keys(object2);
if (keys1.length !== keys2.length) {
return false;
}
for (const key of keys1) {
const val1 = object1[key];
const val2 = object2[key];
const areObjects = isObject(val1) && isObject(val2);
if (
areObjects && !deepEqual(val1, val2) || !areObjects && val1 !== val2
) {
return false;
}
}
return true;
}
function isObject(object) {
return object != null && typeof object === 'object';
}
خط highlight شده areObjects && !deepEqual(val1,val2) نشان میدهد که در صورتی که ویژگیهای مقایسه شده شی باشند، یک فراخوانی بازگشتی اجرا میشود تا مشخص کند آیا اشیاء درونی برابر هستند یا خیر.
حالا مثالی از deepEquality() را می بینیم:
const hero1 = {
name: 'Batman',
address: {
city: 'Gotham'
}
};
const hero2 = {
name: 'Batman',
address: {
city: 'Gotham'
}
};
deepEqual(hero1, hero2); // => true
تابع مقایسه عمقی به درستی تعیین میکند که hero1 و hero2 ویژگیها و مقادیر یکسانی دارند.