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

۵۷ بازدید
آخرین به‌روزرسانی: ۷ شهریور ۱۴۰۲
زمان مطالعه: ۷ دقیقه
کار با اشیا در جاوا اسکریپت

جاوا اسکریپت مانند بسیاری از زبان‌های دیگر دارای ترفندهای زیادی برای اجرای وظایف آسان و دشوار است. در این مقاله با 9 روش برای کار با اشیا در جاوا اسکریپت آشنا می‌شویم. در این مقاله فهرست کوتاهی از روش‌های کار با اشیا ارائه می‌کنیم. برخی موارد جالب هستند، برخی موارد مشهور هستند و برخی دیگر نیز صرفاً به منظور داشتن اطلاعات ارائه شده‌اند. اگر به کدنویسی جاوا اسکریپت علاقه‌مند باشید، احتمالاً موافق هستید که کار با اشیا نسبت به کار با انواع دیگر جذابیت بیشتری دارد. با ما تا انتهای این راهنما همراه باشید.

1. ایجاد واقعی شیئ خالی

همه می‌دانیم که امکان ایجاد شیء در جاوا اسکریپت وجود دارد، اما آیا می‌دانید که امکان ایجاد شیء خالی نیز وجود دارد؟

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

const myEmptyObject = {}

با استفاده از دستور فوق می‌توانید اشیای خالی ساده‌ای ایجاد کنید. با این حال به صورت داخلی این اشیا واقعاً خالی نیستند زیرا ما در عمل کاری مانند زیر انجام داده‌ایم:

Object.create(Object.prototype)

دستور فوق یک شیء ایجاد می‌کند که به مشخصه‌های درون Object.prototype دسترسی داشته باشیم که در ابتدای زنجیره پروتوتایپ قرار دارد. این بدان معنی است که می‌توانید از متدهایی مانند زیر استفاده کنید:

myEmptyObject.toString()

برای ایجاد واقعی شیء خالی باید در زمان استفاده از آن null ارسال شود:

const myTrulyEmptyObject = Object.create(null)

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

2. ادغام اشیا - روش شماره یک (Object.assign)

const novice = { username: 'henry123', level: 10, hp: 100 }

function transform(target) {
  return Object.assign(target, {
    fireBolt(player) {
      player.hp -= 15
      return this
    },
  })
}

const sorceress = transform(novice)
const lucy = { username: 'iamlucy', level: 5, hp: 100 }

sorceress.fireBolt(lucy)

زمانی که از متد Object.assign استفاده می‌کنید، باید یک شیء target به عنوان شیئی برای ادغام اشیای اضافی و/یا مشخصه‌ها داشته باشید. شیء هدف آرگومان نخست Object.assign است. هر آرگومان بعد از آن در نهایت در شیء هدف ادغام می‌شود. مستندات رسمی موزیلا در مورد این متد به صورت زیر است:

متد ()Object.assign همه مشخصه‌های شمارش پذیر خود را از یک یا چند شیء منبع به یک شیء هدف کپی می‌کند. در نهایت شیء مقصد بازگشت می‌یابد.

3. ادغام اشیا – روش شماره دو (Spread Syntax)

const novice = { username: 'henry123', level: 10, hp: 100 }

function transform(target) {
  return {
    ...target,
    fireBolt(player) {
      player.hp -= 15
      return this
    },
  }
}

const sorceress = transform(novice)
const lucy = { username: 'iamlucy', level: 5, hp: 100 }

sorceress.fireBolt(lucy)

زمانی که اشیا را به این روش ادغام می‌کنید، در واقع از عملگر اسپرد روی یک لفظ شیء بهره می‌گیرید. این ساختار در نسخه رسمی ECMAScript 2018 معرفی شده است و از این رو جدید محسوب می‌شود. استفاده از این روش برای ادغام چندین شیء کاملاً ساده است و افراد زیادی استفاده از آن را توصیه می‌کنند زیرا کد همچنان خوانا و تمیز می‌ماند. در واقع تنها کاری که باید انجام دهید تایپ کردن سه‌نقطه است.

گسترش تابع‌های IIFE

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

برای نمونه از آنجا که تابع‌ها در جاوا اسکریپت همچنان شیء هم محسوب می‌شوند، می‌توان با تابع‌ها مانند اشیا رفتار کرد. معنی این حرف آن است که می‌توان آن‌ها را به اطراف ارسال کرد و کارهای جالبی با آن‌ها انجام داد. حتی می‌توان از تابع‌ها برای ادغام در لفظ‌های شیء به روش‌های عجیب مانند زیر استفاده کرد:

import React from 'react'
import {
  EditIcon,
  DeleteIcon,
  ResetIcon,
  TrashIcon,
  UndoIcon,
} from '../lib/icons'
import * as utils from '../utils

export const audioExts = ['mp3', 'mpa', 'ogg', 'wav']

const icons = {
  edit: {
    component: EditIcon,
    onClick: () => window.alert('You clicked the edit component'),
    name: 'edit',
  },
  delete: {
    component: DeleteIcon,
    name: 'delete',
  },
   // Audio icons
  // IIFE returning an object
  ...(function() {
    return audioExts.reduce((acc, ext) => {
      acc[ext] = {
        component: MdAudiotrack,
        title: 'Audio Track',
      }
      return acc
  })(),
}

از آنجا که IIFE-ها خود فراخوان هستند، بی‌درنگ شیئی را بازگشت می‌دهیم که باید در شیء icons قرار گیرد. نتیجه همان شیء خواهد بود، اما ادغام خواهد داشت:

export const audioExts = ['mp3', 'mpa', 'ogg', 'wav']

const icons = {
  edit: {
    component: EditIcon,
    onClick: () => window.alert('You clicked the edit component'),
    name: 'edit',
  },
  delete: {
    component: DeleteIcon,
    name: 'delete',
  },
  // Merged with audio icons
  mp3: {
    component: MdAudiotrack,
    title: 'Audio Track',
  },
  mpa: {
    component: MdAudiotrack,
    title: 'Audio Track',
  },
  ogg: {
    component: MdAudiotrack,
    title: 'Audio Track',
  },
  wav: {
    component: MdAudiotrack,
    title: 'Audio Track',
  },
}

4. بررسی مشخصه‌های موجود در 2020

یک قابلیتی که قطعاً سروصدای زیادی در جامعه توسعه‌دهندگان جاوا اسکریپت ایجاد خواهد کرد، «زنجیره‌سازی اختیاری» (optional chaining) است. عملگر جدید به شکل ?. است و بدون نیاز به اعتبارسنجی صریح تک تک حلقه‌های زنجیره، امکان خواندن مقدار مشخصه‌ای را فراهم می‌سازد که در اعماق یک زنجیره از اشیای به هم متصل قرار دارد. این بدان معنی است که اگر یک ساختمان شیء عمیقاً تودرتو مانند زیر داشته باشید:

const food = {
  fruits: {
    apple: {
      dates: {
        expired: '2019-08-14',
      },
    },
  },
}

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

function getAppleExpirationDate(obj) {
  if (food.fruits && food.fruits.apple && food.fruits.apple.dates) {
    return food.fruits.apple.dates.expired
  }
}

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

function getAppleExpirationDate(obj) {
  return food?.fruits?.apple?.dates?.expired
}

استفاده از این روش در هر جای کد موجب ایجاد کد بسیار تمیزتری می‌شود. مثلاً تابعی مانند زیر:

function findFatDogs(dog, result = []) {
  if (dog && dog.children) {
    return dog.children.reduce((acc, child) => {
      if (child && child.weight > 100) {
        return acc.concat(child)
      } else {
        return acc.concat(findFatDogs(child))
      }
    }, result)
  }
  return result
}

می‌تواند به سادگی به تابع زیر تبدیل شود و در عین حال خوانایی آن نیز حفظ شود:

function findFatDogs(dog, result = []) {
  if (dog?.children) {
    return dog.children.reduce((acc, child) => {
      return child?.weight > 100
        ? acc.concat(child)
        : acc.concat(findFatFrogs(child))
    }, result)
  }
  return result
}

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

5. فراخوانی اشیا با Overriding

زمانی که اشیا به صورت کلیدهای لفظ‌های شیئی انتساب می‌یابند، به صورت رشته درمی‌آیند. این حالت کاربردهای بسیار زیبایی دارد.

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

function Command(name, execute) {
  this.name = name
  this.execute = execute
}

Command.prototype.toString = function() {
  return this.name
}

const createCommandHub = function() {
  const commands = {}
  return {
    add(command) {
      commands[command.name] = command
    },
    execute(command, ...args) {
      return commands[command].execute(...args)
    },
  }
}

const cmds = createCommandHub()
const talkCommand = new Command('talk', function(wordsToSay) {
  console.log(wordsToSay)
})
const destroyEverythingCommand = new Command('destroyEverything', function() {
  throw new Error('Destroying everything')
})

cmds.add(talkCommand)
cmds.add(destroyEverythingCommand)
cmds.execute(talkCommand, 'Is talking a talent?')

اگر قطعه کد فوق را اجرا کنید، می‌بیند که کد کار می‌کند و نتیجه به صورت زیر است:

اگر دقیقاً به روش اضافه شدن این دستور نگاه کنید، می‌بینید که باید خطایی مانند زیر صادر کند:

دلیل این که این خطا صادر نمی‌شود، این است که وقتی سازنده Command تعریف شد، ما می‌توانیم متد پروتوتایپ toString را مانند زیر override کنیم:

زمانی که مقادیر انواع غیر ابتدایی به مشخصه‌های یک شیء انتساب یابند، جاوا اسکریپت تلاش می‌کند که آن‌ها را پیش از الصاق کلید، رشته‌ای (Stringify) کند و این کار را با استفاده از متد ‎.toString خود روی پروتوتایپ انجام می‌دهد. reduxjs/toolkit@ از این ترفند بهره می‌گیرد تا امکان ارسال اکشن مستقیماً به صورت کلید ایجاد شود. برای نمونه آن‌ها می‌توانند مستقیماً به صورت کلید استفاده شوند و از این رو تابع reducer انتساب یافته به مقدار ‎.type اکشن نگاشت می‌شود.

6. تخریب ساختار

از جمله امکانات جدیدی که به جاوا اسکریپت اضافه شده است، بحث «تخریب ساختار» (Destructuring) اشیا است:

const obj = {
  foods: {
    apples: ['orange', 'pineapple'],
  },
}
const { foods } = obj
console.log(foods) // apples: ["orange", "pineapple"]

7. تغییر دادن نام مشخصه‌های تخریب‌ شده

برای تغییر نام مشخصه‌های تخریب‌شده می‌توانید به صورت زیر عمل کنید:

const obj = {
  foods: {
    apples: ['orange', 'pineapple'],
  },
}
const { foods: myFoods } = obj
console.log(myFoods) // apples: ["orange", "pineapple"]

8. چرخه تکرار روی کلیدهای یک شیئ

یک روش آسان برای تکرار روی کلیدهای یک شیء استفاده از ساختار for in است:

const obj = {
  foods: {
    apples: ['orange', 'pineapple'],
  },
  water: {
    f: '',
  },
  tolupa: function() {
    return this.name
  },
}

const { foods } = obj

for (let k in obj) {
  console.log(k)
}
/*
  result: 
    "foods"
    "water"
    "tolupa"
*/

9. تکرار روی کلیدهای یک شیئ – روش دوم

یک رویکرد متفاوت برای تعریف چرخه تکرار روی کلیدهای یک شیء استفاده از متد Object.keys است:

const obj = {
  foods: {
    apples: ['orange', 'pineapple'],
  },
  water: {
    f: '',
  },
  tolupa: function() {
    return this.name
  },
}
const { foods } = obj
const keys = Object.keys(obj)
console.log(keys)

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

const people = {
  bob: {
    age: 15,
    gender: 'male',
  },
  jessica: {
    age: 24,
    gender: 'female',
  },
  lucy: {
    age: 11,
    gender: 'female',
  },
  sally: {
    age: 14,
    gender: 'female',
  },
}

const { males, females } = Object.keys(people).reduce(
  (acc, name) => {
    const person = people[name]
    if (person.gender === 'male') {
      acc.males.push(name)
    } else {
      acc.females.push(name)
    }
    return acc
  },
  { males: [], females: [] },
)

console.log(males) // ["bob"]
console.log(females) // ["jessica", "lucy", "sally"]

سخن پایانی

به این ترتیب به پایان مقاله می‌رسیم. در این راهنما با 9 روش کار با اشیای جاوا اسکریپت آشنا شدیم که اغلب آن‌ها در نسخه‌های جدید این زبان اضافه شده‌اند. اگر شما نیز موردی سراغ دارید که فکر می‌کنید می‌توان به این فهرست اضافه کرد، پیشنهاد می‌کنیم در بخش نظرات این نوشته با ما و دیگر خوانندگان مجله فرادرس در میان بگذارید.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۰ نفر
آیا این مطلب برای شما مفید بود؟
شما قبلا رای داده‌اید!
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
better-programming

نظر شما چیست؟

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