فارسی

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

دکوراتورهای جاوا اسکریپت: آزادسازی فراداده و اصلاح کد

دکوراتورهای جاوا اسکریپت روشی قدرتمند و زیبا برای افزودن فراداده و اصلاح رفتار کلاس‌ها، متدها، ویژگی‌ها و پارامترها ارائه می‌دهند. آنها یک سینتکس اعلانی برای بهبود کد با دغدغه‌های مشترک (cross-cutting concerns) مانند لاگ‌برداری، اعتبارسنجی، احراز هویت و موارد دیگر فراهم می‌کنند. در حالی که هنوز یک ویژگی نسبتاً جدید محسوب می‌شوند، دکوراتورها در حال کسب محبوبیت هستند، به ویژه در تایپ‌اسکریپت، و وعده بهبود خوانایی، قابلیت نگهداری و قابلیت استفاده مجدد کد را می‌دهند. این مقاله قابلیت‌های دکوراتورهای جاوا اسکریپت را بررسی کرده و نمونه‌های عملی و بینش‌هایی را برای توسعه‌دهندگان در سراسر جهان ارائه می‌دهد.

دکوراتورهای جاوا اسکریپت چه هستند؟

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

آنها را به عنوان یک ساده‌سازی سینتکسی (syntactic sugar) برای توابع مرتبه بالا در نظر بگیرید که روشی تمیزتر و خواناتر برای اعمال دغدغه‌های مشترک بر روی کد شما ارائه می‌دهند. دکوراتورها شما را قادر می‌سازند تا دغدغه‌ها را به طور مؤثر جدا کنید، که منجر به برنامه‌های کاربردی ماژولارتر و قابل نگهداری‌تر می‌شود.

انواع دکوراتورها

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

سینتکس پایه

سینتکس اعمال یک دکوراتور ساده است:

@decoratorName
class MyClass {
  @methodDecorator
  myMethod( @parameterDecorator param: string ) {
    @propertyDecorator
    myProperty: number;
  }
}

در اینجا یک تفکیک ارائه شده است:

دکوراتورهای کلاس: اصلاح رفتار کلاس

دکوراتورهای کلاس توابعی هستند که سازنده (constructor) کلاس را به عنوان آرگومان دریافت می‌کنند. آنها می‌توانند برای موارد زیر استفاده شوند:

مثال: لاگ‌برداری از ایجاد کلاس

تصور کنید می‌خواهید هر زمان که یک نمونه جدید از یک کلاس ایجاد می‌شود، آن را لاگ کنید. یک دکوراتور کلاس می‌تواند این کار را انجام دهد:

function logClassCreation(constructor: Function) {
  return class extends constructor {
    constructor(...args: any[]) {
      console.log(`Creating a new instance of ${constructor.name}`);
      super(...args);
    }
  };
}

@logClassCreation
class User {
  name: string;

  constructor(name: string) {
    this.name = name;
  }
}

const user = new User("Alice"); // خروجی: Creating a new instance of User

در این مثال، logClassCreation کلاس اصلی User را با یک کلاس جدید که از آن ارث‌بری می‌کند، جایگزین می‌کند. سازنده کلاس جدید یک پیام را لاگ می‌کند و سپس با استفاده از super سازنده اصلی را فراخوانی می‌کند.

دکوراتورهای متد: بهبود عملکرد متد

دکوراتورهای متد سه آرگومان دریافت می‌کنند:

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

مثال: لاگ‌برداری از فراخوانی متدها

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

function logMethodCall(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Calling method ${propertyKey} with arguments: ${JSON.stringify(args)}`);
    const result = originalMethod.apply(this, args);
    console.log(`Method ${propertyKey} returned: ${result}`);
    return result;
  };

  return descriptor;
}

class Calculator {
  @logMethodCall
  add(x: number, y: number): number {
    return x + y;
  }
}

const calculator = new Calculator();
const sum = calculator.add(5, 3); // خروجی: Calling method add with arguments: [5,3]
                                 //         Method add returned: 8

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

دکوراتورهای اکسسور: کنترل دسترسی به ویژگی‌ها

دکوراتورهای اکسسور شبیه به دکوراتورهای متد هستند اما به طور خاص بر روی متدهای getter و setter (اکسسورها) اعمال می‌شوند. آنها همان سه آرگومان دکوراتورهای متد را دریافت می‌کنند:

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

مثال: اعتبارسنجی مقادیر Setter

بیایید یک دکوراتور اکسسور ایجاد کنیم که مقداری را که برای یک ویژگی تنظیم می‌شود، اعتبارسنجی کند:

function validateAge(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalSet = descriptor.set;

  descriptor.set = function (value: number) {
    if (value < 0) {
      throw new Error("Age cannot be negative");
    }
    originalSet.call(this, value);
  };

  return descriptor;
}

class Person {
  private _age: number;

  @validateAge
  set age(value: number) {
    this._age = value;
  }

  get age(): number {
    return this._age;
  }
}

const person = new Person();
person.age = 30; // به درستی کار می‌کند

try {
  person.age = -5; // خطا ایجاد می‌کند: Age cannot be negative
} catch (error:any) {
  console.error(error.message);
}

دکوراتور validateAge عمل setter را برای ویژگی age رهگیری می‌کند. این دکوراتور بررسی می‌کند که آیا مقدار منفی است و اگر باشد، یک خطا پرتاب می‌کند. در غیر این صورت، setter اصلی را فراخوانی می‌کند.

دکوراتورهای ویژگی: اصلاح توصیف‌گرهای ویژگی

دکوراتورهای ویژگی دو آرگومان دریافت می‌کنند:

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

مثال: فقط-خواندنی کردن یک ویژگی

بیایید یک دکوراتور ویژگی ایجاد کنیم که یک ویژگی را فقط-خواندنی کند:

function readOnly(target: any, propertyKey: string) {
  Object.defineProperty(target, propertyKey, {
    writable: false,
  });
}

class Configuration {
  @readOnly
  apiUrl: string = "https://api.example.com";
}

const config = new Configuration();

try {
  (config as any).apiUrl = "https://newapi.example.com"; // در حالت سخت‌گیرانه خطا ایجاد می‌کند
  console.log(config.apiUrl); // خروجی: https://api.example.com
} catch (error) {
  console.error("Cannot assign to read only property 'apiUrl' of object '#'", error);
}

دکوراتور readOnly از Object.defineProperty برای اصلاح توصیف‌گر ویژگی استفاده می‌کند و writable را به false تنظیم می‌کند. تلاش برای تغییر این ویژگی اکنون منجر به خطا می‌شود (در حالت سخت‌گیرانه) یا نادیده گرفته می‌شود.

دکوراتورهای پارامتر: ارائه فراداده در مورد پارامترها

دکوراتورهای پارامتر سه آرگومان دریافت می‌کنند:

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

مثال: تزریق وابستگی (Dependency Injection)

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

const dependencies: any[] = [];

function inject(token: any) {
  return function (target: any, propertyKey: string | symbol, parameterIndex: number) {
    dependencies.push({
      target,
      propertyKey,
      parameterIndex,
      token,
    });
  };
}

class UserService {
  getUser(id: number) {
    return `User with ID ${id}`;
  }
}

class UserController {
  private userService: UserService;

  constructor(@inject(UserService) userService: UserService) {
    this.userService = userService;
  }

  getUser(id: number) {
    return this.userService.getUser(id);
  }
}

// بازیابی ساده وابستگی‌ها
const userServiceInstance = new UserService();
const userController = new UserController(userServiceInstance);
console.log(userController.getUser(123)); // خروجی: User with ID 123

در این مثال، دکوراتور @inject فراداده مربوط به پارامتر userService را در آرایه dependencies ذخیره می‌کند. یک کانتینر تزریق وابستگی می‌تواند سپس از این فراداده برای حل و تزریق وابستگی مناسب استفاده کند.

کاربردهای عملی و موارد استفاده

دکوراتورها می‌توانند در سناریوهای بسیار متنوعی برای بهبود کیفیت و قابلیت نگهداری کد به کار روند:

مزایای استفاده از دکوراتورها

دکوراتورها چندین مزیت کلیدی ارائه می‌دهند:

ملاحظات و بهترین شیوه‌ها

دکوراتورها در محیط‌های مختلف

در حالی که دکوراتورها بخشی از مشخصات ESNext هستند، پشتیبانی از آنها در محیط‌های مختلف جاوا اسکریپت متفاوت است:

دیدگاه‌های جهانی در مورد دکوراتورها

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

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

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

نتیجه‌گیری

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