نکاتی برای بهتر نوشتن شرط‌ها در جاوااسکریپت

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

۱. استفاده از Array.includes:

// condition
function test(fruit) {
  if (fruit == 'apple' || fruit == 'strawberry') {
    console.log('red');
  }
}

در اولین نگاه مثال بالا خوب به نظر می‌رسد. اما اگر بخواهیم تعداد میوه‌ها را زیاد کنیم، آیا این راه حل درستی است؟ یعنی به ازای هر میوه باید یک عملگر “یا” به شرط اضافه کنیم. خوب در این جا بهتر است که با استفاده از متد includes() کد را بازنویسی کنیم.

function test(fruit) {
  // extract conditions to array
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

  if (redFruits.includes(fruit)) {
    console.log('red');
  }
}

در این جا شرط را به داخل آرایه redFruits بردیم. با انجام این کار، کد ساده‌تر خواهد شد. متد includes() تعیین می‌کند که آرایه حاوی یک عنصر خاص است یا خیر  که به ترتیب آن را با true یا false بر می‌گرداند.

۲. شرط‌های تو در توی کمتر، بازگشت سریع‌تر:

به مثال قبلی دو تا شرط  زیر را اضافه کنید:

  • اگر هیچ میوه‌ای نداشتیم خطا بدهد
  • اگر بیش از ۱۰ عدد میوه باشد، مقدار آن را هم چاپ کند
function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

  // condition 1: fruit must has value
  if (fruit) {
    // condition 2: must be red
    if (redFruits.includes(fruit)) {
      console.log('red');

      // condition 3: must be big quantity
      if (quantity > 10) {
        console.log('big quantity');
      }
    }
  } else {
    throw new Error('No fruit!');
  }
}

// test results
test(null); // error: No fruits
test('apple'); // print: red
test('apple', 20); // print: red, big quantity

در کد بالا ، با استفاده از عبارت if/else در ۳ لایه ، شرط تو در تو داریم.

function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

  // condition 1: throw error early
  if (!fruit) throw new Error('No fruit!');

  // condition 2: must be red
  if (redFruits.includes(fruit)) {
    console.log('red');

    // condition 3: must be big quantity
    if (quantity > 10) {
      console.log('big quantity');
    }
  }
}

با انجام این کار یک لایه غیر ضروری شرط را کاهش می‌دهیم. این استایل برای کد مناسب است، مخصوصا وقتی عبارت if طولانی داشته باشیم. در غیر این صورت شاید مجبور باشیم برای پیدا کردن else به پایین اسکرول کنیم.

در کد زیر می‌توانیم با استفاده از معکوس کردن شرط‌ها و برگشت سریع یک لایه دیگر، if را با استفاده از معکوس کردن شرط ها کاهش دهیم.

function test(fruit, quantity) {
  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];

  if (!fruit) throw new Error('No fruit!'); // condition 1: throw error early
  if (!redFruits.includes(fruit)) return; // condition 2: stop when fruit is not red

  console.log('red');

  // condition 3: must be big quantity
  if (quantity > 10) {
    console.log('big quantity');
  }
}

همان طور که مشاهده کردید، این روش بدون شرط تو در تو، به این دلایل برای ما مفید است: کد کوتاهتر، منطقی‌تر و واضح‌تر از روش قبلی می‌شود.

بنابراین همیشه هدف کمتر کردن شرط‌های تو در تو و بازگشت سریع است. اما توجه داشته باشید که استفاده  بیش از حد آن شاید باعث سردرگمی شود.

اگر به این موضوع علاقه‌مند هستید، در این لینک بیشتر درباره این موضوع صحبت شده است.

۳. استفاده از پارامترهای تابع پیش فرض و destruct:

به احتمال زیاد کد زیر برای شما آشنا به نظر برسد. وقتی با جاوااسکریپت کار می‌کنید، همیشه نیاز است که مقدار null/undefined چک شود و مقدار پیش فرضی به آن اختصاص دهید.

function test(fruit, quantity) {
  if (!fruit) return;
  const q = quantity || 1; // if quantity not provided, default to one

  console.log(`We have ${q} ${fruit}!`);
}

//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!

می‌توانیم متغییر q را با اختصاص دادن پارامترهای پیش فرض تخمین بزنیم.

function test(fruit, quantity = 1) { // if quantity not provided, default to one
  if (!fruit) return;
  console.log(`We have ${quantity} ${fruit}!`);
}

//test results
test('banana'); // We have 1 banana!
test('apple', 2); // We have 2 apple!

توجه کنید که هر پارامتر می‌تواند پارامتر تابع پیش فرض خود را داشته باشد. به طور مثال می‌توانیم مقدار پیش فرض را به صورت زیر به fruit اختصاص دهیم.

function test(fruit = 'unknown', quantity = 1)

حالا اگر fruit یک آبجکت باشد، چگونه می توانیم پارامتر پیش فرض را تعیین کنیم؟

function test(fruit) { 
  // printing fruit name if value provided
  if (fruit && fruit.name)  {
    console.log (fruit.name);
  } else {
    console.log('unknown');
  }
}

//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

به مثال بالا نگاه کنید. ما می‌خواهیم fruit.name را چاپ کنیم. اگر مقدار داشت اسم میوه را چاپ کند و اگر مقدار نداشت، unknown را چاپ کند.

با استفاده از پارامتر تابع پیش فرض و destruct، می‌توانیم شرط (fruit && fruit.name) را حذف کنیم.

// destructing - get name property only
// assign default empty object {}
function test({name} = {}) {
  console.log (name || 'unknown');
}

//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

دستور destruct در جاوااسکریپت، مقدار آرایه‌ها و آبجکت‌های اختصاص داده شده را از هم مجزا می‌کند.

از آنجایی که ما فقط به خاصیت name از fruit نیاز داریم، می‌توانیم با استفاده از پارامتر {name ،{name را به عنوان متغییر در کد به جای fruit.name به کار ببریم.

ما همچنین آبجکت خالی {} را به عنوان مقدار پیش فرض اختصاص می‌دهیم. اگر این کار را انجام ندهیم خطای Undefined را دریافت می‌کنیم.

روش دیگر استفاده از تابع get در loadash است. کتابخانه loadash آرایه، رشته و آرگومان را پشتیبانی می‌کند. مقدار name را از آبجکت fruit می‌گیرد. اگر مقدار آن تعریف نشده باشد مقدار defaultValue را بر می‌گرداند.

// Include lodash library, you will get _
function test(fruit) {
  console.log(__.get(fruit, 'name', 'unknown'); // get property name, if not available, assign default value 'unknown'
}

//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple', color: 'red' }); // apple

۴. متد Map و Object Literal نسبت به عبارت switch:

در این جا می‌خواهیم میوه‌ها را بر اساس رنگ چاپ کنیم.

function test(color) {
  // use switch case to find fruits in color
  switch (color) {
    case 'red':
      return ['apple', 'strawberry'];
    case 'yellow':
      return ['banana', 'pineapple'];
    case 'purple':
      return ['grape', 'plum'];
    default:
      return [];
  }
}

//test results
test(null); // []
test('yellow'); // ['banana', 'pineapple']

این کد به نظر می رسد که هیچ ایرادی ندارد. ما می توانیم با استفاده از object literal همان نتیجه را بدست بیاوریم ولی درعوض کد تمیزتری داشته باشیم. همان طور که می دانید object literal یکی از روش های ایجاد آبجکت در جاوا اسکریپت است که مقدار key ، value ها داخل {} قرار می گیرند.

// use object literal to find fruits in color
  const fruitColor = {
    red: ['apple', 'strawberry'],
    yellow: ['banana', 'pineapple'],
    purple: ['grape', 'plum']
  };

function test(color) {
  return fruitColor[color] || [];
}

همچنین می‌توان با استفاده از map به همان نتیجه  رسید. آبجکت map برای ES6 در دسترس است و key و value آن را می‌توانید ذخیره کنید. آبجکت map عناصر خود را مثل حلقه for و بر اساس آرایه [key, value] تکرار می‌شود.

// use Map to find fruits in color
  const fruitColor = new Map()
    .set('red', ['apple', 'strawberry'])
    .set('yellow', ['banana', 'pineapple'])
    .set('purple', ['grape', 'plum']);

function test(color) {
  return fruitColor.get(color) || [];
}

روش دیگر که نتیجه یکسانی با دو روش قبلی دارد، استفاده از متد array.filter است. این متد شامل آرایه جدیدی است که روی آن شرط مورد نظر آرایه بررسی می‌شود. یعنی همه عناصر آرایه جدید، شرط مورد نظر را دارند.

const fruits = [
    { name: 'apple', color: 'red' }, 
    { name: 'strawberry', color: 'red' }, 
    { name: 'banana', color: 'yellow' }, 
    { name: 'pineapple', color: 'yellow' }, 
    { name: 'grape', color: 'purple' }, 
    { name: 'plum', color: 'purple' }
];

function test(color) {
  // use Array filter to find fruits in color

  return fruits.filter(f => f.color == color);
}

 ۵. از Array.every  و Array.some استفاده کنید:

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

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];

function test() {
  let isAllRed = true;

  // condition: all fruits must be red
  for (let f of fruits) {
    if (!isAllRed) break;
    isAllRed = (f.color == 'red');
  }

  console.log(isAllRed); // false
}

کد ما این جا خیلی طولانی است می توانیم با استفاده از متدهای every و some  کدهای کوتاهتر و تمیزتری  داشته باشیم.

با متد every() می توانیم چک کنیم که آیا همه میوه ها رنگشان قرمز است یا نه ، در این متد باید همه عناصر آرایه ، شرط مورد نظر را داشته باشند تا true برگرداند.

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
  ];

function test() {
  // condition: short way, all fruits must be red
  const isAllRed = fruits.every(f => f.color == 'red');

  console.log(isAllRed); // false
}

برای تست حداقل میوه‌ای که رنگ قرمز دارد، از متد some() استفاده می‌کنیم. این متد چک می‌کند که اگر حداقل یک عنصر در آرایه شرط مورد نظر را داشته باشد، مقدار true  را برگرداند. چون این جا عنصر apple با رنگ قرمز داریم پس شرط مورد نظر در آرایه را داریم و مقدار true را بر می‌گرداند.

const fruits = [
    { name: 'apple', color: 'red' },
    { name: 'banana', color: 'yellow' },
    { name: 'grape', color: 'purple' }
];

function test() {
  // condition: if any fruit is red
  const isAnyRed = fruits.some(f => f.color == 'red');

  console.log(isAnyRed); // true
}

 

منبع: scotch.io

دیدگاه‌ها:

افزودن دیدگاه جدید